vue+oss实现服务端签名后直传的图片上传功能
OSS服务端签名后直传vue + element UI 实现图片直传到OSS
·
vue+oss实现服务端签名后直传的图片上传功能
vue+oss实现服务端签名后直传的图片上传功能
前言
基于Post Policy的使用规则在服务端通过java后端代码完成签名,然后通过表单直传数据到OSS。由于服务端签名直传无需将AccessKey暴露在前端页面,相比JavaScript客户端签名直传具有更高的安全性。
一、java服务端签名接口设计
- 引入OSS依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
- Controller层设计
@GetMapping("/policy")
public R policy() {
LinkedHashMap<String, String> map = ossService.policy();
return R.ok().put("data",map);
}
- 实现类设计
@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图片上传
- 页面
- 图片上传前处理方法
- 上传成功后调用方法
- 前端完整代码
父组件:
<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修改相关跨域策略
编辑跨域规则
确定保存
四、服务端签名流程
- 用户向应用服务器请求上传Policy和回调
- 应用服务器返回上传Policy和签名给用户
字段 | 描述 |
---|---|
accessId | 用户请求的AccessKey ID |
policy | 用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串 |
signature | 对Policy签名后的字符串 |
dir | 限制上传的文件前缀 |
host | 用户发送上传请求的域名 说明:host不支持自定义域名 |
expire | 由服务器端指定的Policy过期时间,格式为Unix时间戳(自UTC时间1970年01月01号开始的秒数) |
- 用户使用Post方法向OSS发送文件上传请求
总结
与数据上传至服务器,再由服务器上传到oss相比:(Vue+element Upload利用http-request实现第三方地址图片上传)
- 上传快:通过应用服务端上传,用户数据需先上传到应用服务器,之后再上传到OSS,网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
- 扩展性好:如果后续用户数量逐渐增加,则应用服务器会成为瓶颈。
- 费用低:由于OSS上行流量是免费的,如果数据直传到OSS,将节省多台应用服务器的费用。
更多推荐
已为社区贡献1条内容
所有评论(0)