diff --git a/README.md b/README.md index cb1ff60faee39c3cefc00215086c4cbd4bd29c7b..bce04add25360f754742d39413e3665bb64a536e 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Created from templates made available by Stagehand under a BSD-style |----------------------- |------------------------ |---------------------- |----------------------------------------- | | `makeBucket` | `getObject` | presignedUrl | getBucketNotification | | `listBuckets` | `getPartialObject` | presignedGetObject | setBucketNotification | -| `bucketExists` | fGetObject | presignedPutObject | removeAllBucketNotification | +| `bucketExists` | `fGetObject` | presignedPutObject | removeAllBucketNotification | | `removeBucket` | `putObject` | presignedPostPolicy | getBucketPolicy | -| `listObjects` | fPutObject | | setBucketPolicy | +| `listObjects` | `fPutObject` | | setBucketPolicy | | `listObjectsV2` | `copyObject` | | listenBucketNotification | | `listIncompleteUploads` | `statObject` | | | | | `removeObject` | | | diff --git a/example/custed.png b/example/custed.png new file mode 100644 index 0000000000000000000000000000000000000000..fb7f4c21e83607b8a2acb1e94c374c4c5c4b064b Binary files /dev/null and b/example/custed.png differ diff --git a/example/minio_example.dart b/example/minio_example.dart index 882c39705574b3102295829b73706c61c4f81d11..dcf4563d4938c1e7e746b082903aa6ce58b0a2de 100644 --- a/example/minio_example.dart +++ b/example/minio_example.dart @@ -1,5 +1,4 @@ -import 'dart:io'; - +import 'package:minio/io.dart'; import 'package:minio/minio.dart'; void main() async { @@ -12,9 +11,9 @@ void main() async { ); final bucket = '00test'; - final object = 'teaweb.png'; - final copy1 = '$object.copy1'; - final copy2 = '$object.copy2'; + final object = 'custed.png'; + final copy1 = 'custed.copy1.png'; + final copy2 = 'custed.copy2.png'; if (!await minio.bucketExists(bucket)) { await minio.makeBucket(bucket); @@ -27,9 +26,7 @@ void main() async { 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); + final etag = await minio.fPutObject(bucket, object, 'example/$object'); print('--- etag:'); print(etag); @@ -40,6 +37,9 @@ void main() async { print('--- copy2 etag:'); print(copyResult2.eTag); + await minio.fGetObject(bucket, object, 'example/$copy1'); + print('--- copy1 downloaded'); + await minio.listObjects(bucket).forEach((chunk) { print('--- objects:'); chunk.objects.forEach((o) => print(o.key)); diff --git a/example/teaweb.png b/example/teaweb.png deleted file mode 100644 index 84746b3b671c92a99bfe0bb17c9abd2a66b24ac6..0000000000000000000000000000000000000000 Binary files a/example/teaweb.png and /dev/null differ diff --git a/lib/io.dart b/lib/io.dart new file mode 100644 index 0000000000000000000000000000000000000000..26f5ebf3fa63fa71c1c7111dff8c43a2657a1cb5 --- /dev/null +++ b/lib/io.dart @@ -0,0 +1,71 @@ +import 'dart:io'; + +import 'package:minio/src/minio.dart'; +import 'package:minio/src/minio_errors.dart'; +import 'package:minio/src/minio_helpers.dart'; +import 'package:path/path.dart' show dirname; + +extension MinioX on Minio { + Future<String> fPutObject( + String bucket, + String object, + String filePath, [ + Map<String, String> metaData, + ]) async { + MinioInvalidBucketNameError.check(bucket); + MinioInvalidObjectNameError.check(object); + + metaData ??= {}; + metaData = insertContentType(metaData, filePath); + metaData = prependXAMZMeta(metaData); + + final file = File(filePath); + final stat = await file.stat(); + if (stat.size > maxObjectSize) { + throw MinioError( + '${filePath} size : ${stat.size}, max allowed size : 5TB', + ); + } + + return putObject(bucket, object, file.openRead(), stat.size); + } + + Future<void> fGetObject( + String bucket, + String object, + String filePath, + ) async { + MinioInvalidBucketNameError.check(bucket); + MinioInvalidObjectNameError.check(object); + + final stat = await statObject(bucket, object); + final dir = dirname(filePath); + await Directory(dir).create(recursive: true); + + final partFileName = '${filePath}.${stat.etag}.part.minio'; + final partFile = File(partFileName); + IOSink partFileStream; + var offset = 0; + + final rename = () => partFile.rename(filePath); + + if (await partFile.exists()) { + final localStat = await partFile.stat(); + if (stat.size == localStat.size) return rename(); + offset = localStat.size; + partFileStream = partFile.openWrite(mode: FileMode.append); + } else { + partFileStream = partFile.openWrite(mode: FileMode.write); + } + + final dataStream = await getPartialObject(bucket, object, offset); + await dataStream.pipe(partFileStream); + + final localStat = await partFile.stat(); + if (localStat.size != stat.size) { + throw MinioError('Size mismatch between downloaded file and the object'); + } + + return rename(); + } +} diff --git a/lib/src/minio.dart b/lib/src/minio.dart index d3551de3fb2b1a6b65a6b1e4f3438dbafce54bec..787e13d85789786596e50720b838701035269627 100644 --- a/lib/src/minio.dart +++ b/lib/src/minio.dart @@ -382,7 +382,7 @@ class Minio { return latestUpload?.uploadId; } - + Future<String> getBucketRegion(String bucket) async { MinioInvalidBucketNameError.check(bucket); diff --git a/lib/src/minio_helpers.dart b/lib/src/minio_helpers.dart index c0e7277fe4d7cfe2f4bbec054d7ded09ceafb19e..5c0bf5457790a99f2981d3d80c14fbe764d8d3cf 100644 --- a/lib/src/minio_helpers.dart +++ b/lib/src/minio_helpers.dart @@ -1,3 +1,5 @@ +import 'package:mime/mime.dart' show lookupMimeType; + bool isValidBucketName(String bucket) { if (bucket == null) return false; @@ -170,3 +172,23 @@ Map<String, String> extractMetadata(Map<String, String> metaData) { } return newMetadata; } + +String probeContentType(String path) { + final contentType = lookupMimeType(path); + return contentType ?? 'application/octet-stream'; +} + +Map<String, String> insertContentType( + Map<String, String> metaData, + String filePath, +) { + for (var key in metaData.keys) { + if (key.toLowerCase() == 'content-type') { + return metaData; + } + } + + final newMetadata = Map<String, String>.from(metaData); + newMetadata['content-type'] = probeContentType(filePath); + return newMetadata; +} diff --git a/pubspec.yaml b/pubspec.yaml index cccb17125ce0e026b63f86a9dd5e3dba80daf084..3bd02a1d1c6da1168e40b85f0da540a0150db926 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ version: 0.1.0 author: xuty <root@xuty.tk> environment: - sdk: ">=2.5.0 <3.0.0" + sdk: ">=2.7.0 <3.0.0" dependencies: meta: ^1.1.7 @@ -15,7 +15,8 @@ dependencies: xml: ^3.7.0 buffer: ^1.0.6 intl: ^0.16.1 -# path: ^1.6.0 + mime: ^0.9.6+3 + path: ^1.6.0 dev_dependencies: pedantic: ^1.8.0