From 8b1c3cc2e6723b49593e372c08ee64e5e7a11c96 Mon Sep 17 00:00:00 2001 From: xuty <xty50337@hotmail.com> Date: Sun, 29 Mar 2020 19:43:10 +0800 Subject: [PATCH] finish listObjectsV2 --- README.md | 2 +- example/minio_example.dart | 55 +++++++++---------- lib/src/minio.dart | 108 ++++++++++++++++++++++++++++++++----- lib/src/minio_models.dart | 7 +++ lib/src/utils.dart | 2 +- 5 files changed, 127 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 9539925..836c683 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Created from templates made available by Stagehand under a BSD-style | `bucketExists` | fGetObject | presignedPutObject | removeAllBucketNotification | | `removeBucket` | `putObject` | presignedPostPolicy | getBucketPolicy | | `listObjects` | fPutObject | | setBucketPolicy | -| listObjectsV2 | `copyObject` | | listenBucketNotification | +| `listObjectsV2` | `copyObject` | | listenBucketNotification | | `listIncompleteUploads` | `statObject` | | | | | `removeObject` | | | | | `removeObjects` | | | diff --git a/example/minio_example.dart b/example/minio_example.dart index c26829b..882c397 100644 --- a/example/minio_example.dart +++ b/example/minio_example.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:minio/minio.dart'; -import 'package:minio/models.dart'; void main() async { final minio = Minio( @@ -9,12 +8,12 @@ void main() async { accessKey: 'Q3AM3UQ867SPQQA43P2F', secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG', useSSL: false, - enableTrace: true, + // enableTrace: true, ); final bucket = '00test'; final object = 'teaweb.png'; - final copy1 = '$object.copy'; + final copy1 = '$object.copy1'; final copy2 = '$object.copy2'; if (!await minio.bucketExists(bucket)) { @@ -24,52 +23,46 @@ void main() async { print('bucket $bucket already exists'); } - // print(await minio.bucketExists('02test')); - // await minio.makeBucket('00test'); - // await minio.removeBucket('05test'); - // print(await minio.getBucketRegion('00test')); - // print(await minio.getBucketRegion('00test')); - // print((await minio.listBuckets()).map((e) => e.name)); - // print(await minio.listObjectsQuery('00test', '/', null, '', null)); - - // await minio.listObjects('0inst').forEach((chunk) { - // print(chunk.objects.join('\n')); - // print(chunk.prefixes.join('\n')); - // }); - - // await minio.listObjects('0inst', recursive: true).forEach((o) => print(o.key)); - - // final object = await minio.getObject('00test', 'sys8_captcha.png'); - // await File('sys8_captcha.png').openWrite().addStream(object); + final region = await minio.getBucketRegion('00test'); + print('--- object region:'); + print(region); final file = File('example/$object'); final size = await file.length(); final etag = await minio.putObject(bucket, object, file.openRead(), size); - print('---'); - print('etag:'); + print('--- etag:'); print(etag); final copyResult1 = await minio.copyObject(bucket, copy1, '$bucket/$object'); final copyResult2 = await minio.copyObject(bucket, copy2, '$bucket/$object'); - print('---'); - print('Copy1 etag:'); + print('--- copy1 etag:'); print(copyResult1.eTag); - print('---'); - print('Copy2 etag:'); + print('--- copy2 etag:'); print(copyResult2.eTag); + await minio.listObjects(bucket).forEach((chunk) { + print('--- objects:'); + chunk.objects.forEach((o) => print(o.key)); + }); + + await minio.listObjectsV2(bucket).forEach((chunk) { + print('--- objects(v2):'); + chunk.objects.forEach((o) => print(o.key)); + }); + final stat = await minio.statObject(bucket, object); - print('Stat:'); + print('--- object stat:'); print(stat.etag); print(stat.size); print(stat.lastModified); print(stat.metaData); await minio.removeObject(bucket, object); - print('---'); - print('Removed'); + print('--- object removed'); await minio.removeObjects(bucket, [copy1, copy2]); - print('---'); - print('Copy removed'); + print('--- copy1, copy2 removed'); + + await minio.removeBucket(bucket); + print('--- bucket removed'); } diff --git a/lib/src/minio.dart b/lib/src/minio.dart index 74120b4..134dc08 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 = queries == null ? '' : encodeQueries(queries); + final resourcePart = resource == null ? '' : '$resource'; + final queryPart = queries == null ? '' : '&${encodeQueries(queries)}'; final query = resourcePart + queryPart; return Uri( @@ -399,9 +399,13 @@ class Minio { validate(resp); final node = xml.parse(resp.body); - final location = node.findAllElements('LocationConstraint').first.text; - _regionMap[bucket] = location ?? 'us-east-1'; + var location = node.findAllElements('LocationConstraint').first.text; + if (location == null || location.isEmpty) { + location = 'us-east-1'; + } + + _regionMap[bucket] = location; return location; } @@ -560,7 +564,7 @@ class Minio { MinioInvalidPrefixError.check(prefix); final delimiter = recursive ? '' : '/'; - var marker = ''; + String marker; var isTruncated = false; do { @@ -625,6 +629,85 @@ class Minio { ..nextMarker = nextMarker; } + /// Returns all [Object]s in a bucket. + /// If recursive is true, the returned stream may also contains [CommonPrefix] + Stream<ListObjectsChunk> listObjectsV2( + String bucket, { + String prefix = '', + bool recursive = false, + String startAfter, + }) async* { + MinioInvalidBucketNameError.check(bucket); + MinioInvalidPrefixError.check(prefix); + final delimiter = recursive ? '' : '/'; + + var isTruncated = false; + String continuationToken; + + do { + final resp = await listObjectsV2Query( + bucket, prefix, continuationToken, delimiter, 1000, startAfter); + isTruncated = resp.isTruncated; + continuationToken = resp.nextContinuationToken; + yield ListObjectsChunk() + ..objects = resp.contents + ..prefixes = resp.commonPrefixes.map((e) => e.prefix).toList(); + } while (isTruncated); + } + + Future<ListObjectsV2Output> listObjectsV2Query( + String bucket, + String prefix, + String continuationToken, + String delimiter, + int maxKeys, + String startAfter, + ) async { + MinioInvalidBucketNameError.check(bucket); + MinioInvalidPrefixError.check(prefix); + + final queries = <String, String>{}; + queries['prefix'] = prefix; + queries['delimiter'] = delimiter; + queries['list-type'] = '2'; + + if (continuationToken != null) { + queries['continuation-token'] = continuationToken; + } + + if (startAfter != null) { + queries['start-after'] = startAfter; + } + + if (maxKeys != null) { + maxKeys = maxKeys >= 1000 ? 1000 : maxKeys; + queries['maxKeys'] = maxKeys.toString(); + } + + final resp = await _client.request( + method: 'GET', + bucket: bucket, + queries: queries, + ); + + validate(resp); + + final node = xml.parse(resp.body); + final isTruncated = getNodeProp(node.rootElement, 'IsTruncated')?.text; + final nextContinuationToken = + getNodeProp(node.rootElement, 'NextContinuationToken')?.text; + final objs = node.findAllElements('Contents').map((c) => Object.fromXml(c)); + final prefixes = node + .findAllElements('CommonPrefixes') + .map((c) => CommonPrefix.fromXml(c)); + + return ListObjectsV2Output() + ..contents = objs.toList() + ..commonPrefixes = prefixes.toList() + ..isTruncated = isTruncated.toLowerCase() == 'true' + ..nextContinuationToken = nextContinuationToken; + } + Stream<Part> listParts( String bucket, String object, @@ -758,17 +841,14 @@ class Minio { true, ).toXml().toString(); - final headers = { - 'Content-MD5': md5Base64(payload) - }; + final headers = {'Content-MD5': md5Base64(payload)}; await _client.request( - method: 'POST', - bucket: bucket, - resource: 'delete', - headers: headers, - payload: payload - ); + method: 'POST', + bucket: bucket, + resource: 'delete', + headers: headers, + payload: payload); } } diff --git a/lib/src/minio_models.dart b/lib/src/minio_models.dart index 404c4dc..8de010b 100644 --- a/lib/src/minio_models.dart +++ b/lib/src/minio_models.dart @@ -13,6 +13,13 @@ class ListObjectsOutput { List<CommonPrefix> commonPrefixes; } +class ListObjectsV2Output { + bool isTruncated; + String nextContinuationToken; + List<Object> contents; + List<CommonPrefix> commonPrefixes; +} + class CompleteMultipartUpload { CompleteMultipartUpload( this.parts, diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 1f20c22..d62d422 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -44,7 +44,7 @@ String encodeQueries(Map<String, String> queries) { final value = queries[key]; pairs.add(encodeQuery(key, value)); } - return pairs.join('='); + return pairs.join('&'); } class BlockStream extends StreamTransformerBase<List<int>, List<int>> { -- GitLab