diff --git a/README.md b/README.md index 840beb24b89a30bf1c5d8dcdd5f88a3a27383f5d..060006d7b5a1f8aa084754b65af547462d0339e7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Created from templates made available by Stagehand under a BSD-style | `removeBucket` | `putObject` | presignedPostPolicy | getBucketPolicy | | `listObjects` | fPutObject | | setBucketPolicy | | listObjectsV2 | `copyObject` | | listenBucketNotification | -| `listIncompleteUploads` | statObject | | | +| `listIncompleteUploads` | `statObject` | | | | | removeObject | | | | | removeObjects | | | | | removeIncompleteUpload | | | diff --git a/example/minio_example.dart b/example/minio_example.dart index e2b9f26e50d4fca45662f61494637710932a3f80..4e2329a6449865490e8979396a50b9f65c82f79e 100644 --- a/example/minio_example.dart +++ b/example/minio_example.dart @@ -12,6 +12,7 @@ void main() async { ); final bucket = '00test'; + final object = 'teaweb.png'; if (!await minio.bucketExists(bucket)) { await minio.makeBucket(bucket); @@ -38,8 +39,7 @@ 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 object = 'teaweb.png'; + final file = File('example/$object'); final size = await file.length(); final etag = await minio.putObject(bucket, object, file.openRead(), size); print(etag); @@ -50,4 +50,11 @@ void main() async { '$bucket/$object', ); print(copyResult.eTag); + + final stat = await minio.statObject(bucket, object); + print('Stat:'); + print(stat.etag); + print(stat.size); + print(stat.lastModified); + print(stat.metaData); } diff --git a/lib/src/minio.dart b/lib/src/minio.dart index 4f260f53e9722ae4a94b065b2420f8f142e84141..d1c3043bb4b291283c7808b8dfe3fb83a8b7a8b3 100644 --- a/lib/src/minio.dart +++ b/lib/src/minio.dart @@ -346,7 +346,7 @@ class Minio { validate(resp); final node = xml.parse(resp.body); - final result = CopyObjectResult.fromXml(node.rootElement); + final result = CopyObjectResult.fromXml(node.rootElement); result.eTag = trimDoubleQuote(result.eTag); return result; } @@ -733,6 +733,31 @@ class Minio { validate(resp, expect: 204); _regionMap.remove(bucket); } + + Future<StatObjectResult> statObject(String bucket, String object) async { + MinioInvalidBucketNameError.check(bucket); + MinioInvalidObjectNameError.check(object); + + final resp = await _client.request( + method: 'HEAD', + bucket: bucket, + object: object, + ); + + validate(resp, expect: 200); + + var etag = resp.headers['etag']; + if (etag != null) { + etag = trimDoubleQuote(etag); + } + + return StatObjectResult( + etag: etag, + size: int.parse(resp.headers['content-length']), + metaData: extractMetadata(resp.headers), + lastModified: parseRfc7231Time(resp.headers['last-modified']), + ); + } } Future<void> validateStreamed( diff --git a/lib/src/minio_helpers.dart b/lib/src/minio_helpers.dart index eb8b108dac1207d1cbd82bf59c5d8a7cf03ba30d..c0e7277fe4d7cfe2f4bbec054d7ded09ceafb19e 100644 --- a/lib/src/minio_helpers.dart +++ b/lib/src/minio_helpers.dart @@ -154,3 +154,19 @@ bool isSupportedHeader(key) { bool isStorageclassHeader(key) { return key.toLowerCase() == 'x-amz-storage-class'; } + +Map<String, String> extractMetadata(Map<String, String> metaData) { + var newMetadata = <String, String>{}; + for (var key in metaData.keys) { + if (isSupportedHeader(key) || + isStorageclassHeader(key) || + isAmzHeader(key)) { + if (key.toLowerCase().startsWith('x-amz-meta-')) { + newMetadata[key.substring(11, key.length)] = metaData[key]; + } else { + newMetadata[key] = metaData[key]; + } + } + } + return newMetadata; +} diff --git a/lib/src/minio_models.dart b/lib/src/minio_models.dart index 4c3f08b1e217bebba6e0110068a2308d62db8ce9..404c4dc34d9f948476678a5edf179d4d96a3bb63 100644 --- a/lib/src/minio_models.dart +++ b/lib/src/minio_models.dart @@ -92,3 +92,17 @@ class CopyConditions { matchETagExcept = etag; } } + +class StatObjectResult { + StatObjectResult({ + this.size, + this.etag, + this.lastModified, + this.metaData, + }); + + final int size; + final String etag; + final DateTime lastModified; + final Map<String, String> metaData; +} diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 920c02683e526aaeacde0f107fa26ca5454f5d23..e0be39815625d1c5611a00118f55bfcd51b233e1 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:typed_data'; import 'package:buffer/buffer.dart'; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; +import 'package:intl/intl.dart'; import 'package:xml/xml.dart'; String sha256Hex(Object data) { @@ -68,4 +68,9 @@ class BlockStream extends StreamTransformerBase<List<int>, List<int>> { String trimDoubleQuote(String str) { return str.replaceAll(RegExp('^"'), '').replaceAll(RegExp(r'"$'), ''); +} + +DateTime parseRfc7231Time(String time) { + final format = DateFormat('EEE, dd MMM yyyy hh:mm:ss zzz'); + return format.parse(time); } \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index d42d6fc8bbac140a9d1590250ea1fa31cc64dccb..cccb17125ce0e026b63f86a9dd5e3dba80daf084 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: convert: ^2.1.1 xml: ^3.7.0 buffer: ^1.0.6 + intl: ^0.16.1 # path: ^1.6.0 dev_dependencies: