【玩转腾讯云】万物皆可Serverless之在Flutter中写一个Dart原生腾讯云对象存储插件

万物皆可Serverless系列文章

  1. 万物皆可Serverless之免费搭建自己的不限速大容量云盘(5TB)
  2. 万物皆可Serverless之使用云函数Timer触发器实现每天自动定时打卡
  3. 万物皆可Serverless之使用SCF+COS快速开发全栈应用
  4. 万物皆可Serverless之使用SCF+COS免费运营微信公众号
  5. 万物皆可Serverless之使用SCF快速部署验证码识别接口
  6. 万物皆可Serverless之Kaggle+SCF端到端验证码识别从训练到部署
  7. 万物皆可Serverless之借助微信公众号简单管理用户激活码
  8. 万物皆可Serverless之使用SCF+COS给未来写封信
  9. 万物皆可Serverless之在Flutter中快速接入腾讯云开发
  10. 万物皆可Serverless之在Flutter中写一个Dart原生腾讯云对象存储插件
  11. 万物皆可Serverless之我的Serverless之路

一、本文介绍

在上一篇文章中,我们尝试在Flutter中接入了腾讯云开发SDK

不过在有些应用场景下我们只需要用到腾讯云对象存储的能力,

比如将用户头像上传存储到自己的对象存储桶中,然后返回文件下载链接保存到本地数据库中,

这时候用云开发的话就有点高射炮打蚊子–>大材小用的感觉了。

所以这里我就带大家直接上手从头写一个Dart原生的腾讯云对象存储插件

废话少说,上图

直接在dart vm里调试

注意,

这里我是直接在windows本地的dart vm里运行的示例代码哈,

并不需要连接手机或者设备虚拟机去调试运行

因为这是Dart原生应用,放到哪里都可以运行的奥~

二、开始教程

第一步:创建Package

我们根据Flutter官方文档 https://flutter.dev/docs/development/packages-and-plugins/developing-packages

先创建一个名为 tencent_cloud_cos 的package

flutter create --template=package tencent_cloud_cos

创建成功

创建完之后,你的package目录应该是和上图一样的,下面我们就来编写插件

第二步:导入依赖

打开项目根目录下的pubspec.yaml配置文件,添加必要依赖

dependencies:  flutter:    sdk: flutter  dio: ^3.0.9  crypto: ^2.1.3

这里我们仅添加了dio和crypto两个dart原生依赖库,分别用来进行http请求和请求的加密签名工作

flutter pub get

当然,配置好依赖之后不要忘记下载安装一下依赖

第三步:编写插件

Life is short, show me the code.

打开lib/tencent_cloud_cos.dart文件,修改代码如下

// @author = WJG.// @email = idootop@163.com// @date = 2020-04-19// @dart = 2.7library tencent_cloud_cos;import 'dart:convert';import 'package:dio/dio.dart';import 'package:crypto/crypto.dart';/// 腾讯云对象存储工具类/// 使用腾讯云secret_id,secret_key和存储桶地址来初始化//////   ```dart///    String secret_id='xxxxxxx';///    String secret_key='xxxxxxx';///    String bucket_host='https://xxxxxx.cos.xxxxx.myqcloud.com';///    Cos cos = Cos(secret_id, secret_key, bucket_host);///   ```/// 上传/更新文件///   ```dart///    String imgUrl = await cos.upload('/example.jpg', File('example.jpg').readAsBytesSync());///   ```/// 下载文件///   ```dart///    bool success = await cos.download(imgUrl, 'download/example.jpg');///   ```/// 删除文件///   ```dart///    bool success = await cos.delete('/example.jpg');///   ```class Cos {  Dio dio = Dio();  String id;  String key;  String host;  Cos(this.id, this.key, this.host);  /// 上传文件成功后返回文件下载链接  ///  /// `path` : 存储桶文件存放路径  ///  /// `bytes` : 待上传文件二进制数组  ///  /// `params` : 请求参数  ///  /// `headers` : 请求头部  ///  /// `progress` : 上传进度回调函数,示例  ///   ```dart  ///   progress(int count, int total) {  ///     double progress = (count / total) * 100;  ///     if (progress % 5 == 0) print('上传进度---> ${progress.round()}%');  ///   }  ///  ```  Future<String> upload(String path, List<int> bytes,      {Map<String, String> params,      Map<String, String> headers,      Function(int, int) progress}) async {    String url = host + path;    params = params ?? Map<String, String>();    Options options = Options();    options.headers = headers ?? Map<String, String>();    options.headers['content-length'] =        bytes.length.toString(); // 设置content-length,否则无法监听文件上传进度    //对put上传请求签名    options.headers['Authorization'] =        sign('put', path, headers: options.headers, params: params);    try {      Response response = await dio.put(url,          data: Stream.fromIterable(bytes.map((e) => [e])), //bytes转为Stream          onSendProgress: progress ??              (int count, int total) {                double progress = (count / total) * 100;                if (progress % 5 == 0) print('上传进度---> ${progress.round()}%');              },          queryParameters: params,          options: options);      return response.statusCode == 200 ? url : '';    } on DioError catch (e) {      print('Error:' + e.message);      return '';    }  }  /// 删除在线文件  ///  /// `path` : 存储桶文件存放路径  ///  /// `params` : 请求参数  ///  /// `headers` : 请求头部  ///  Future<bool> delete(String path,      {Map<String, String> params, Map<String, String> headers}) async {    String url = host + path;    params = params ?? Map<String, String>();    Options options = Options();    options.headers = headers ?? Map<String, String>();    //对请求签名    options.headers['Authorization'] =        sign('DELETE', path, headers: options.headers, params: params);    try {      Response response =          await dio.delete(url, queryParameters: params, options: options);      return response.statusCode == 204 ? true : false;    } on DioError catch (e) {      print('Error:' + e.message);      return false;    }  }  /// 下载文件  ///  /// `urlPath` : 存储桶文件存放路径  ///  /// `savePath` : 文件保存路径  ///  /// `progress` : 下载进度回调函数,示例  ///   ```dart  ///   progress(int count, int total) {  ///     double progress = (count / total) * 100;  ///     if (progress % 5 == 0) print('下载进度---> ${progress.round()}%');  ///   }  ///  ```  Future<bool> download(String urlPath, String savePath,      {Function(int, int) progress}) async {    try {      await dio.download(urlPath, savePath,          options: Options(receiveTimeout: 0),          onReceiveProgress: progress ??              (int count, int total) {                double progress = (count / total) * 100;                if (progress % 5 == 0) print('下载进度---> ${progress.round()}%');              });      return true;    } on DioError catch (e) {      print('Error:' + e.message);      return false;    }  }  /// 对http请求进行签名,返回Authorization签名字符串  ///  /// `httpMethod` : 请求方法  ///  /// `httpUrl` : 请求地址  ///  /// `params` : 请求参数  ///  /// `headers` : 请求头部  ///  String sign(String httpMethod, String httpUrl,      {Map<String, String> headers,      Map<String, String> params,      int expire = 10}) {    headers = headers ?? Map();    params = params ?? Map();    headers = headers.map((key, value) => MapEntry(key.toLowerCase(), value));    params = params.map((key, value) => MapEntry(key.toLowerCase(), value));    List<String> headerKeys = headers.keys.toList();    headerKeys.sort();    String headerList = headerKeys.join(';');    String httpHeaders = headerKeys        .map((item) => '$item=${Uri.encodeFull(headers[item])}')        .join('&');    List<String> paramKeys = params.keys.toList();    paramKeys.sort();    String urlParamList = paramKeys.join(';');    String httpParameters = paramKeys        .map((item) => '$item=${Uri.encodeFull(params[item])}')        .join('&');    String httpString =        '${httpMethod.toLowerCase()}\n$httpUrl\n$httpParameters\n$httpHeaders\n';    String httpStringData = sha1.convert(utf8.encode(httpString)).toString();    int timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;    String keyTime = '$timestamp;${timestamp + expire}';    String signKey =        Hmac(sha1, utf8.encode(key)).convert(utf8.encode(keyTime)).toString();    String stringToSign = 'sha1\n$keyTime\n$httpStringData\n';    String signature = Hmac(sha1, utf8.encode(signKey))        .convert(utf8.encode(stringToSign))        .toString();    return 'q-sign-algorithm=sha1&q-ak=$id&q-sign-time=$keyTime&q-key-time=$keyTime&q-header-list=$headerList&q-url-param-list=$urlParamList&q-signature=$signature';  }}

这里我就不再详细解释了,代码里都写得很清楚

请求签名

请求签名过程可参考腾讯云官方文档,地址 https://cloud.tencent.com/document/product/436/7778

第四步:代码示例

在项目根目录创建一个bin目录,然后在里面新建一个main.dart

示例程序

填上以下测试代码

import 'dart:io';import '../lib/tencent_cloud_cos.dart';main() async {  String secret_id = 'xxxxxxxxxx'; //你的腾讯云secret_id  String secret_key = 'xxxxxxxxxxxxxxxxx'; //你的腾讯云secret_key  String bucket_host = 'https://xxxxxx-6666666.cos.ap-chongqing.myqcloud.com'; //你的对象存储桶访问域名  Cos cos = Cos(secret_id, secret_key, bucket_host);  String imgUrl = await cos.upload('/example.jpg', File('example.jpg').readAsBytesSync());  await cos.download(imgUrl, 'example2.jpg');  await cos.delete('/example.jpg');}

然后按F5调试运行一下吧,没啥意外你就可以看到文章一开始那张图了

测试成功

三、文章最后

哈?这也算Serverless?

你可能会疑问,这不是介绍腾讯云对象存储吗,和serverless有啥关系~

哈哈,我只能说cos也是serverless的一种表现形式,

只要是不需要自己购买服务器运行的服务,大体都可以称之为serverless(无服务器)

以上,逃~

注明:本文为星速云原创版权所有,禁止转载,一经发现将追究版权责任!

发表评论

登录后才能评论
问答社区 QQ客服
微信客服
微信客服
分享本页
返回顶部