diff --git a/example/minio_example.dart b/example/minio_example.dart index 4b19f389c58d233fed11a440735e982b9ad24105..fc7faf2ebb304f377d34ff6c4de6a0201d8e90f1 100644 --- a/example/minio_example.dart +++ b/example/minio_example.dart @@ -11,8 +11,17 @@ void main() async { secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG', ); + final bucket = '00test'; + + if (!await minio.bucketExists(bucket)) { + await minio.makeBucket(bucket); + print('bucket $bucket created'); + } else { + print('bucket $bucket already exists'); + } + // print(await minio.bucketExists('02test')); - // await minio.makeBucket('05test'); + // await minio.makeBucket('00test'); // await minio.removeBucket('05test'); // print(await minio.getBucketRegion('00test')); // print(await minio.getBucketRegion('00test')); @@ -28,4 +37,10 @@ void main() async { // final object = await minio.getObject('00test', 'sys8_captcha.png'); // await File('sys8_captcha.png').openWrite().addStream(object); + + final file = File('example/teaweb.png'); + final size = await file.length(); + final etag = + await minio.putObject(bucket, 'teaweb.png', file.openRead(), size); + print(etag); } diff --git a/example/teaweb.png b/example/teaweb.png new file mode 100644 index 0000000000000000000000000000000000000000..84746b3b671c92a99bfe0bb17c9abd2a66b24ac6 Binary files /dev/null and b/example/teaweb.png differ diff --git a/lib/src/minio.dart b/lib/src/minio.dart index 752f456f58251bf027920eba47faf91393f7fc74..e57a8ec2a7f40a5620242177f6fc17261e1a3f40 100644 --- a/lib/src/minio.dart +++ b/lib/src/minio.dart @@ -156,8 +156,8 @@ class MinioClient { if (object != null) path = '/${bucket}/${object}'; } - final resourcePart = resource != null ? '$resource&' : ''; - final queryPart = encodeQueries(queries); + final resourcePart = resource == null ? '' : '$resource&'; + final queryPart = queries == null ? '' : encodeQueries(queries); final query = resourcePart + queryPart; return Uri( @@ -179,7 +179,12 @@ class MinioClient { for (var header in request.headers.entries) { buffer.writeln('${header.key}: ${header.value}'); } - buffer.writeln(request.body); + + if (request.body is List<int>) { + buffer.writeln('List<int> of size ${request.body.length}'); + } else { + buffer.writeln(request.body); + } print(buffer.toString()); } @@ -237,11 +242,14 @@ class Minio { Future<bool> bucketExists(String bucket) async { MinioInvalidBucketNameError.check(bucket); - final resp = await _client.request(method: 'HEAD', bucket: bucket); - if (resp.statusCode != 200 && resp.statusCode != 404) { - throw MinioS3Error('bucketExists failed.'); + try { + await _client.request(method: 'HEAD', bucket: bucket); + } on MinioS3Error catch (e) { + final code = e.error.code; + if (code == 'NoSuchBucket' || code == 'NotFound') return false; + rethrow; } - return resp.statusCode == 200; + return true; } int calculatePartSize(int size) { @@ -349,7 +357,7 @@ class Minio { final node = xml.parse(resp.body); final location = node.findAllElements('LocationConstraint').first.text; - _regionMap[bucket] = location; + _regionMap[bucket] = location ?? 'us-east-1'; return location; } @@ -645,7 +653,7 @@ class Minio { Stream<List<int>> data, int size, { Map<String, String> metadata, - }) { + }) async { MinioInvalidBucketNameError.check(bucket); MinioInvalidObjectNameError.check(object); @@ -653,15 +661,21 @@ class Minio { assert(size >= 0 || size == null); metadata = prependXAMZMeta(metadata ?? {}); - // Stream. size ??= maxObjectSize; size = calculatePartSize(size); final chunker = BlockStream(size); - final uploader = - MinioUploader(this, _client, bucket, object, size, metadata); - return data.transform(chunker).pipe(uploader); + final uploader = MinioUploader( + this, + _client, + bucket, + object, + size, + metadata, + ); + final etag = await data.transform(chunker).pipe(uploader); + return etag.toString(); } Future<void> removeBucket(String bucket) async { diff --git a/lib/src/minio_uploader.dart b/lib/src/minio_uploader.dart index 38d0e856f2957119770db295529cfe14528397a0..f8d115a9699b1259b15e24d613840fa9939b2953 100644 --- a/lib/src/minio_uploader.dart +++ b/lib/src/minio_uploader.dart @@ -42,7 +42,8 @@ class MinioUploader implements StreamConsumer<List<int>> { } if (this.partNumber == 1 && chunk.length < partSize) { - return uploadInOneGo(chunk, headers); + this.etag = await upload(chunk, headers, null); + return; } if (uploadId == null) { @@ -68,20 +69,7 @@ class MinioUploader implements StreamConsumer<List<int>> { 'uploadId': uploadId, }; - final resp = await client.request( - method: 'PUT', - queries: queries, - headers: headers, - bucket: bucket, - object: object, - ); - - validate(resp); - - var etag = resp.headers['etag']; - if (etag != null) { - etag = etag.replaceAll(RegExp('^"'), '').replaceAll(RegExp(r'"$'), ''); - } + final etag = await upload(chunk, headers, queries); final part = CompletedPart(etag, partNumber); parts.add(part); } @@ -89,10 +77,7 @@ class MinioUploader implements StreamConsumer<List<int>> { @override Future<String> close() async { - if (uploadId == null) { - return etag; - } - + if (uploadId == null) return etag; return minio.completeMultipartUpload(bucket, object, uploadId, parts); } @@ -107,11 +92,15 @@ class MinioUploader implements StreamConsumer<List<int>> { return headers; } - Future<void> uploadInOneGo( - List<int> chunk, Map<String, String> headers) async { + Future<String> upload( + List<int> chunk, + Map<String, String> headers, + Map<String, String> queries, + ) async { final resp = await client.request( method: 'PUT', headers: headers, + queries: queries, bucket: bucket, object: object, payload: chunk, @@ -119,10 +108,12 @@ class MinioUploader implements StreamConsumer<List<int>> { validate(resp); - etag = resp.headers['etag']; + var etag = resp.headers['etag']; if (etag != null) { etag = etag.replaceAll(RegExp('^"'), '').replaceAll(RegExp(r'"$'), ''); } + + return etag; } Future<void> initMultipartUpload() async { diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 36ecc57afa90737c1bba8a10d423c154b94b3826..774a07d2ad9f3aa07ef5657dd301612874ba73e8 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -7,8 +7,16 @@ import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; import 'package:xml/xml.dart'; -String sha256Hex(String data) { - return hex.encode(sha256.convert(utf8.encode(data)).bytes); +String sha256Hex(Object data) { + if (data is String) { + data = utf8.encode(data); + } else if (data is List<int>) { + data = data; + } else { + throw ArgumentError('unsupported data type: ${data.runtimeType}'); + } + + return hex.encode(sha256.convert(data).bytes); } XmlElement getNodeProp(XmlElement xml, String name) {