一种Flutter上传图片到阿里OSS的方式

626 阅读1分钟

一种Flutter上传图片到阿里OSS的方式

折腾了几天的flutter上传图片到阿里云,连通过flutter通道调用原生都写完了,老大却说一定有直接使用flutter的方式,只好再继续研究了。

扒了阿里云上传安卓SDK的源码,又用抓包的方式在flutter这边获取更多的信息(否则的话只能拿到403等,根本没有有用的信息),总算是搞定了。

首先引入包crypto: ^2.1.4

        dio: ^4.0.6

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';

const String bucket = '替换成你的bucket';
String host = "$bucket.oss-cn-beijing.aliyuncs.com"; //写入你对应的地址
String url = 'https://$bucket.oss-cn-beijing.aliyuncs.com';

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

  //第一步 从服务器获取stsinfo
  Dio dio = Dio();

  Map params = {}; //参数自理
  Response e = await dio.get("baseUrl/oss/getStsInfo", //地址自理
      queryParameters: Map.from(params));
  Map<String, dynamic> responseData = jsonDecode(e.data);
  String statusCode = responseData["StatusCode"];
  String accessKeyId = responseData["AccessKeyId"];
  String accessKeySecret = responseData["AccessKeySecret"];
  String securityToken = responseData["SecurityToken"];

  String date = getGMTDateString();

  if (statusCode == "200") {
    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()] = securityToken.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(accessKeySecret);
    List<int> data = utf8.encode(contentToSign);
    var signaturePre = Hmac(sha1, key).convert(data).bytes;
    //最后一步,将上述所得进行base64 编码
    String signature = base64.encode(signaturePre);
    String signatureA = "OSS " + accessKeyId + ":" + 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"] = securityToken;
      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 "替换成你的base地址/$pathName";
    } catch (e) {
      return '';
    }
  }
  return "";
}

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);
}