阿里云对象存储【OSS】服务端签名后前端vue分片直传

1、前言

阿里云对象存储OSSObject Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,这也是我们开发过程中较为常用的一个服务。Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS。

而在这里则是基于 Post Policy(用户表单上传的策略) 的使用规则在服务端完成签名,然后通过表单直传数据到OSS。由于服务端签名直传无需将AccessKey暴露在前端页面,相比JavaScript客户端签名直传具有更高的安全性。
在这里插入图片描述

2、阿里云oss开通服务这里省略…

3、服务端

3.1、依赖导入

在服务端,主要就是对OSS进行一个配置连接,利用阿里云提供的API生成需要的签名。针对这一需求,我们需要导入阿里云OSS云存储依赖,注意的是这里已忽略Spring web、Lombok等依赖。

<dependencies>
    <!-- 略 -->
    <!--阿里云OSS云存储依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
        <version>2.2.0.RELEASE</version>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3.2、配置文件

oss:
  endpoint: 你的地域节点
  accessKeyId: 你的AK
  accessKeySecret: 你的AS
  bucket: 你的Bucket名

为了数据的统一管理,将服务所需数据统一填写在nacos文件中,同样也可以直接存放在类中,下面是各个属性数据的获取处:

  • endpoint:地域节点,位于对象存储/Bucket
    列表/所创建的Bucket/概览下的访问域名的外网访问的Endpoint(地域节点);
  • accessKeyId:创建子账户时生成的AK,如果前面保存了可直接cv,没有的话也可前往子账户界面进行查看;
  • accessKeySecret:创建子账户时生成的AS,只能前面保存然后进行cv,没有保存的话需重新创建子账户。
  • bucket:所创建的Bucket名;

3.3、代码编写

编写实体类

@Data
public class OssConfigProperties {

    private String endpoint;

    private String accessKeyId;

    private String accessKeySecret;

    private String bucket;

}

初始化容器

@Slf4j
@Configuration
public class OssConfig {

    @Bean("ossConfigPro")
    @ConfigurationProperties(prefix = "oss")
    public OssConfigProperties ossConfigProperties() {
        return new OssConfigProperties();
    }

    @Bean("OssUtils")
    public OssUtils ossOperationUtils(@Autowired OssConfigProperties ossConfigPro) {
        return new OssUtils(ossConfigPro);
    }
    
}
@Slf4j
public class OssUtils {

    private final OSS ossClient;

    private final String bucketName;

    private final String accessId;

    private String endpoint;

    //初始化oss客户端
    public OssUtils(OssConfigProperties ossConfigPro) {
        String endpoint = ossConfigPro.getEndpoint();
        String accessId = ossConfigPro.getAccessId();
        String accessKey = ossConfigPro.getAccessKey();
        String bucketName = ossConfigPro.getBucket();
        if (StringUtils.isBlank(endpoint) || StringUtils.isBlank(accessId)
                || StringUtils.isBlank(accessKey) || StringUtils.isBlank(bucketName)) {
            throw new ServiceException("please check oss config!");
        }
        this.ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey);
        this.bucketName = bucketName;
        this.accessId = accessId;
        this.endpoint = endpoint;
    }
    }

   /**
     * 获取签名信息
     *
     * @return {@link PolicyVo}
     */
    public PolicyVo getPolicy(String dir) {
        // host的格式为 bucketname.endpoint,即https://你的bucket名.地域节点/文件名.文件后缀
        endpoint = endpoint.replace("https://", "");
        // 用户上传文件时指定的前缀,即存放在以时间命名的文件夹内
        dir = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String host = "https://" + bucketName + "." + endpoint;
        PolicyVo policyVo = new PolicyVo();
        try {
            // 过期时间
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            // 生成签名
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);
            // AK
            policyVo.setAccessId(accessId);
            // 用户表单上传的策略(Policy)
            policyVo.setPolicy(encodedPolicy);
            // 签名
            policyVo.setSignature(postSignature);
            // 上传文件时指定的前缀
            policyVo.setDir(dir);
            // oss保存文件的host
            policyVo.setHost(host);
            // 过期时间
            policyVo.setExpire(String.valueOf(expireEndTime / 1000));
        } catch (Exception e) {
            log.info(e.getMessage());
        }
        return policyVo;
    }

签名信息实体

@Data
@ApiModel("签名信息实体")
public class PolicyVo {

    @ApiModelProperty("AK")
    private String accessId;

    @ApiModelProperty("用户表单上传的策略(Policy)")
    private String policy;

    @ApiModelProperty("签名")
    private String signature;

    @ApiModelProperty("上传文件时指定的前缀")
    private String dir;

    @ApiModelProperty("oss保存文件的host")
    private String host;

    @ApiModelProperty("过期时间")
    private String expire;

}

控制层编写

@Resource
private OssUtils ossUtils;
    
@GetMapping(path = "/getPolicy")
@ApiOperation(value = "获取签名信息(获取文件签名)")
public BaseResponse<PolicyVo> getPolicy() {
    return BaseResponse.success(ossUtils.getPolicy());
}

参考产品 https://help.aliyun.com/document_detail/91868.html?spm=a2c4g.267439.0.0.68125d3f9Nc7YP

4、前端联调

4.1、组件编写

这里使用到的前端技术为Vue+Element-UI,这里只提供了单独的文件上传组件,大家自行导入使用即可,但是记得下载Element-UI以及UUID的依赖包。注意的是上传组件中的action需要你创建的Bucket中的Bucket域名,这个和地域节点获取的地方一致。

<template> 
  <div>
    <el-upload
      action="你创建的Bucket中的Bucket域名,xu"
      :data="dataObj"
      list-type="picture"
      :multiple="false" :show-file-list="showFileList"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview">
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="fileList[0].url" alt="">
    </el-dialog>
  </div>
</template>
<script>

  export default {
    name: 'singleUpload',
    props: {
      value: String
    },
    computed: {
      imageUrl() {
        return this.value;
      },
      imageName() {
        if (this.value != null && this.value !== '') {
          return this.value.substr(this.value.lastIndexOf("/") + 1);
        } else {
          return null;
        }
      },
      fileList() {
        return [{
          name: this.imageName,
          url: this.imageUrl
        }]
      },
      showFileList: {
        get: function () {
          return this.value !== null && this.value !== ''&& this.value!==undefined;
        },
        set: function (newValue) {
        }
      }
    },
    data() {
      return {
        // 封装服务端返回的数据
        dataObj: {
          policy: '',
          signature: '',
          key: '',
          ossaccessKeyId: '',
          dir: '',
          host: '',
        },
        dialogVisible: false
      };
    },
    methods: {
      emitInput(val) {
        this.$emit('input', val)
      },
      handleRemove(file, fileList) {
        this.emitInput('');
      },
      handlePreview(file) {
        this.dialogVisible = true;
      },
      beforeUpload(file) {
        console.log(file.size);
        let _self = this;
        return new Promise((resolve, reject) => {
          // 这里使用了封装的请求方式,可以直接换成axios即可
          http({
            url: http.adornUrl("/oss/policy"),
            method: "get",
            params: http.adornParams({})
          }).then(response => {
            console.log("响应的数据",response);
            _self.dataObj.policy = response.data.policy;
            _self.dataObj.signature = response.data.signature;
            _self.dataObj.ossaccessKeyId = response.data.accessid;
            // 存放进oss的命名格式为uuid+原本文件名
            _self.dataObj.key = response.data.dir + '/' + getUUID()+'_${filename}';
            _self.dataObj.dir = response.data.dir;
            _self.dataObj.host = response.data.host;
            resolve(true)
          }).catch(err => {
            reject(false)
          })
        })
      },
      handleUploadSuccess(res, file) {
        console.log("上传成功...")
        this.showFileList = true;
        this.fileList.pop();
        this.fileList.push({name: file.name, url: this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name) });
        this.emitInput(this.fileList[0].url);
      }
    }
  }
</script>
<style>

</style>
  • ps:前端直传
    // 设置服务端返回200状态码,默认返回204。
    ‘success_action_status’ : ‘200’,

4.2、开启跨域访问

这时候功能按道理来说其实都是可以正常运行的了,只是在前端测试的时候会出现CORS跨域问题。这时因为客户端进行表单直传到OSS时,会从浏览器向OSS发送带有Origin的请求消息。OSS对带有Origin头的请求消息会进行跨域规则(CORS)的验证。因此需要为Bucket设置跨域规则以支持Post方法。

  1. 登录OSS管理控制台
  2. 单击Bucket列表,然后单击目标Bucket名称。
  3. 在左侧导航栏,选择****权限管理** > 跨域设置,然后在跨域设置区域,单击设置**。
  4. 单击创建规则,配置如下图所示,确认即可。

在这里插入图片描述
敬请期待我的下一篇文章,谢谢。
在这里插入图片描述

参考文章:
https://blog.csdn.net/Aqting/article/details/126486282?spm=1001.2014.3001.5501

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值