翼度科技»论坛 编程开发 .net 查看内容

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

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
OSS前端直传+后端签名

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

首先安装阿里云SDK Aliyun.OSS.SDK.NetCore
  1.         public static string accessKeyId = "你的accessKeyId";
  2.         public static string accessKeySecret = "你的accessKeySecret";
  3.         public static string bucketName = "你的桶名称";
  4.         public static string endpoint = "oss-cn-beijing.aliyuncs.com";
  5.         public static int expireTime = 30;
  6.         public Dictionary<string, string> GetPolicy(string fileName)
  7.         {
  8.             var dir = DateTime.Now.ToString("yyyyMMdd") + "/";
  9.             // 构造OssClient实例。 endpoint 格式:https://oss-cn-beijing.aliyuncs.com
  10.             var ossClient = new OssClient("https://" + endpoint, accessKeyId, accessKeySecret);
  11.             var config = new PolicyConditions();
  12.             config.AddConditionItem(PolicyConditions.CondContentLengthRange, 1, 1024L * 1024 * 1024 * 5);// 文件大小范围:单位byte
  13.             config.AddConditionItem(MatchMode.StartWith, PolicyConditions.CondKey, dir);
  14.             var expire = DateTimeOffset.Now.AddMinutes(30);// 过期时间
  15.             // 生成 Policy,并进行 Base64 编码
  16.             var policy = ossClient.GeneratePostPolicy(expire.LocalDateTime, config);
  17.             var policyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(policy));
  18.             // 计算签名
  19.             var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(accessKeySecret));
  20.             var bytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(policyBase64));
  21.             var sign = Convert.ToBase64String(bytes);
  22.             // 将签名和回调的内容,返回给前端
  23.             var host = $"https://{bucketName}.{endpoint}";
  24.             var key = $"{dir}{Guid.NewGuid()}/{fileName}";
  25.             var fullUrl = $"https://{bucketName}.{endpoint}/{key}";
  26.             var rt = new Dictionary<string, string>
  27.             {
  28.                 { "OSSAccessKeyId",accessKeyId},
  29.                 { "Host",host },
  30.                 { "key",key},
  31.                 { "policy",policyBase64},
  32.                 { "Signature",sign},
  33.                 { "success_action_status","200"},
  34.                 { "fullUrl",fullUrl },
  35.                 {"expire",expire.ToString() }
  36.             };
  37.             return rt;
  38.         }
复制代码
前端首先访问后端获取签名,获取签名后使用FromData的形式上传文件
  1. async startUpload() {
  2.       // 获取后端签名和上传地址
  3.       const res = await axios.get("http://localhost:5152/api/OSS/GetPolicy", {
  4.         params: {
  5.           name: this.file.name
  6.         }
  7.       });
  8.       var formData = new FormData();
  9.       formData.append("name", this.file.name);
  10.       formData.append("OSSAccessKeyId", res.data.OSSAccessKeyId);
  11.       formData.append("key", res.data.key);
  12.       formData.append("policy", res.data.policy);
  13.       formData.append("signature", res.data.Signature);
  14.       formData.append("success_action_status", res.data.success_action_status);
  15.       formData.append("file", this.file);
  16.       axios
  17.         .post(res.data.Host, formData, {
  18.           headers: {
  19.             "Content-Type": "multipart/form-data"
  20.           },
  21.           withCredentials: false
  22.         })
  23.         .then(res => {
  24.           console.log(res);
  25.         });
  26.     }
复制代码
二、服务端STS签名前端分片上传+断点续传

当文件过大时,考虑使用分片上传和断点续传的方式来上传文件到oss,这时我们就不能直接使用accesskeyId和accessKeySecret的方式来在前端上传,以免暴露我们的密钥,当然也不能直接使用第一种的方式进行签名(或许可以,没有找到示例,也没有研究出来),所以我们采用STStoken的方式签名,然后在前端使用阿里云提供的SDK进行文件上传。
断点续传的思路是在每个分片上传的时候存储当前文件的上传进度,如果中间因为各种原因无法继续上传时,当用户重新上传同一个文件的时候,获取文件的上传进度,继续上传没有上传完的部分,而不是重新上传整个文件。为了确保断点续传前后上传的是同一个文件,我们使用md5作为存储进度的key值,如果是同一个文件,则续传,如果不是同一个文件,则从0开始上传。
首先登录阿里云开通sts账户和权限。
安装 aliyun-net-sdk-core和aliyun-net-sdk-sts sdk
  1. public Dictionary<string, string> GetSTSToken()
  2.         {
  3.             //此处使用sts账户的id和secret
  4.             var AccessKeyID = "***";
  5.             var AccessKeySecret = "***";
  6.             string bucketName = "***";
  7.             // ststoken
  8.             IClientProfile profile = DefaultProfile.GetProfile("oss-cn-beijing", AccessKeyID, AccessKeySecret);
  9.             DefaultAcsClient client = new DefaultAcsClient(profile);
  10.             var request = new AssumeRoleRequest();
  11.             request.RoleArn = "***";
  12.             request.RoleSessionName = "xxx";//这里的名字随便写
  13.             request.DurationSeconds = 3600;//过期时间
  14.             var response = client.GetAcsResponse(request);
  15.             var result = new Dictionary<string, string>
  16.             {
  17.                 {"AccessKeyId", response.Credentials.AccessKeyId},
  18.                 {"AccessKeySecret",response.Credentials.AccessKeySecret },
  19.                 {"SecurityToken",response.Credentials.SecurityToken },
  20.                 {"Expiration",response.Credentials.Expiration },
  21.                 {"BucketName",bucketName }
  22.             };
  23.             return result;
  24.         }
复制代码
签名完成后,安装阿里云oss sdk
  1. npm install ali-oss;
  2. npm install spark-md5;
复制代码
  1. <template>
  2.   
  3.    
  4.       <input type="file" @change="fileChange" />
  5.       {{ progress }}
  6.    
  7.   
  8. </template>
  9. #自行导入包,自行定义变量
  10. async fileChange(e) {
  11.       this.file = e.target.files[0];
  12.       this.uploadFile(this.file);
  13.     },
  14. async uploadFile(file) {
  15.       const objectKey = "xxx" + "/file/" + this.file.name;
  16.       // 初始化 OSS 客户端 SDK
  17.       await this.initOSSClient();
  18.       this.resumeUpload(objectKey, file);
  19.     }
  20. # 首先初始化oss 对象
  21.     async initOSSClient() {
  22.       const res = await axios.get("http://localhost:5152/api/OSS/GetSTSToken");
  23.       console.log(res);
  24.       const {
  25.         AccessKeyId,
  26.         AccessKeySecret,
  27.         SecurityToken,
  28.         BucketName
  29.       } = res.data;
  30.       this.bucketName = BucketName;
  31.       this.client = new OSS({
  32.         region: "oss-cn-beijing",
  33.         accessKeyId: AccessKeyId,
  34.         accessKeySecret: AccessKeySecret,
  35.         stsToken: SecurityToken,
  36.         bucket: BucketName
  37.       });
  38.     },
  39.     # 断点上传
  40.     async resumeUpload(objectKey, file) {
  41.       //使用SparkMd5计算文件的md5值
  42.       let md5 =await this.calculateFileMD5(file);
  43.       let checkpoint = JSON.parse(
  44.         window.localStorage.getItem("checkpoint_" + md5)
  45.       );
  46.       var _this = this;
  47.       // 重试五次。
  48.       for (let i = 0; i < 5; i++) {
  49.         try {
  50.           const result = await this.client.multipartUpload(objectKey, file, {
  51.             checkpoint,
  52.             async progress(percentage, cpt) {
  53.               checkpoint = cpt;
  54.               _this.progress = parseInt(percentage * 100);
  55.               // 将 checkpoint 保存到浏览器localstorage 中。
  56.               window.localStorage.setItem(
  57.                 "checkpoint_" + md5,
  58.                 JSON.stringify(checkpoint)
  59.               );
  60.             }
  61.           });
  62.           // 删除本地保存的 checkpoint,如果此处不删除的话,上传成功后,用户无法再次上传同名文件
  63.           window.localStorage.removeItem("checkpoint_" + md5);
  64.           break; // 跳出当前循环。
  65.         } catch (e) {
  66.           console.log(e);
  67.         }
  68.       }
  69.     },
  70.     // 使用sparkMD5 计算文件md5
  71.      calculateFileMD5(file, chunkSize = 2097152) {
  72.       // chunkSize为分块大小,默认为2MB
  73.       return new Promise((resolve, reject) => {
  74.         const fileReader = new FileReader();
  75.         let currentPosition = 0;
  76.         const spark = new SparkMD5.ArrayBuffer();
  77.         fileReader.onerror = function() {
  78.           reject("文件读取失败!");
  79.         };
  80.         fileReader.onload = function() {
  81.           spark.append(fileReader.result); // 将读取到的数据添加到MD5计算器中
  82.           currentPosition += chunkSize;
  83.           if (currentPosition < file.size) {
  84.             // 文件还没读完,继续读取下一块
  85.             loadNext();
  86.           } else {
  87.             // 文件读取完毕,计算MD5值并返回结果
  88.             const hash = spark.end();
  89.             resolve(hash);
  90.           }
  91.         };
  92.         function loadNext() {
  93.           const blob = file.slice(currentPosition, currentPosition + chunkSize);
  94.           fileReader.readAsArrayBuffer(blob);
  95.         }
  96.         // 开始读取第一块
  97.         loadNext();
  98.       });
  99.     }
复制代码
如果想自己控制上传的各步骤可以使用initiateMultipartUpload  uploadPart completeMultipartUpload 等方法自行实现各步骤,大致思路就是先initiateMultipartUpload初始化一个分片上传,返回uploadid,然后将文件按一定的大小分片,之后循环上传每个分片,完成分片之后调用completeMultipartUpload方法合并文件,这种方式比较复杂。

来源:https://www.cnblogs.com/zhangjun0204/archive/2023/03/22/17243583.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具