2

阿里云OSS前端直传+net core后端签名 - 天际层云

 1 year ago
source link: https://www.cnblogs.com/zhangjun0204/p/17243583.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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方法合并文件,这种方式比较复杂。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK