vue+oss实现服务端签名后直传的图片上传功能



前言

基于Post Policy的使用规则在服务端通过java后端代码完成签名,然后通过表单直传数据到OSS。由于服务端签名直传无需将AccessKey暴露在前端页面,相比JavaScript客户端签名直传具有更高的安全性。


一、java服务端签名接口设计

  1. 引入OSS依赖
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
  1. Controller层设计
    @GetMapping("/policy")
    public R policy() {
        LinkedHashMap<String, String> map = ossService.policy();
        return R.ok().put("data",map);
    }
  1. 实现类设计
@Autowired
OSS ossClient;
@Value("${spring.cloud.alicloud.oss.endpoint}")
private String endpoint;
@Value("${spring.cloud.alicloud.oss.bucket}")
private String bucket;

@Value("${spring.cloud.alicloud.access-key}")
private String accessId;

public LinkedHashMap<String, String> policy() {

        // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
        // 填写Host地址,格式为https://bucketname.endpoint。
        String host = "https://" + bucket + "." + endpoint;
        // 设置上传回调URL,即回调服务器地址,用于处理应用服务器与OSS之间的通信。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。
//        String callbackUrl = "https://192.168.0.0:8888";
        // 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
        String dir = "banner/uat/";

        Map<String, String> respMap = null;
        // 创建ossClient实例。
//        OSS ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey);
        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);

            respMap = new LinkedHashMap<String, String>();
            respMap.put("accessId", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            // 设置服务端返回200状态码,默认返回204。
            respMap.put("success_action_status","200");
        } catch (Exception e) {
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        }
        return (LinkedHashMap<String, String>) respMap;
    }

在这里插入图片描述

二、Vue + element UI的upload图片上传

  1. 页面
    在这里插入图片描述
  2. 图片上传前处理方法
    在这里插入图片描述
  3. 上传成功后调用方法
    在这里插入图片描述
  4. 前端完整代码
    父组件:
<el-form-item label="上传图片" :prop="image" :rules="rules.image">
   <SingleUpload v-model="image"></SingleUpload>
 </el-form-item>
import SingleUpload from "@/views/subupload";

子组件:

<template>
  <div>
    <el-upload
      class="avatar-uploader"
      :data="dataObj"
      action="https://ron-test.oss-cn-shanghai.aliyuncs.com"
      :show-file-list="false"
      :on-success="handleAvatarSuccess"
      :before-upload="beforeAvatarUpload"
    >
      <img v-if="imageUrl" :src="imageUrl" class="avatar" />
      <i v-else class="el-icon-plus avatar-uploader-icon"></i>
    </el-upload>
  </div>
</template>

<script>
import { policy } from "@/api/upload";
export default {
  name: "singleUpload",
  props: {
    value: String
  },
  computed: {
    imageUrl() {
      return this.value;
    }
  },
  data() {
    return {
      //   imageUrl: "",
      dataObj: {
        accessId: "",
        policy: "",
        signature: "",
        dir: "",
        host: "",
        expire: ""
      }
    };
  },
  methods: {
    emitInput(val) {
      this.$emit("input", val);
    },
    handleAvatarSuccess(file) {
      console.log("上传成功", file);
      //   this.imageUrl = URL.createObjectURL(file.raw);
      this.emitInput(
      	// 将图片路径传给父组件
        this.dataObj.host + "/" + this.dataObj.key.replace("${filename}", file.name)
      );
    },
    beforeAvatarUpload(file) {
      console.log(file);
      let _self = this;
      return policy()
        .then(response => {
          console.log("响应的数据", response.data);
          _self.dataObj.policy = response.data.policy;
          _self.dataObj.signature = response.data.signature;
          _self.dataObj.ossaccessKeyId = response.data.accessId;
          _self.dataObj.key = response.data.dir + "random" + file.name;
          _self.dataObj.dir = response.data.dir;
          _self.dataObj.host = response.data.host;
          // 设置服务端返回200状态码,默认返回204。
          _self.dataObj.success_action_status = response.data.success_action_status;
          console.log("响应的数据222。。。", _self.dataObj);
        })
        .catch(err => {
          console.log("error", err);
        });
    }
  },
  created() {},
  mounted() {}
};
</script>

<style>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
  width: 178px;
  height: 178px;
  display: block;
}
.bg-purple {
  background: #d3dce6;
}
.bg-purple-light {
  background: #e5e9f2;
}
.grid-content {
  border-radius: 4px;
  min-height: 36px;
}
</style>

在这里插入图片描述

三、前端直传oss可能出现跨域问题

需要进入oss修改相关跨域策略
在这里插入图片描述
编辑跨域规则
在这里插入图片描述
确定保存
在这里插入图片描述

四、服务端签名流程

在这里插入图片描述

  1. 用户向应用服务器请求上传Policy和回调
  2. 应用服务器返回上传Policy和签名给用户
    在这里插入图片描述
字段描述
accessId用户请求的AccessKey ID
policy用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串
signature对Policy签名后的字符串
dir限制上传的文件前缀
host用户发送上传请求的域名 说明:host不支持自定义域名
expire由服务器端指定的Policy过期时间,格式为Unix时间戳(自UTC时间1970年01月01号开始的秒数)
  1. 用户使用Post方法向OSS发送文件上传请求
    在这里插入图片描述

总结

与数据上传至服务器,再由服务器上传到oss相比:(Vue+element Upload利用http-request实现第三方地址图片上传

  • 上传快:通过应用服务端上传,用户数据需先上传到应用服务器,之后再上传到OSS,网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
  • 扩展性好:如果后续用户数量逐渐增加,则应用服务器会成为瓶颈。
  • 费用低:由于OSS上行流量是免费的,如果数据直传到OSS,将节省多台应用服务器的费用。
Logo

前往低代码交流专区

更多推荐