Flutter阿里云OSS存储插件flutter_oss_aliyun的使用

发布于 1周前 作者 itying888 来自 Flutter

Flutter阿里云OSS存储插件flutter_oss_aliyun的使用

简介

flutter_oss_aliyun 是一个用于访问阿里云OSS(对象存储服务)的Flutter库,支持STS临时访问凭证,并且涵盖了阿里云OSS SDK的大部分功能。它可以帮助开发者轻松地在Flutter应用中集成OSS文件上传、下载、删除等操作。

初始化Client

添加依赖

pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter_oss_aliyun: ^6.4.2

初始化oss client

1. 提供sts server地址

通过后端API获取STS临时凭证来初始化Client:

Client.init(
    stsUrl: "server url get sts token",
    ossEndpoint: "oss-cn-beijing.aliyuncs.com",
    bucketName: "bucket name",
);

后端API返回的数据格式如下:

{
  "AccessKeyId": "AccessKeyId",
  "AccessKeySecret": "AccessKeySecret",
  "SecurityToken": "SecurityToken",
  "Expiration": "2022-03-22T11:33:06Z"
}

2. 自定义authGetter得到Auth

通过自定义认证函数来初始化Client:

Client.init(
    ossEndpoint: "oss-cn-beijing.aliyuncs.com",
    bucketName: "bucketName",
    authGetter: _authGetter
);

Auth _authGetter() {
  return Auth(
      accessKey: "accessKey",
      accessSecret: 'accessSecret',
      expire: '2023-02-23T14:02:46Z',
      secureToken: 'token',
  );
}

集成get_it

可以与get_it一起使用以实现依赖注入:

@module
abstract class OssProvider {
  @singleton
  Client client() {
    return Client.init(
      stsUrl: Env.stsUrl,
      ossEndpoint: Env.endpointUrl,
      bucketName: Env.bucketName,
    );
  }
}

使用自定义Dio

可以在初始化时传入自定义的Dio实例:

Client.init(
    stsUrl: "server url get sts token",
    ossEndpoint: "oss-cn-beijing.aliyuncs.com",
    bucketName: "bucket name",
    dio: Dio(BaseOptions(connectTimeout: 9000)),
);

使用示例

文件上传

final bytes = "file bytes".codeUnits;

await Client().putObject(
  bytes,
  "test.txt",
  option: PutRequestOption(
    onSendProgress: (count, total) {
      print("send: count = $count, and total = $total");
    },
    onReceiveProgress: (count, total) {
      print("receive: count = $count, and total = $total");
    },
    override: false,
    aclModel: AclMode.publicRead,
    storageType: StorageType.ia,
    headers: {"cache-control": "no-cache"},
    callback: Callback(
      callbackUrl: "callback url",
      callbackBody: "{\"mimeType\":\${mimeType}, \"filepath\":\${object},\"size\":\${size},\"bucket\":\${bucket},\"phone\":\${x:phone}}",
      callbackVar: {"x:phone": "android"},
      calbackBodyType: CalbackBodyType.json,
    ),       
  ),
);

追加文件上传

final Response<dynamic> resp = await Client().appendObject(
  Uint8List.fromList(utf8.encode("Hello World")),
  "test_append.txt",
);

final Response<dynamic> resp2 = await Client().appendObject(
  position: int.parse(resp.headers["x-oss-next-append-position"]?[0]),
  Uint8List.fromList(utf8.encode(", Fluter.")),
  "test_append.txt",
);

跨bucket复制文件

final Response<dynamic> resp = await Client().copyObject(
  const CopyRequestOption(
    sourceFileKey: 'test.csv',
    targetFileKey: "test_copy.csv",
    targetBucketName: "bucket_2"
  ),
);

取消文件上传

final CancelToken cancelToken = CancelToken();
final bytes = ("long long bytes" * 1000).codeUnits;

Client().putObject(
  Uint8List.fromList(utf8.encode(string)),
  "cancel_token_test.txt",
  cancelToken: cancelToken,
  option: PutRequestOption(
    onSendProgress: (count, total) {
      if (kDebugMode) {
        print("send: count = $count, and total = $total");
      }
      if (count > 56) {
        cancelToken.cancel("cancel the uploading.");
      }
    },
  ),
).then((response) {
  // success
  print("upload success = ${response.statusCode}");
}).catchError((err) {
  if (CancelToken.isCancel(err)) {
    print("error message = ${err.message}");
  } else {
    // handle other errors
  }
});

批量文件上传

await Client().putObjects([
  AssetEntity(
    filename: "filename1.txt",
    bytes: "files1".codeUnits,
    option: PutRequestOption(
      onSendProgress: (count, total) {
        print("send: count = $count, and total = $total");
      },
      onReceiveProgress: (count, total) {
        print("receive: count = $count, and total = $total");
      },
      aclModel: AclMode.private,
    ),
  ),
  AssetEntity(filename: "filename2.txt", bytes: "files2".codeUnits),
]);

本地文件上传

final Response<dynamic> resp = await Client().putObjectFile(
  "/Users/aaa.pdf",
  fileKey: "aaa.png",
  option: PutRequestOption(
    onSendProgress: (count, total) {
      print("send: count = $count, and total = $total");
    },
    onReceiveProgress: (count, total) {
      print("receive: count = $count, and total = $total");
    },
    aclModel: AclMode.private,
    callback: Callback(
      callbackUrl: callbackUrl,
      callbackBody:
          "{\"mimeType\":\${mimeType}, \"filepath\":\${object},\"size\":\${size},\"bucket\":\${bucket},\"phone\":\${x:phone}}",
      callbackVar: {"x:phone": "android"},
      calbackBodyType: CalbackBodyType.json,
    ),    
  ),
);

批量本地文件上传

final List<Response<dynamic>> resp = await Client().putObjectFiles(
  [
    AssetFileEntity(
      filepath: "//Users/private.txt",
      option: PutRequestOption(
        onSendProgress: (count, total) {
          print("send: count = $count, and total = $total");
        },
        onReceiveProgress: (count, total) {
          print("receive: count = $count, and total = $total");
        },
        override: false,
        aclModel: AclMode.private,
      ),
    ),
    AssetFileEntity(
      filepath: "//Users/splash.png",
      filename: "aaa.png",
      option: PutRequestOption(
        onSendProgress: (count, total) {
          print("send: count = $count, and total = $total");
        },
        onReceiveProgress: (count, total) {
          print("receive: count = $count, and total = $total");
        },
        override: true,
      ),
    ),
  ],
);

文件下载

await Client().getObject(
  "test.txt",
  onReceiveProgress: (count, total) {
    debugPrint("received = $count, total = $total");
  },
);

查询文件是否存在

final bool isExisted = await Client().doesObjectExist(
    "aaa.jpg",
);

文件下载并保存

await Client().downloadObject(
  "test.txt",
  "./example/test.txt",
  onReceiveProgress: (count, total) {
    debugPrint("received = $count, total = $total");
  },
);

文件删除

await Client().deleteObject("test.txt");

批量文件删除

await Client().deleteObjects(["filename1.txt", "filename2.txt"]);

获取已签名的文件url

final String url = await Client().getSignedUrl(
  "test.jpg",
  params: {"x-oss-process": "image/resize,w_10/quality,q_90"},
);

获取多个已签名的文件url

final Map<String, String> result = await Client().getSignedUrls(["test.txt", "filename1.txt"]);

列举所有的存储空间

final Response<dynamic> resp = await Client().listBuckets({"max-keys": 2});

列举存储空间中所有文件

final Response<dynamic> resp = await Client().listFiles({});

获取bucket信息

final Response<dynamic> resp = await Client().getBucketInfo();

获取bucket的储容量以及文件数量

final Response<dynamic> resp = await Client().getBucketStat();

获取文件元信息

final Response<dynamic> resp = await Client().getObjectMeta("huhx.csv");

查询所有regions

final Response<dynamic> resp = await Client().getAllRegions();

查询特定region

final Response<dynamic> resp = await Client().getRegion("oss-ap-northeast-1");

查询bucket acl

final Response<dynamic> resp = await Client().getBucketAcl(
  bucketName: "bucket-name",
);

更新bucket acl

final Response<dynamic> resp = await Client().putBucketAcl(
  AciMode.publicRead, 
  bucketName: "bucket-name",
);

查询bucket policy

final Response<dynamic> resp = await Client().getBucketPolicy(
  bucketName: "bucket-name",
);

更新bucket policy

final Response<dynamic> resp = await Client().putBucketPolicy(
  {}, 
  bucketName: "bucket-name",
);

删除bucket policy

final Response<dynamic> resp = await Client().deleteBucketPolicy(
  bucketName: "bucket-name",
);

完整示例Demo

// ignore: depend_on_referenced_packages
import 'package:flutter/material.dart';
import 'package:flutter_oss_aliyun/flutter_oss_aliyun.dart';

void main() {
  runApp(
    const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomeScreen(),
    ),
  );
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  void initState() {
    super.initState();
    Client.init(
      stsUrl: "server sts url",
      ossEndpoint: "oss-cn-beijing.aliyuncs.com",
      bucketName: "bucket name",
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Flutter aliyun oss example"),
      ),
      body: Container(
        alignment: Alignment.center,
        child: Column(
          children: [
            TextButton(
              onPressed: () async {
                final bytes = "Hello World".codeUnits;
                await Client().putObject(
                  bytes,
                  "filename.txt",
                  option: PutRequestOption(
                    onSendProgress: (count, total) {
                      if (kDebugMode) {
                        print("send: count = $count, and total = $total");
                      }
                    },
                    onReceiveProgress: (count, total) {
                      if (kDebugMode) {
                        print("receive: count = $count, and total = $total");
                      }
                    },
                    override: false,
                    aclModel: AclMode.private,
                    storageType: StorageType.standard,
                    callback: const Callback(
                      callbackUrl: "callbackUrl",
                      callbackBody:
                          "{\"mimeType\":\${mimeType}, \"filepath\":\${object},\"size\":\${size},\"bucket\":\${bucket},\"phone\":\${x:phone}}",
                      callbackVar: {"x:phone": "android"},
                      calbackBodyType: CalbackBodyType.json,
                    ),
                  ),
                );
              },
              child: const Text("Upload object"),
            ),
            TextButton(
              onPressed: () async {
                await Client().getObject(
                  "filename.txt",
                  onReceiveProgress: (count, total) {
                    debugPrint("received = $count, total = $total");
                  },
                );
              },
              child: const Text("Get object"),
            ),
            TextButton(
              onPressed: () async {
                await Client().downloadObject(
                  "filename.txt",
                  "./example/savePath.txt",
                  onReceiveProgress: (count, total) {
                    debugPrint("received = $count, total = $total");
                  },
                );
              },
              child: const Text("Download object"),
            ),
            TextButton(
              onPressed: () async {
                await Client().deleteObject("filename.txt");
              },
              child: const Text("Delete object"),
            ),
            TextButton(
              onPressed: () async {
                await Client().putObjects(
                  [
                    AssetEntity(
                      filename: "filename1.txt",
                      bytes: "files1".codeUnits,
                      option: PutRequestOption(
                        onSendProgress: (count, total) {
                          if (kDebugMode) {
                            print("send: count = $count, and total = $total");
                          }
                        },
                        onReceiveProgress: (count, total) {
                          if (kDebugMode) {
                            print(
                                "receive: count = $count, and total = $total");
                          }
                        },
                        override: true,
                        aclModel: AclMode.inherited,
                      ),
                    ),
                    AssetEntity(
                        filename: "filename2.txt", bytes: "files2".codeUnits),
                  ],
                );
              },
              child: const Text("Batch upload object"),
            ),
            TextButton(
              onPressed: () async {
                await Client()
                    .deleteObjects(["filename1.txt", "filename2.txt"]);
              },
              child: const Text("Batch delete object"),
            ),
          ],
        ),
      ),
    );
  }
}

如果你觉得这个插件对你有帮助,请给它一个⭐。


更多关于Flutter阿里云OSS存储插件flutter_oss_aliyun的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter阿里云OSS存储插件flutter_oss_aliyun的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用 flutter_oss_aliyun 插件在 Flutter 应用中上传文件到阿里云 OSS(对象存储服务)的示例代码。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_oss_aliyun 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_oss_aliyun: ^版本号  # 请替换为实际的最新版本号

然后运行 flutter pub get 来获取依赖。

接下来,你需要进行一些初始化配置,包括设置阿里云 OSS 的 endpointaccessKeyIdaccessKeySecret 以及 bucketName 等信息。在实际应用中,这些信息通常不应该硬编码在客户端,而是通过安全的方式(例如后端服务)获取。

以下是一个完整的示例代码,展示了如何使用 flutter_oss_aliyun 插件上传文件到阿里云 OSS:

import 'package:flutter/material.dart';
import 'package:flutter_oss_aliyun/flutter_oss_aliyun.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Aliyun OSS Upload Example'),
        ),
        body: Center(
          child: UploadButton(),
        ),
      ),
    );
  }
}

class UploadButton extends StatefulWidget {
  @override
  _UploadButtonState createState() => _UploadButtonState();
}

class _UploadButtonState extends State<UploadButton> {
  final AliyunOssFlutter aliyunOssFlutter = AliyunOssFlutter();

  @override
  void initState() {
    super.initState();
    // 初始化配置,这里为了示例直接硬编码,实际应用中应从安全的地方获取
    aliyunOssFlutter.init(
      endpoint: 'https://oss-cn-hangzhou.aliyuncs.com', // 替换为你的OSS endpoint
      accessKeyId: 'yourAccessKeyId', // 替换为你的AccessKeyId
      accessKeySecret: 'yourAccessKeySecret', // 替换为你的AccessKeySecret
      bucketName: 'yourBucketName', // 替换为你的BucketName
      region: 'oss-cn-hangzhou', // 替换为你的Region
    );
  }

  void _uploadFile() async {
    String localFilePath = 'path/to/your/local/file.jpg'; // 替换为你的本地文件路径
    String ossObjectName = 'folder/yourFileName.jpg'; // 替换为你希望存储在OSS上的文件名

    try {
      bool result = await aliyunOssFlutter.uploadFile(
        localFilePath,
        ossObjectName,
      );
      if (result) {
        print('File uploaded successfully!');
      } else {
        print('File upload failed!');
      }
    } catch (e) {
      print('Error uploading file: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _uploadFile,
      child: Text('Upload File to OSS'),
    );
  }
}

在这个示例中:

  1. MyApp 是主应用组件,包含一个 Scaffold 和一个居中的 UploadButton
  2. UploadButton 是一个有状态的组件,用于处理文件上传的逻辑。
  3. initState 方法中,我们初始化了 AliyunOssFlutter 插件,并设置了必要的配置信息。
  4. _uploadFile 方法是实际执行文件上传的地方,它接收本地文件路径和 OSS 对象名称作为参数,并调用 aliyunOssFlutter.uploadFile 方法上传文件。

请注意,在实际应用中,你应该采取安全措施来保护你的 accessKeyIdaccessKeySecret,避免直接在客户端代码中暴露这些信息。通常,你可以在后端生成一个临时的 STS(Security Token Service)令牌,并在客户端使用这个令牌进行身份验证和授权。

回到顶部