【flutter直接上传图片到阿里云OSS】

flutter直接上传文件到阿里云需要获取凭证,通过调用阿里云获取凭证的接口能拿到下面这些参数

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

获取凭证的接口一般是后台去对接阿里云,前端调后台接口即可。(STS.的这种AccessKeyId安全性高一些)

1.获取OSSToken信息

dart 复制代码
import 'dart:math';
import 'package:jade/https/HttpApplication.dart';
import 'package:jade/utils/alioss/AliOSSAccessTokenInfoBean.dart';
import 'package:intl/intl.dart';
import 'dart:typed_data';
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:uuid/uuid.dart';


class OSSHelper{

  //从给定的字母中生成随机字符串
  String getRandom(int num) {
    String alphabet = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
    String left = "";
    for (var i = 0; i < num; i++) {
      left = left + alphabet[Random().nextInt(alphabet.length)];
    }
    return left;
  }

  //这个时间要注意
  String getGMTDateString() {
    var date = DateTime.now();
    date = date.subtract(const Duration(hours: 8));
    return DateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", 'en').format(date);
  }


  ///使用md5加密
  String generateMD5(String data) {
    Uint8List content = new Utf8Encoder().convert(data);
    Digest digest = md5.convert(content);
    return digest.toString();
  }
  
  
  httpGetStsInfo({Function callback}){
  //根据后台给定的规则配置uuid
    var uuid = Uuid();
    String uuidStr = uuid.v4().replaceAll('-', ''); //去掉uuid中的'-',获取到32位


    String nowDate = DateTime.now().toString().substring(0,19);

	//当前时间拼接uuid,并加盐(后面这串盐由后台给定)
    String md5Str = nowDate + uuidStr + 'ikloniyq8923yvakn67q4in'; 

	//调用获取OSSToken信息的接口
    HttpApplication.getInstance().alOSSStsInfo(uuidStr, nowDate, generateMD5(md5Str),
        callBack: (result){
          if(result != null){
            AliOssAccessTokenInfoBean _aliOSSAccessTokenInfoBean = AliOssAccessTokenInfoBean.fromJson(result);
            if(_aliOSSAccessTokenInfoBean != null){
              callback(_aliOSSAccessTokenInfoBean);
            }
          }
        },
        errorCallBack: (error){});
  }
}

token信息实体类

dart 复制代码
class AliOssAccessTokenInfoBean {
  AliOssAccessTokenInfoBean({
      this.securityToken, 
      this.accessKeySecret, 
      this.accessKeyId, 
      this.expiration, 
      this.statusCode,});

  AliOssAccessTokenInfoBean.fromJson(dynamic json) {
    securityToken = json['SecurityToken'];
    accessKeySecret = json['AccessKeySecret'];
    accessKeyId = json['AccessKeyId'];
    expiration = json['Expiration'];
    statusCode = json['StatusCode'];
  }
  String securityToken;
  String accessKeySecret;
  String accessKeyId;
  String expiration;
  int statusCode;

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['SecurityToken'] = securityToken;
    map['AccessKeySecret'] = accessKeySecret;
    map['AccessKeyId'] = accessKeyId;
    map['Expiration'] = expiration;
    map['StatusCode'] = statusCode;
    return map;
  }

}

上传方法

dart 复制代码
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';

class UploadOss {

  //请求下来的AccessKeyId
  static String ossAccessKeyId;
 //请求下来的AccessKeySecret
  static String ossAccessKeySecret;
  // oss设置的bucket列表中用来存放图片视频的文件夹的名字
  static String bucket = 'zmkx';
  // 发送请求用的url,根据你自己设置的是哪个城市的
  static String url = 'https://$bucket.oss-cn-hangzhou.aliyuncs.com';

  static String host = "$bucket.oss-cn-hangzhou.aliyuncs.com"; //写入你对应的地址

//请求下来的AccessKeySecret
  static String ossSecurityToken;

  // 过期时间  请求下来的expiration 
  static String expiration;

  /*
  * @params file 要上传的文件对象
  * @params rootDir 阿里云oss设置的根目录文件夹名字
  * @param fileType 文件类型例如jpg,mp4等
  * @param callback 回调函数我这里用于传cancelToken,方便后期关闭请求
  * @param onSendProgress 上传的进度事件
  * */

  static Future<String> upload({ Uint8List file , String rootDir = 'oss/folder', String fileName,Function callback, Function onSendProgress}) async {
    String policyText = '{"expiration": "$expiration","conditions": [{"bucket": "$bucket" },["content-length-range", 0, 1048576000]]}';
    // 获取签名
    String signature = getSignature(policyText);

    BaseOptions options = new BaseOptions();
    options.responseType = ResponseType.plain;


    //创建dio对象
    Dio dio = new Dio(options);

    /*dio.options.responseType = ResponseType.plain;
    dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
      options.headers["Authorization"] = "OSS " + ossAccessKeyId + ":" + signature;
      options.headers["Host"] = host;
      options.headers["x-oss-security-token"] = ossSecurityToken;
      options.contentType = 'multipart/form-data';
      //options.headers["date"] = date;
      handler.next(options);
    }));*/

    // 生成oss的路径和文件名
    String pathName = '$rootDir/test_hyf.$fileName';

    // 请求参数的form对象
    FormData data = new FormData.fromMap({
     // 'Filename': fileName,
      'key': pathName,
      'policy': getSplicyBase64(policyText),
      'OSSAccessKeyId': ossAccessKeyId,
      'success_action_status': '200', //OSSToken信息里的StatusCode是200我设置成200,阿里云默认返回204
      'signature': signature,
      'x-oss-security-token': ossSecurityToken,
      'contentType': 'multipart/form-data',
      'file': MultipartFile.fromBytes(file),
    });

    Response response;

    print('url = $url');

    try {
      // 发送请求
      response = await dio.post(url, data: data);
      // 成功后返回文件访问路径
      if(response.statusCode == 200){ //获取OSSToken信息接口的StatusCode返多少就判断等于多少
        return '$url/$pathName';
      }
    } catch(e) {
      throw(e.message);
    }
  }

  /*
  * 生成固定长度的随机字符串
  * */
  static String getRandom(int num) {
    String alphabet = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
    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;
  }
  /*
  * 根据图片本地路径获取图片名称
  * */
  static String getImageNameByPath(String filePath) {
    // ignore: null_aware_before_operator
    return filePath?.substring(filePath?.lastIndexOf("/")+1,filePath?.length);
  }
  /*获取文件类型
  * */
  static String getFileType(String path) {
    print(path);
    List<String> array = path.split('.');
    return array[array.length -1];
  }
  /// 获取日期
  static String getDate() {
    DateTime now = DateTime.now();
    return '${now.year}${now.month}${now.day}';
  }

  // 获取plice的base64
  static getSplicyBase64(String policyText) {
    //进行utf8编码
    List<int> policyText_utf8 = utf8.encode(policyText);
    //进行base64编码
    String policy_base64 = base64.encode(policyText_utf8);
    return policy_base64;
  }

  /// 获取签名
  static String getSignature(String policyText) {
    //进行utf8编码
    List<int> policyText_utf8 = utf8.encode(policyText);
    //进行base64编码
    String policy_base64 = base64.encode(policyText_utf8);
    //再次进行utf8编码
    List<int> policy = utf8.encode(policy_base64);
    //进行utf8 编码
    List<int> key = utf8.encode(ossAccessKeySecret);
    //通过hmac,使用sha1进行加密
    List<int> signature_pre = Hmac(sha1, key).convert(policy).bytes;
    //最后一步,将上述所得进行base64 编码
    String signature = base64.encode(signature_pre);
    return signature;
  }
}

调用方式

dart 复制代码
import 'dart:typed_data';
import 'package:jade/utils/alioss/AliOSSAccessTokenInfoBean.dart';
import 'package:jade/utils/alioss/OSSHelper.dart';
import 'package:jade/utils/alioss/UploadOss.dart';
import 'package:flutter/material.dart';
import 'package:multi_image_picker/multi_image_picker.dart';

class AliUpLoadTest extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _AliUpLoadTest();
  }
}

class _AliUpLoadTest extends State<AliUpLoadTest>{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return GestureDetector(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Container(
            width: 100,
            height: 60,
            color: Colors.red,
          )
        ],
      ),
      onTap: (){
      //最好是在调完接口后对对应参数进行赋值接着使用上传方法(本人写死请求下来的token信息进行测试一直报403,应该是token信息的持有时间很短)
        OSSHelper().httpGetStsInfo(callback: (aliOSSAccessTokenInfoBean){
          AliOssAccessTokenInfoBean _aliOSSAccessTokenInfoBean = aliOSSAccessTokenInfoBean;
          setState(() {
            UploadOss.ossAccessKeyId = _aliOSSAccessTokenInfoBean.accessKeyId;
            UploadOss.ossAccessKeySecret = _aliOSSAccessTokenInfoBean.accessKeySecret;
            UploadOss.expiration = _aliOSSAccessTokenInfoBean.expiration;
            UploadOss.ossSecurityToken = _aliOSSAccessTokenInfoBean.securityToken;
          });
          openPhotoSelect(1);

        });
      },
    );
  }


  openPhotoSelect(int maxImages) async {
    try {
      List<Asset> images = await MultiImagePicker.pickImages(
        maxImages: 1,
        enableCamera: true,
        cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
        materialOptions: MaterialOptions(
          actionBarTitle: "图片选择",
          allViewTitle: "所有图片",
          useDetailsView: true,
          selectCircleStrokeColor: "#4dc8b6",
          selectionLimitReachedText: "最多选择$maxImages张图片",
        ),
      );
      Asset asset = images[0];
      uploadFile(asset);
    } on Exception catch (e) {
      print(e);
    }
  }

  Future<String> uploadFile(Asset asset ) async {
    ByteData byteData = await asset.getByteData();
    List<int> imageData = byteData.buffer.asUint8List();
    final String url = await UploadOss.upload(file: imageData,fileName:asset.name);
    print('OSS返回的文件地址 = $url');
    return url;
  }
}

以上直接是上传图片到阿里云成功调通了的demo,并没由做多图的封装。UploadOss是在网上找的工具类,百分之九十九搜出来都是这个。

还有找到另外一个工具类,不过并没有用测试过,代码:

dart 复制代码
import 'dart:collection';
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';

import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:intl/intl.dart';

class UploadUtil {

  String host = "$bucket.oss-cn-hangzhou.aliyuncs.com"; //写入你对应的地址

  // 发送请求的url,根据你自己设置的是哪个城市的
  static String url = 'https://$bucket.oss-cn-hangzhou.aliyuncs.com';

  static String ossAccessKeyId = 'STS.NV864oDQJQELywUH427zm7V46';

  static String ossAccessKeySecret = '2KAmY77P6z9C3cEMFrdESxSXdzUSpZucW6PEo3uxpewy';

  static String ossSecurityToken = 'CAISoQJ1q6Ft5B2yfSjIr5WNfY7bqY5r5oene1HkrDRnO/VB2JOf1Dz2IH1JfnVtCO4et/w3mWhS5/cZlrhIWoR4XkHeStBr1ZlM6gKmZdIFGEcHJOVW5qe+EE2/VjThvqaLEeCbIfrZfvCyER+m8gZ43br9cxi7QlWhKufnoJV7b9MRLG7aCD1dH4VuOxdFos0XPmerZpTLCBPxhXfKB0dFoxd1jXgFiZ6y2cqB8BHT/iqYv+YevNb2OYP2LZsuboV6UMfy2/dtMaTG1CJd8V8I1t8v0vEfqG2W74/AWQQMvEzeCYeOrI0zdj0eT7MhBqtJoML7kfBFoeHJn+z1sU0QYLsJDnWBHNn4mJacQrL4bcxYb7/+PG/WycGUJm9lZMuVjuJxGoABb28iVg9ghcjTcfGgi3kkzknftUIsfBQieSUlngPouzWRJQYpvb74JlVLxbVtnxrW8J/LBqlSVjRlW++1WPxgq+wX4K5KwJ8zwFwM61JnNJsm4eCMsG2lgzZw2qAIWh2cfw1wrGdz20sWBaDxy9ne/AtHiEEc2H6E23TQIcfoNtU=';

  // oss设置的bucket的名字
  static String bucket = 'zmkx';

  // 过期时间
  static String expiration = '2023-08-22T03:45:25Z';


  Future<String> ossUploadImage(Uint8List imageData,
      { String fileType, String directory = "community"}) async {
    //命名
    String timeStr = DateFormat("yyyyMMdd", 'en').format(DateTime.now());
    String pathName = "img/$directory/app$timeStr${getRandom(12)}.$fileType";

    String date = getGMTDateString();

    String contentType = 'image/$fileType';
    //签名相关
    //请求头
    SplayTreeMap<String, String> treeMap = SplayTreeMap();
    treeMap["Content-Type".toLowerCase()] = contentType.trim();
    treeMap["Content-MD5".toLowerCase()] = "";
    treeMap["Date".toLowerCase()] = date.trim();
    treeMap["x-oss-security-token".toLowerCase()] = ossSecurityToken.trim();
    String headString = "PUT\n";
    treeMap.forEach((key, value) {
      if (key.startsWith("x-oss-")) {
        headString += key;
        headString += ':';
        headString += value;
      } else {
        headString += value;
      }
      headString += '\n';
    });

    String contentString = "/$bucket/$pathName";
    String contentToSign = headString + contentString;

    List<int> key = utf8.encode(ossAccessKeySecret);
    List<int> data = utf8.encode(contentToSign);
    var signaturePre = Hmac(sha1, key)
        .convert(data)
        .bytes;
    //最后一步,将上述所得进行base64 编码
    String signature = base64.encode(signaturePre);
    String signatureA = "OSS " + ossAccessKeyId + ":" + signature;

    Dio dio = Dio();
    dio.options.responseType = ResponseType.plain;
    dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
      options.headers["Authorization"] = signatureA;
      options.headers["Host"] = host;
      options.headers["x-oss-security-token"] = ossSecurityToken;
      options.contentType = contentType;
      options.headers["date"] = date;
      handler.next(options);
    }));
    try {
      // 发送请求
      var resultUrl = url /*+ "/$pathName"*/;
      //必须转成这个类型才可以
      Stream<List<int>> stream = Stream.value(imageData);
      var rep = await dio.put(resultUrl, data: stream);
      // 成功后返回文件访问路径
      return "$url/$pathName";
    } catch (e) {
      return '错误:${e.toString()}';
    }
  }

  String getRandom(int num) {
    String alphabet = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
    String left = "";
    for (var i = 0; i < num; i++) {
      left = left + alphabet[Random().nextInt(alphabet.length)];
    }
    return left;
  }

//这个时间要注意
  String getGMTDateString() {
    var date = DateTime.now();
    date = date.subtract(const Duration(hours: 8));
    return DateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", 'en').format(date);
  }

  /*获取文件类型
  * */
  static String getFileType(String path) {
    print(path);
    List<String> array = path.split('.');
    return array[array.length -1];
  }

}
相关推荐
problc3 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
lqj_本人12 小时前
鸿蒙next选择 Flutter 开发跨平台应用的原因
flutter·华为·harmonyos
lqj_本人15 小时前
Flutter&鸿蒙next 状态管理框架对比分析
flutter·华为·harmonyos
起司锅仔19 小时前
Flutter启动流程(2)
flutter
hello world smile1 天前
最全的Flutter中pubspec.yaml及其yaml 语法的使用说明
android·前端·javascript·flutter·dart·yaml·pubspec.yaml
lqj_本人1 天前
Flutter 的 Widget 概述与常用 Widgets 与鸿蒙 Next 的对比
flutter·harmonyos
iFlyCai1 天前
极简实现酷炫动效:Flutter隐式动画指南第二篇之一些酷炫的隐式动画效果
flutter
lqj_本人1 天前
Flutter&鸿蒙next 中使用 MobX 进行状态管理
flutter·华为·harmonyos
lqj_本人1 天前
Flutter&鸿蒙next 中的 setState 使用场景与最佳实践
flutter·华为·harmonyos
hello world smile1 天前
Flutter常用命令整理
android·flutter·移动开发·android studio·安卓