阿里云OSS前端直传+net core后端签名

  • 阿里云OSS前端直传+net core后端签名已关闭评论
  • 183 次浏览
  • A+
所属分类:.NET技术
摘要

首先安装阿里云SDK Aliyun.OSS.SDK.NetCore前端首先访问后端获取签名,获取签名后使用FromData的形式上传文件


OSS前端直传+后端签名

一、服务端签名后前端直传

首先安装阿里云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") + "/";             // 构造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);             // 将签名和回调的内容,返回给前端             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);         });     } 

二、服务端STS签名前端分片上传+断点续传

当文件过大时,考虑使用分片上传和断点续传的方式来上传文件到oss,这时我们就不能直接使用accesskeyId和accessKeySecret的方式来在前端上传,以免暴露我们的密钥,当然也不能直接使用第一种的方式进行签名(或许可以,没有找到示例,也没有研究出来),所以我们采用STStoken的方式签名,然后在前端使用阿里云提供的SDK进行文件上传。

断点续传的思路是在每个分片上传的时候存储当前文件的上传进度,如果中间因为各种原因无法继续上传时,当用户重新上传同一个文件的时候,获取文件的上传进度,继续上传没有上传完的部分,而不是重新上传整个文件。为了确保断点续传前后上传的是同一个文件,我们使用md5作为存储进度的key值,如果是同一个文件,则续传,如果不是同一个文件,则从0开始上传。

首先登录阿里云开通sts账户和权限。

安装 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> #自行导入包,自行定义变量 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; // 跳出当前循环。         } catch (e) {           console.log(e);         }       }     },     // 使用sparkMD5 计算文件md5      calculateFileMD5(file, chunkSize = 2097152) {       // chunkSize为分块大小,默认为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); // 将读取到的数据添加到MD5计算器中            currentPosition += chunkSize;           if (currentPosition < file.size) {             // 文件还没读完,继续读取下一块             loadNext();           } else {             // 文件读取完毕,计算MD5值并返回结果             const hash = spark.end();             resolve(hash);           }         };          function loadNext() {           const blob = file.slice(currentPosition, currentPosition + chunkSize);           fileReader.readAsArrayBuffer(blob);         }          // 开始读取第一块         loadNext();       });     } 

如果想自己控制上传的各步骤可以使用initiateMultipartUpload uploadPart completeMultipartUpload 等方法自行实现各步骤,大致思路就是先initiateMultipartUpload初始化一个分片上传,返回uploadid,然后将文件按一定的大小分片,之后循环上传每个分片,完成分片之后调用completeMultipartUpload方法合并文件,这种方式比较复杂。