Flutter 上传文件到阿里云OSS

大家好,我是学习Flutter一个月的三须鳗鱼张信哲 ,熟练使用CV大法。

Flutter 上传文件到阿里云OSS是个大坑,查了一堆博客,多多少少有些问题
有人写了库:[aly_oss 1.0.1](不建议使用 )
参考了这个博客:Flutter图片上传OSS,这个博客开篇第一句就把我搞蒙了…………

目前阿里没有针对Flutter有官方SDK可集成, 但是有人对Flutter的上传有封装库https://pub.dev/packages/aly_oss#-example-tab-, 所以我们只能通过post提交PostObject的形式来上传;

不过没关系,核心技术还在

开始开展传统艺能:CV大法

这是那位老哥的

// 通过ossServerUrl直接get获取到如下数据
{
        "StatusCode": 200,
          "AccessKeyId": "STS.NSsrKZes4cqm.....",
          "AccessKeySecret": "7eGnLZaEFsRCGYJAnrtdE9n.....",
          "Expiration": "2020-04-08T03:44:21Z",
          "SecurityToken": "CAISlQJ1q6Ft5B2y....."
  }

//表单需要的参数: AccessKeyId、AccessKeySecret、SecurityToken;
FormData formdata = new FormData.from({
        //文件名,随意
        'Filename': OssUtil.instance.getImageName(filePath),
        //"可以填写文件夹名(对应于oss服务中的文件夹)/" + fileName
        'key': uploadName,//上传后的文件名
        'policy': OssUtil.policy,
        //Bucket 拥有者的AccessKeyId。
        'OSSAccessKeyId': data['AccessKeyId'],
        //让服务端返回200,不然,默认会返回204
        'success_action_status': '200',
        'signature': OssUtil.instance.getSignature(data['AccessKeySecret']),
        //临时用户授权时必须,需要携带后台返回的security-token
        'x-oss-security-token': data['SecurityToken'],
        'file': new UploadFileInfo(
            file, OssUtil.instance.getImageNameByPath(filePath))
        //必须放在参数最后
   });
//然后通过存储地址直接把表单(formdata)上传上去;
Dio dio=Dio();
dio.options.responseType=ResponseType.plain;
Response response=awaitdio.post(<ossEndPointCdn>,data:formData);
......

有个问题:UploadFileInfo 已弃用,改用MultipartFile

准备工作一:准备文件

文件准备阶段,使用image_pickerimage_picker获取到的路径是个带包名的(临时?)路径,文件名也使用image_picker前缀来命名,使用 MultipartFile.fromFile获取文件时会有问题,需要使用MultipartFile.fromFileSync(MultipartFile文档也没有说明啊,那个接口干嘛,只能瞎几把猜望文生义了 )

准备工作二:准备OSS许可

oss许可让服务端去搞,返回一段json

{
    "StatusCode": "200",
    "AccessKeyId": "STS.NSsrKZes4cqm.....",
    "AccessKeySecret": "7eGnLZaEFsRCGYJAnrtdE9n.....",
    "Expiration": "2020-04-08T03:44:21Z",
    "SecurityToken": "CAISlQJ1q6Ft5B2y....."
}

开始上传

就用之前那位老哥的方法,直接上传

//表单需要的参数: AccessKeyId、AccessKeySecret、SecurityToken;
FormData formdata = new FormData.from({
        //文件名,随意
        'Filename': OssUtil.instance.getImageName(filePath),
        //"可以填写文件夹名(对应于oss服务中的文件夹)/" + fileName
        'key': uploadName,//上传后的文件名
        'policy': OssUtil.policy,
        //Bucket 拥有者的AccessKeyId。
        'OSSAccessKeyId': data['AccessKeyId'],
        //让服务端返回200,不然,默认会返回204
        'success_action_status': '200',
        'signature': OssUtil.instance.getSignature(data['AccessKeySecret']),
        //临时用户授权时必须,需要携带后台返回的security-token
        'x-oss-security-token': data['SecurityToken'],
        'file': new MultipartFile.fromFileSync(
            filePath, OssUtil.instance.getImageNameByPath(filePath))
        //必须放在参数最后
   });
//然后通过存储地址直接把表单(formdata)上传上去;
Dio dio=Dio();
dio.options.responseType=ResponseType.plain;
Response response=awaitdio.post(<ossEndPointCdn>,data:formData);

最后

来看看我改写的


import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:dio/dio.dart' ;
import 'package:flutter/cupertino.dart';
import 'package:packageForHttp.dart';
import 'package:crypto/crypto.dart';

class OssHelper{
 
  /// 上传的oss目录
  static final uploadFileRoot = "test";
  /// oss 节点   eg 是桶名   oss-cn-shenzhen.aliyuncs.com  看实际的,一般是深圳或者上海
  static String endpoint = "http://eg.oss-cn-shenzhen.aliyuncs.com";


  // 通过服务端接口获取到如下格式数据
  Map<String, dynamic> ossMsg = {
    "StatusCode": "200",
    "AccessKeyId": "STS.NSsrKZes4cqm.....",
    "AccessKeySecret": "7eGnLZaEFsRCGYJAnrtdE9n.....",
    "Expiration": "2020-04-08T03:44:21Z",
    "SecurityToken": "CAISlQJ1q6Ft5B2y....."};
  // 获取 OSS 配置
  static getOSS(File file) async {
  /// 这里是我本地的Http helper 类, 把这个换成你自己的
    HttpUtil.get(url,success: (rep){
      if(rep.data!=null && "200"== rep.data['statusCode']){
        uploadToOss(file,rep.data);
      }
    },error: (e){

    },onFinish: (){});
  }

  // 组装数据,开始上传
  static uploadToOss(File file , Map<String, dynamic> data) async {
    //表单需要的参数: AccessKeyId、AccessKeySecret、SecurityToken;
      FormData formdata = new FormData.fromMap({
        //文件名,随意
        'Filename': OssUtil.instance.getImageName(file.path),
        //"可以填写文件夹名(对应于oss服务中的文件夹)/" + fileName
        'key': OssUtil.instance.getImageUploadName(uploadFileRoot,OssUtil.instance.getImageName(file.path)),//上传后的文件名
        'policy': OssUtil.policy,
        //Bucket 拥有者的AccessKeyId。
        'OSSAccessKeyId': data['accessKeyId'],//  accessKeyId 大小写 和服务端返回的对应就成,不要在意这些细节  下同
        //让服务端返回200,不然,默认会返回204
        'success_action_status': '200',
        'signature': OssUtil.instance.getSignature(data['accessKeySecret']),
        //临时用户授权时必须,需要携带后台返回的security-token
        'x-oss-security-token': data['securityToken'],
        'file': MultipartFile.fromFileSync(file.path, filename:OssUtil.instance.getImageNameByPath(file.path))
      });
      //然后通过存储地址直接把表单(formdata)上传上去;
      Dio dio=Dio();
      dio.options.responseType=ResponseType.plain;
      Response response=await dio.post(endpoint,data:formdata);
      if (response.statusCode == 200) {
        debugPrint("上传成功" );
      }else{
        debugPrint("错误" );
      }
  }
}

/// 照搬过来的 
class OssUtil {

  //获取验证文本域
  static String _policyText =
  '{"expiration": "2069-05-22T03:15:00.000Z","conditions": [["content-length-range", 0, 1048576000]]}';//UTC时间+8=北京时间

  //进行utf8编码
  // ignore: non_constant_identifier_names
  static List<int> _policyText_utf8 = utf8.encode(_policyText);
  //进行base64编码
  static String policy= base64.encode(_policyText_utf8);

  //再次进行utf8编码
  // ignore: non_constant_identifier_names
  static List<int> _policy_utf8 = utf8.encode(policy);

  // 工厂模式
  factory OssUtil() => _getInstance();

  static OssUtil get instance => _getInstance();
  static OssUtil _instance;

  OssUtil._internal();

  static OssUtil _getInstance() {
  if (_instance == null) {
  _instance = new OssUtil._internal();
  }
  return _instance;
  }

  /*
  *获取signature签名参数
  */
  String getSignature(String _accessKeySecret){
  //进行utf8 编码
  // ignore: non_constant_identifier_names
  List<int> AccessKeySecretUtf8 = utf8.encode(_accessKeySecret);
  //通过hmac,使用sha1进行加密
  List<int> signaturePre = new Hmac(sha1, AccessKeySecretUtf8).convert(_policy_utf8).bytes;
  //最后一步,将上述所得进行base64 编码
  String signature = base64.encode(signaturePre);
  return signature;
  }

  // ignore: slash_for_doc_comments
  /**
   * 生成上传上传图片的名称 ,获得的格式:photo/20171027175940_oCiobK
   * 可以定义上传的路径uploadPath(Oss中保存文件夹的名称)
   * @param uploadPath 上传的路径 如:/photo
   * @return photo/20171027175940_oCiobK
   */
  String getImageUploadName(String uploadPath,String filePath) {
  String imageMame = "";
  var timestamp = new DateTime.now().millisecondsSinceEpoch;
  imageMame =timestamp.toString()+"_"+getRandom(6);
  if(uploadPath!=null&&uploadPath.isNotEmpty){
  imageMame=uploadPath+"/"+imageMame;
  }
  String imageType=filePath?.substring(filePath?.lastIndexOf("."),filePath?.length);
  return imageMame+imageType;
  }

  String getImageName(String filePath) {
  String imageMame = "";
  var timestamp = new DateTime.now().millisecondsSinceEpoch;
  imageMame =timestamp.toString()+"_"+getRandom(6);
  String imageType=".jpg";
  return imageMame+imageType;
  }

  /*
  * 生成固定长度的随机字符串
  * 在原基础上添加了0123456789
  * */
  String getRandom(int num) {
  String alphabet = '0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
  String left = '';
  for (var i = 0; i < num; i++) {
//    right = right + (min + (Random().nextInt(max - min))).toString();
  left = left + alphabet[Random().nextInt(alphabet.length)];
  }
  return left;
  }

  /*
  * 根据图片本地路径获取图片名称
  * */
  String getImageNameByPath(String filePath) {
  // ignore: null_aware_before_operator
  return filePath?.substring(filePath?.lastIndexOf("/")+1,filePath?.length);
  }
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值