flutter直接上传文件到阿里云需要获取凭证,通过调用阿里云获取凭证的接口能拿到下面这些参数
{"StatusCode": 200,"AccessKeyId": "STS.NSsrKZes4cqm.....","AccessKeySecret": "7eGnLZaEFsRCGYJAnrtdE9n.....","Expiration": "2020-04-08T03:44:21Z","SecurityToken": "CAISlQJ1q6Ft5B2y....."}
获取凭证的接口一般是后台去对接阿里云,前端调后台接口即可。(STS.的这种AccessKeyId安全性高一些)
1.获取OSSToken信息
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}){//根据后台给定的规则配置uuidvar 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信息实体类
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;}}
上传方法
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 {//请求下来的AccessKeyIdstatic String ossAccessKeyId; //请求下来的AccessKeySecretstatic 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"; //写入你对应的地址//请求下来的AccessKeySecretstatic 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_operatorreturn 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的base64static 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;}}
调用方式
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{State<StatefulWidget> createState() {// TODO: implement createStatereturn _AliUpLoadTest();}}class _AliUpLoadTest extends State<AliUpLoadTest>{Widget build(BuildContext context) {// TODO: implement buildreturn 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是在网上找的工具类,百分之九十九搜出来都是这个。
还有找到另外一个工具类,不过并没有用测试过,代码:
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];}}