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