提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录- 前言
- 一、OSS签名直传是什么?
- 1.概念
- 2.优点
- 二、后端代码
- 1.请求OSS签名
- 三.前端代码
- 碰到的问题如下:
- 1.前端采用ImageCropper组件直接上传的时候,不知道将OSS所需参数加到何处?
- 2.当参数问题解决后,发现上传失败,具体报错为
- 3.文件可以顺利上传到OSS中,但是vue-admin-template却提示上传失败
- 总结
前言
练手的项目中使用到了OSS的签名直传,将该过程进行记录,以便后续使用。前端采用的是vue-admin-template框架的上传组件,后端采用java完成
一、OSS签名直传是什么? 1.概念
Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS。这种方式需通过应用服务器中转,传输效率明显低于数据直传至OSS的方式。数据直传至OSS是利用OSS的PostObject接口,使用表单上传方式上传文件至OSS
流程解析:
- 前端向后端请求OSS签名
- 前端得到签名之后,直接上传文件到OSS中
- 上传文件成功之后采用回调机制保存图片地址到数据库中
2.1 签名直传无需将Accesskey暴露在前端页面,相比Javascript客户端签名直传具有更高的安全性
2.2 签名直传无需将资源传到应用服务器再转发到OSS中,而是直接上传至OSS中,节省了带宽
后端的代码较为简单,只需返回直传所需的一些验证参数即可,具体参数可参照官方文档:阿里云OSS直传.
Controller层:
@Api("oss管理接口") @RestController @RequestMapping("/ossService") public class OssController { private final OssService ossService; public OssController(OssService ossService) { this.ossService = ossService; } @ApiOperation(value = "获取oss签名") @GetMapping("/getOssSign") public R getOssSign(){ MapsignMap = ossService.getOssSign(); if(signMap == null || signMap.size() == 0){ return R.error().msg("未获取到签名,请重试"); } return R.ok().data(signMap); } }
Service层:
@Service public class OssServiceImpl implements OssService { @Override public Map三.前端代码getOssSign() { String accessId = ConstantPropertiesUtils.KEY_ID; // 请填写您的AccessKeyId。 String accessKey = ConstantPropertiesUtils.KEY_SECRET; // 请填写您的AccessKeySecret。 String endpoint = ConstantPropertiesUtils.END_POINT; // 请填写您的 endpoint。 String bucket = ConstantPropertiesUtils.BUCKET; // 请填写您的 bucketname 。 String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint String dir = new DateTime().toString("yyyy/MM/dd"); // 用户上传文件时指定的前缀。 Map 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); // PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。 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<>(); //这些参数名必须要这样写,与官方文档一一对应 respMap.put("OSSAccessKeyId", 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()); } finally { ossClient.shutdown(); } return respMap; } }
前端使用的是vue-admin-template框架,其中文件上传用的是ImageCropper和Hamburger组件,由于对前端代码不熟,因此碰到了一些问题(有坑,需要自己改部分源码)
前端html代码:
更换头像
其中有2个主要的函数,其作用分别为:
- beforeUpload:用于上传OSS前到后端获取签名
- uploadSuccess:上传OSS成功后拼接url
前端js代码
//获取oss的token和签名 beforeUpload() { this.imagecropperShow = true; //获取后台签名 oss.getOssSign().then((response) => { console.log(response); this.ossObj = response.data; this.ossObj.key = response.data.dir + "/" + this.randomString(10); console.log(this.ossObj.key); }); }, //上传成功的回调 uploadSuccess(data) { this.imagecropperShow = false; //上传之后拼接url,这样后续提交表单数据时便可保存url到数据库中 let url = this.ossObj.host + "/" + this.ossObj.key; this.teacher.avatar = url; this.imagecropperKey = this.imagecropperKey + 1; }, //用于随机生成文件名 randomString(len) { len = len || 32; var chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"; var maxPos = chars.length; var pwd = ""; for (var i = 0; i < len; i++) { pwd += chars.charAt(Math.floor(Math.random() * maxPos)); } return pwd; },碰到的问题如下: 1.前端采用ImageCropper组件直接上传的时候,不知道将OSS所需参数加到何处?
解决:查看ImageCropper的源码发现,所有的其余参数都应该加到:parms中,这样会自动上传
The bucket POST must contain the specified ‘key‘. If it is specified, please check the order
报错原因:阿里云OSS规定文件上传时,必须携带key参数,且key参数必须在上传的file之前,如果key在file后或者缺少key参数,便会报错。而ImageCropper源码在填充参数时是先将file放在第一位,后面的参数依次遍历添加,因此出现了key在file后面的情况,导致报错
// 807行 fmData.append( field, data2blob(createImgUrl, mime), field + "." + imgFormat ); //添加其他参数 if (typeof params === "object" && params) { Object.keys(params).forEach((k) => { fmData.append(k, params[k]); }); }
解决:将代码顺序进行修改,这样key就会在file之前,报错解决
if (typeof params === "object" && params) { Object.keys(params).forEach((k) => { fmData.append(k, params[k]); }); } fmData.append( field, data2blob(createImgUrl, mime), field + "." + imgFormat );3.文件可以顺利上传到OSS中,但是vue-admin-template却提示上传失败
报错原因:vue-admin-template封装了axios请求,加入了拦截器,因此它对于是否上传成功的判断是,如果后端返回的data.code===20000则成功,否则失败。但是OSS是外部服务器,不会出现20000的响应,因此应当根据响应状态码进行判断
解决:在封装axios请求的js代码中新增判断条件(修改response拦截器即可),不仅code为20000时可以成功,并且响应码为204时也可以成功
utils/request.js:
import axios from 'axios' import { Message, MessageBox } from 'element-ui' import store from '../store' import { getToken } from '@/utils/auth' // 创建axios实例 const service = axios.create({ baseURL: process.env.base_API, // api 的 base_url timeout: 5000 // 请求超时时间 }) // request拦截器 service.interceptors.request.use( config => { if (store.getters.token) { config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 } return config }, error => { // Do something with request error console.log(error) // for debug Promise.reject(error) } ) // response 拦截器 service.interceptors.response.use( response => { const res = response.data if (response.status === 204 || res.code === 20000) { return response.data } else { Message({ message: res.message, type: 'error', duration: 5 * 1000 }) // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; if (res.code === 50008 || res.code === 50012 || res.code === 50014) { MessageBox./confirm/i( '你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { /confirm/iButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' } ).then(() => { store.dispatch('FedLogOut').then(() => { location.reload() // 为了重新实例化vue-router对象 避免bug }) }) } return Promise.reject('error') } }, error => { console.log('err' + error) // for debug Message({ message: error.message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } ) export default service总结
阿里云OSS签名直传是如今比较流行的文件上传方式,本文介绍了签名直传的实现,后端代码实现较为简单,主要出的坑都在前端(主要是前端框架的使用问题),应当加强前端代码能力
TO DO
签名直传还有回调功能,该回调功能应当写在后端代码中,但实现起来与签名直传并无太大区别,加上回调函数即可,具体可参照服务端签名直传并设置上传回调
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)