OSS前端直傳+后端簽名
一重父、服務(wù)端簽名后前端直傳
首先安裝阿里云SDK Aliyun.OSS.SDK.NetCore
public static string accessKeyId = "你的accessKeyId";
public static string accessKeySecret = "你的accessKeySecret";
public static string bucketName = "你的桶名稱";
public static string endpoint = "oss-cn-beijing.aliyuncs.com";
public static int expireTime = 30;
public Dictionary<string, string> GetPolicy(string fileName)
{
var dir = DateTime.Now.ToString("yyyyMMdd") + "/";
// 構(gòu)造OssClient實例。 endpoint 格式:https://oss-cn-beijing.aliyuncs.com
var ossClient = new OssClient("https://" + endpoint, accessKeyId, accessKeySecret);
var config = new PolicyConditions();
config.AddConditionItem(PolicyConditions.CondContentLengthRange, 1, 1024L * 1024 * 1024 * 5);// 文件大小范圍:單位byte
config.AddConditionItem(MatchMode.StartWith, PolicyConditions.CondKey, dir);
var expire = DateTimeOffset.Now.AddMinutes(30);// 過期時間
// 生成 Policy,并進行 Base64 編碼
var policy = ossClient.GeneratePostPolicy(expire.LocalDateTime, config);
var policyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(policy));
// 計算簽名
var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(accessKeySecret));
var bytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(policyBase64));
var sign = Convert.ToBase64String(bytes);
// 將簽名和回調(diào)的內(nèi)容,返回給前端
var host = $"https://{bucketName}.{endpoint}";
var key = $"{dir}{Guid.NewGuid()}/{fileName}";
var fullUrl = $"https://{bucketName}.{endpoint}/{key}";
var rt = new Dictionary<string, string>
{
{ "OSSAccessKeyId",accessKeyId},
{ "Host",host },
{ "key",key},
{ "policy",policyBase64},
{ "Signature",sign},
{ "success_action_status","200"},
{ "fullUrl",fullUrl },
{"expire",expire.ToString() }
};
return rt;
}
前端首先訪問后端獲取簽名,獲取簽名后使用FromData的形式上傳文件
async startUpload() {
// 獲取后端簽名和上傳地址
const res = await axios.get("http://localhost:5152/api/OSS/GetPolicy", {
params: {
name: this.file.name
}
});
var formData = new FormData();
formData.append("name", this.file.name);
formData.append("OSSAccessKeyId", res.data.OSSAccessKeyId);
formData.append("key", res.data.key);
formData.append("policy", res.data.policy);
formData.append("signature", res.data.Signature);
formData.append("success_action_status", res.data.success_action_status);
formData.append("file", this.file);
axios
.post(res.data.Host, formData, {
headers: {
"Content-Type": "multipart/form-data"
},
withCredentials: false
})
.then(res => {
console.log(res);
});
}
二靴拱、服務(wù)端STS簽名前端分片上傳+斷點續(xù)傳
當(dāng)文件過大時野崇,考慮使用分片上傳和斷點續(xù)傳的方式來上傳文件到oss,這時我們就不能直接使用accesskeyId和accessKeySecret的方式來在前端上傳八毯,以免暴露我們的密鑰,當(dāng)然也不能直接使用第一種的方式進行簽名(或許可以瞄桨,沒有找到示例话速,也沒有研究出來),所以我們采用STStoken的方式簽名芯侥,然后在前端使用阿里云提供的SDK進行文件上傳泊交。
斷點續(xù)傳的思路是在每個分片上傳的時候存儲當(dāng)前文件的上傳進度,如果中間因為各種原因無法繼續(xù)上傳時柱查,當(dāng)用戶重新上傳同一個文件的時候廓俭,獲取文件的上傳進度,繼續(xù)上傳沒有上傳完的部分唉工,而不是重新上傳整個文件研乒。為了確保斷點續(xù)傳前后上傳的是同一個文件,我們使用md5作為存儲進度的key值淋硝,如果是同一個文件雹熬,則續(xù)傳,如果不是同一個文件谣膳,則從0開始上傳竿报。
首先登錄阿里云開通sts賬戶和權(quán)限。
安裝 aliyun-net-sdk-core和aliyun-net-sdk-sts sdk
public Dictionary<string, string> GetSTSToken()
{
//此處使用sts賬戶的id和secret
var AccessKeyID = "***";
var AccessKeySecret = "***";
string bucketName = "***";
// ststoken
IClientProfile profile = DefaultProfile.GetProfile("oss-cn-beijing", AccessKeyID, AccessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
var request = new AssumeRoleRequest();
request.RoleArn = "***";
request.RoleSessionName = "xxx";//這里的名字隨便寫
request.DurationSeconds = 3600;//過期時間
var response = client.GetAcsResponse(request);
var result = new Dictionary<string, string>
{
{"AccessKeyId", response.Credentials.AccessKeyId},
{"AccessKeySecret",response.Credentials.AccessKeySecret },
{"SecurityToken",response.Credentials.SecurityToken },
{"Expiration",response.Credentials.Expiration },
{"BucketName",bucketName }
};
return result;
}
簽名完成后继谚,安裝阿里云oss sdk
npm install ali-oss;
npm install spark-md5;
<template>
<div class="hello">
<div>
<input type="file" @change="fileChange" />
<div>{{ progress }}</div>
</div>
</div>
</template>
#自行導(dǎo)入包烈菌,自行定義變量
async fileChange(e) {
this.file = e.target.files[0];
this.uploadFile(this.file);
},
async uploadFile(file) {
const objectKey = "xxx" + "/file/" + this.file.name;
// 初始化 OSS 客戶端 SDK
await this.initOSSClient();
this.resumeUpload(objectKey, file);
}
# 首先初始化oss 對象
async initOSSClient() {
const res = await axios.get("http://localhost:5152/api/OSS/GetSTSToken");
console.log(res);
const {
AccessKeyId,
AccessKeySecret,
SecurityToken,
BucketName
} = res.data;
this.bucketName = BucketName;
this.client = new OSS({
region: "oss-cn-beijing",
accessKeyId: AccessKeyId,
accessKeySecret: AccessKeySecret,
stsToken: SecurityToken,
bucket: BucketName
});
},
# 斷點上傳
async resumeUpload(objectKey, file) {
//使用SparkMd5計算文件的md5值
let md5 =await this.calculateFileMD5(file);
let checkpoint = JSON.parse(
window.localStorage.getItem("checkpoint_" + md5)
);
var _this = this;
// 重試五次。
for (let i = 0; i < 5; i++) {
try {
const result = await this.client.multipartUpload(objectKey, file, {
checkpoint,
async progress(percentage, cpt) {
checkpoint = cpt;
_this.progress = parseInt(percentage * 100);
// 將 checkpoint 保存到瀏覽器localstorage 中。
window.localStorage.setItem(
"checkpoint_" + md5,
JSON.stringify(checkpoint)
);
}
});
// 刪除本地保存的 checkpoint僧界,如果此處不刪除的話侨嘀,上傳成功后,用戶無法再次上傳同名文件
window.localStorage.removeItem("checkpoint_" + md5);
break; // 跳出當(dāng)前循環(huán)捂襟。
} catch (e) {
console.log(e);
}
}
},
// 使用sparkMD5 計算文件md5
calculateFileMD5(file, chunkSize = 2097152) {
// chunkSize為分塊大小咬腕,默認(rèn)為2MB
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
let currentPosition = 0;
const spark = new SparkMD5.ArrayBuffer();
fileReader.onerror = function() {
reject("文件讀取失敗葬荷!");
};
fileReader.onload = function() {
spark.append(fileReader.result); // 將讀取到的數(shù)據(jù)添加到MD5計算器中
currentPosition += chunkSize;
if (currentPosition < file.size) {
// 文件還沒讀完涨共,繼續(xù)讀取下一塊
loadNext();
} else {
// 文件讀取完畢,計算MD5值并返回結(jié)果
const hash = spark.end();
resolve(hash);
}
};
function loadNext() {
const blob = file.slice(currentPosition, currentPosition + chunkSize);
fileReader.readAsArrayBuffer(blob);
}
// 開始讀取第一塊
loadNext();
});
}
如果想自己控制上傳的各步驟可以使用initiateMultipartUpload uploadPart completeMultipartUpload 等方法自行實現(xiàn)各步驟宠漩,大致思路就是先initiateMultipartUpload初始化一個分片上傳举反,返回uploadid,然后將文件按一定的大小分片扒吁,之后循環(huán)上傳每個分片火鼻,完成分片之后調(diào)用completeMultipartUpload方法合并文件,這種方式比較復(fù)雜。