vue+java+oss 实现前端到API获取sts临时凭证后,前端进行分片上传大文件,带文件生成MD5

获取sts临时凭证的java接口


/**
 * sts api
 * @author lpd
 */
@RestController
@RequestMapping("/api/oss")
public class OssStsApiController extends AbstractController {

    /**
     * oss服务注入
     */
    @Autowired
    private OssProperties ossProperties;
    /**
     * LOGGER
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(OssStsApiController.class);

    /***
     * 获取临时凭证

     * @return
     */
    @GetMapping("/sts")
    public CommonResponse sts() throws com.aliyuncs.exceptions.ClientException {
        CommonResponse commonResponse = CommonResponse.createCommonResponse();

        // STS服务接入点,例如sts.cn-hangzhou.aliyuncs.com。您可以通过公网或者VPC接入STS服务。
        String endpoint = "sts.cn-hangzhou.aliyuncs.com";
        // 从环境变量中获取步骤1生成的RAM用户的访问密钥(AccessKey ID和AccessKey Secret)。
        // oss的key和密钥
        String accessKeyId = ossProperties.getWebAccessKeyId();
        String accessKeySecret = ossProperties.getWebAccessKeySecret();
        // 从环境变量中获取步骤3生成的RAM角色的RamRoleArn。
        String roleArn = ossProperties.getWebAcs();
        // 自定义角色会话名称,用来区分不同的令牌,例如可填写为SessionTest。
        String roleSessionName = "Session";
        // 临时访问凭证将获得角色拥有的所有权限。
        String policy = null;
        // 临时访问凭证的有效时间,单位为秒。最小值为900,最大值以当前角色设定的最大会话时间为准。当前角色最大会话时间取值范围为3600秒~43200秒,默认值为3600秒。
        // 在上传大文件或者其他较耗时的使用场景中,建议合理设置临时访问凭证的有效时间,确保在完成目标任务前无需反复调用STS服务以获取临时访问凭证。
        Long durationSeconds = 1800L;
        try {
            // 发起STS请求所在的地域。建议保留默认值,默认值为空字符串("")。
            String regionId = "";
            // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
            DefaultProfile.addEndpoint(regionId, "Sts", endpoint);
            // 添加endpoint。适用于Java SDK 3.12.0以下版本。
            // DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
            // 构造default profile。
            IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
            // 构造client。
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            // 适用于Java SDK 3.12.0及以上版本。
            request.setSysMethod(MethodType.POST);
            // 适用于Java SDK 3.12.0以下版本。
            // request.setMethod(MethodType.POST);
            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
            request.setPolicy(policy);
            request.setDurationSeconds(durationSeconds);
            final AssumeRoleResponse response = client.getAcsResponse(request);
            LOGGER.warn("tempAccessKeyId:" + response.getCredentials().getAccessKeyId());
            LOGGER.warn("tempSecretAccessKey:" + response.getCredentials().getAccessKeySecret());
            LOGGER.warn("sessionToken:" + response.getCredentials().getSecurityToken());

            OssStsResponseInfo ossStsResponseInfo = new OssStsResponseInfo();
            ossStsResponseInfo.setSessionToken(response.getCredentials().getSecurityToken());
            ossStsResponseInfo.setTempAccessKeyId(response.getCredentials().getAccessKeyId());
            ossStsResponseInfo.setTempSecretAccessKey(response.getCredentials().getAccessKeySecret());
            commonResponse.setData(ossStsResponseInfo);
            return commonResponse;
        } catch (ClientException e) {
            LOGGER.error("RequestId: " + e.getRequestId());
        }
        return null;
    }


}

前端代码:

npm install js-md5 --save

npm install ali-oss --save

        <el-upload
                  class="upload-demo"
                  drag
                  action="https://jsonplaceholder.typicode.com/posts/"
                  accept=".rar,.zip,.dwg,.dxf,.pdf,.stp,.step,.doc,.docx,.sldprt,.sldasm"
                  :format = fileType
                  :on-error="videoHandleError"
                  :on-change="handleFileChange"
                  :before-upload="beforeAvatarUpload">
                  <div class="el-upload__text" style="margin-top: 16px;">
                    <span v-if="dataFileName === ''">将文件拖到此处,或<em>点击上传</em></span>
                    <span v-else>{{dataFileName}} <em>重新上传</em></span>
                  </div>
                  <div class="el-upload__text" style="margin-top: 14px;">
                    <span style="color: #999999;font-size: 12px;">

                      <div>文件大小:1024MB</div>
                    </span>
                  </div>
                </el-upload>
<!--          :on-change="handleFileChange"-->
                <div style="line-height: 30px;color: #f07d7d;font-size: 12px;">上传格式:.rar .zip .dwg .dxf .pdf .doc .docx .pdf .stp .step .sldprt .sldasm </div>
                <el-progress text-color="#ffffff" :percentage="uploadProgress" :text-inside="true" :stroke-width="20"
                 style="width: 320px;"></el-progress>
  import OSS from "ali-oss";
  import md5 from 'js-md5';


// 字符串截取
      truncateString(str, prefixLength, suffixLength) {
        const totalLength = prefixLength + suffixLength + 3; // 3 characters for "..."

        if (str.length <= totalLength) {
          // If the string is short enough, return it as is.
          return str;
        }

        const prefix = str.substring(0, prefixLength);
        const suffix = str.substring(str.length - suffixLength);

        return `${prefix}...${suffix}`;
      },

      videoHandleError(){
        // 上传失败
        const that = this;
        that.$message({
          message: '上传失败!',
          type: 'error',
          duration: 1 * 1000
        });
      },


      // 生成和后台对应md5
      calculateFileMD5(file) {
        return new Promise((resolve, reject) => {
          // 创建一个FileReader实例
          const reader = new FileReader();

          // 当文件读取完成时触发
          reader.onload = (e) => {
            // 使用js-md5库计算读取到的文件的MD5
            const hash = md5(e.target.result);
            resolve(hash);
          };

          // 当读取文件出错时触发
          reader.onerror = (error) => {
            reject(error);
          };
          // 读取文件内容为二进制字符串
          reader.readAsArrayBuffer(file);
        });

    },


      // 选择文件后开始上传
      handleFileChange(file,fileList) {

        const that = this;
        const fieldExtensions = file.name.split(".").pop();
        if(!that.fileType.includes(fieldExtensions)){
          return false;
        }
        if(file.size > 1024 * 1024 * 1024){
          return false;
        }

        if(that.submitUploadDisabled){
          that.$message({
            message: '正在上传,请勿重复上传!',
            type: 'error',
            duration: 2 * 1000
          });
          return false;
        }else {
          // 先计算文件的MD5
          that.calculateFileMD5(file.raw).then(md5 => {
            that.resMd5 = md5;
            that.form.fileMd5 = md5
            // 判断md5是否存在
            this.$axios.get(ResourceCreation.getResourcesMd5,{
              params: {md5: that.resMd5}
            }).then(res => {
              if(res === "成功"){
                that.uploadedCount = 0;
                that.submitUploadDisabled = true;
                const lastDotIndex = file.name.lastIndexOf('.')
                if(lastDotIndex === -1 || lastDotIndex === 0){
                  that.form.title = file.name;
                }else {
                  that.form.title = file.name.slice(0,lastDotIndex);
                }

                // 简单上传
                if(file.size < 5 * 1024 * 1024){
                    that.putObject(file);
                }else {
                  // 分片上传
                    that.multipartUpload(file);
                }

              }else {
                this.$message({
                  message: "请上传原创且不重复的资源!",
                  type: 'error'
                });
                return false
              }

            })
          }).catch(error => {
            console.error('Error calculating MD5:', error);
          });

        }

      },



      // 简单上传
      async putObject(file) {
        const that = this;
        const fieldExtensions = file.name.split(".").pop();
        const fileName = Date.now().toString() + "-" + this.loginUserId;
        const objectName = '/resource/' + fileName + '.' +fieldExtensions;
        const filePath = '/resource/' + fileName + '.' +fieldExtensions;
        try {
          const options = {
            meta: { temp: "demo" },
            mime: "json",
            headers: { "Content-Type": "text/plain" },
          };

          const resultSts = await that.$axios.get(ResourceCreation.getSts);
          if(resultSts === null){
            that.$message({
              message: '临时凭证获取失败,请稍后重试!',
              type: 'error',
              duration: 2 * 1000
            });
            return false;
          }
          const store = new OSS({
            // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
            accessKeyId: resultSts.tempAccessKeyId,
            accessKeySecret: resultSts.tempSecretAccessKey,
            // 从STS服务获取的安全令牌(SecurityToken)。
            stsToken: resultSts.sessionToken,
            // 填写Bucket所在地域。以华东1(杭州)为例,设置region为oss-cn-hangzhou。
            region: "oss-cn-hangzhou",
            // 填写Bucket名称,例如examplebucket。
            bucket: "bucketName",
          });


          const result = await store.put(objectName, file.raw, options);

          if(result.res.status === 200){
            that.cnUgcUploadStatusInfo.title = file.name
            that.cnUgcUploadStatusInfo.filePath = filePath
            that.form.filePath = filePath
            that.form.size = file.size
            that.dataFileName = that.truncateString(file.name,18,18)
            that.form.fileName =  file.name
            that.form.fileext = fieldExtensions
            that.uploadProgress = 100
            that.$axios.post(ResourceCreation.ugcUploadStatusInsert,that.cnUgcUploadStatusInfo).then(res => {

              that.submitUploadDisabled = false;

            })
            // ugcUploadStatusInsert
          }else {
            that.$message({
              message: '网络异常,请刷新页面后重新上传!',
              type: 'error',
              duration: 2 * 1000
            });
            return false;
          }

        } catch (e) {
          console.log(e);
        }
      },

      // 开始分片上传。
      async multipartUpload(file) {
        const that = this;
        const options = {
          // 获取分片上传进度、断点和返回值。
          progress: (p, cpt, res) => {
            that.uploadProgress = parseFloat((p * 100).toFixed(2));
          },
          // 设置并发上传的分片数量。
          parallel: 4,
          // 设置分片大小。默认值为1 MB,最小值为100 KB。
          partSize: 1024 * 1024,
          // headers,
          // 自定义元数据,通过HeadObject接口可以获取Object的元数据。
          meta: { year: 2020, people: "test" },
          mime: "text/plain",
        };
        const fieldExtensions = file.name.split(".").pop();
        const fileName = Date.now().toString() + "-" + this.loginUserId;
        const objectName = '/resource/' + fileName + '.' +fieldExtensions;
        const filePath = '/resource/' + fileName + '.' +fieldExtensions;
        try {

          const resultSts = await that.$axios.get(ResourceCreation.getSts);
          if(resultSts === null){
            that.$message({
              message: '临时凭证获取失败,请稍后重试!',
              type: 'error',
              duration: 2 * 1000
            });
            return false;
          }
          const store = new OSS({
            // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
            accessKeyId: resultSts.tempAccessKeyId,
            accessKeySecret: resultSts.tempSecretAccessKey,
            // 从STS服务获取的安全令牌(SecurityToken)。
            stsToken: resultSts.sessionToken,
            // 填写Bucket所在地域。以华东1(杭州)为例,设置region为oss-cn-hangzhou。
            region: "oss-cn-hangzhou",
            // 填写Bucket名称,例如examplebucket。
            bucket: "bucketName",
          });

          const result = await store.multipartUpload(objectName, file.raw, {
            ...options
          });

          if(result.res.status === 200){
            that.cnUgcUploadStatusInfo.title = file.name
            that.cnUgcUploadStatusInfo.filePath = filePath
            that.form.filePath = filePath
            that.dataFileName = that.truncateString(file.name,18,18)
            that.form.size = file.size
            that.form.fileName =  file.name
            that.form.fileext = fieldExtensions
            that.$axios.post(ResourceCreation.ugcUploadStatusInsert,that.cnUgcUploadStatusInfo).then(res => {

              that.submitUploadDisabled = false;

            })
            // ugcUploadStatusInsert
          }else {
            that.$message({
              message: '网络异常,请刷新页面后重新上传!',
              type: 'error',
              duration: 2 * 1000
            });
            return false;
          }

        } catch (e) {
          // 捕获超时异常。
          if (e.code === 'ConnectionTimeoutError') {
            console.log('TimeoutError');
            // do ConnectionTimeoutError operation
          }
          console.log(e);
        }
      },

效果图:

上传1G大概不到2分钟的速度

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值