web页面直传oss
客户端直传是指客户端直接上传文件到对象存储OSS。相对于服务端代理上传,客户端直传避免了业务服务器中转文件,提高了上传速度,节省了服务器资源。
为什么客户端直传
在典型的服务端和客户端架构下,常见的文件上传方式是服务端代理上传:客户端将文件上传到业务服务器,然后业务服务器将文件上传到OSS。在这个过程中,一份数据需要在网络上传输两次,会造成网络资源的浪费,增加服务端的资源开销。为了解决这一问题,您可以在客户端直连OSS来完成文件上传,无需经过业务服务器中转。

服务端签名直传并设置上传回调
本文主要介绍如何基于Post Policy的使用规则在服务端通过各种语言代码完成签名,并且设置上传回调,然后通过表单直传数据到OSS。
大多数情况下,用户上传文件后,应用服务器需要知道用户上传了哪些文件以及文件名;如果上传了图片,还需要知道图片的大小等,为此OSS提供了上传回调方案。
流程介绍
当用户要上传一个文件到OSS,而且希望将上传的结果返回给应用服务器时,需要设置一个回调函数,将请求告知应用服务器。用户上传完文件后,不会直接得到返回结果,而是先通知应用服务器,再把结果转达给用户。

修改CORS
客户端进行表单直传到OSS时,会从浏览器向OSS发送带有Origin的请求消息。OSS对带有Origin头的请求消息会进行跨域规则(CORS)的验证。因此需要为Bucket设置跨域规则以支持Post方法。
- 登录OSS管理控制台。
- 单击Bucket 列表,然后单击目标Bucket名称。
- 在左侧导航栏,选择数据安全 > 跨域设置。
- 单击创建规则,配置如下图所示。

获取上传token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public Object ossToken(@RequestParam(value = "type", required = false, defaultValue = "direct/") String type) { DefaultCredentialProvider credentialsProvider = CredentialsProviderFactory.newDefaultCredentialProvider("E2", "C6");
String endpoint = "oss-cn-hangzhou.aliyuncs.com"; String bucket = "shop"; String host = "https://shop.oss-cn-hangzhou.aliyuncs.com"; String callbackUrl = "https://.../platform-admin/sys/oss/callback"; String dir = type;
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); try { long expireTime = 60; 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 accessId = credentialsProvider.getCredentials().getAccessKeyId(); String encodedPolicy = BinaryUtil.toBase64String(binaryData); String postSignature = ossClient.calculatePostSignature(postPolicy);
Map<String, String> 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));
JSONObject jasonCallback = new JSONObject(); jasonCallback.put("callbackUrl", callbackUrl); jasonCallback.put("callbackBody", "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}"); jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded"); String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes()); respMap.put("callback", base64CallbackBody);
return RestResponse.ok(respMap); } catch (Exception e) { System.out.println(e.getMessage()); } finally { ossClient.shutdown(); }
return null; }
|
设置callback返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @PostMapping("/callback") public void callback(@RequestBody String body, HttpServletRequest request, HttpServletResponse response) throws IOException { log.info("callback:{}", body); String link = ""; String decode = URLDecoder.decode(body); System.out.println("decode = " + decode); String[] split = decode.split("&"); for (String string : split) { System.out.println("key = " + string.split("=")[0] + " \tvalue = " + string.split("=")[1]); if (string.split("=")[0].equals("filename")) { link = string.split("=")[1]; } break; } Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); String headerValue = request.getHeader(headerName); log.info("headerName:{},headerValue:{}", headerName, headerValue); }
if (StringUtils.isNotBlank(link)) { SysOssEntity ossEntity = new SysOssEntity(); ossEntity.setUrl("https://...hangzhou.aliyuncs.com/"+link); ossEntity.setCreateDate(new Date()); ossEntity.setCreateUserId("11"); ossEntity.setCreateUserOrgNo("01"); sysOssService.save(ossEntity); }
JSONObject respMap = new JSONObject(); respMap.put("success", StringUtils.isNotBlank(link)); respMap.put("code", StringUtils.isNotBlank(link)?0:1); respMap.put("msg", StringUtils.isNotBlank(link)?"操作成功":"操作失败"); Map<String, String> data = new HashMap<String, String>(); data.put("OssUrl", link); respMap.put("data", data); response.getOutputStream().write(respMap.toJSONString().getBytes(StandardCharsets.UTF_8)); response.getOutputStream().flush(); }
|
Vue上传组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| <template> <span> <el-upload class="avatar-uploader el-upload-text" :action='url' :show-file-list="false" :data="ossData" :on-success="handleVideoSuccess1" :before-upload="beforeUploadVideo1" :on-progress="uploadVideoProcess" :on-error="uploadFail" > <el-button style="margin: 20px 0" type="primary"><i class="el-icon-plus avatar-uploader-icon"></i>{{ buttonLable }} </el-button> </el-upload> <el-progress v-if="uploadFlag && videoUploadPercent > 0 " </span> </template> <script> export default { props: { buttonLable: { type: String, default: '上传', required: true }, uploadType: { type: String, default: 'direct/' } }, data () { return { url: 'https://....oss-cn-hangzhou.aliyuncs.com', videoUploadPercent: 0, uploadFlag: false, ossData: { policy: '', signature: '', key: '', OSSAccessKeyId: '', dir: '', host: '', callback: '' } } }, methods: { getOssToken (val, file) { return new Promise(async (resolve, reject) => { this.$http({ url: `/sys/oss/ossToken`, method: 'get', params: { 'type': val } }).then(({data}) => { let newStr = file.name.replace(/\s/g, '') this.ossData.OSSAccessKeyId = data.data.accessid this.ossData.callback = data.data.callback this.ossData.dir = data.data.dir this.ossData.policy = data.data.policy this.ossData.host = data.data.host this.ossData.signature = data.data.signature this.ossData.expire = data.data.expire this.ossData.key = 'direct/' + this.getUUID() + newStr resolve(true) }).catch(err => { console.log(err) // eslint-disable-next-line prefer-promise-reject-errors reject(false) }) }) },
getUUID () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16) }) }, async beforeUploadVideo1 (file) { // 同步等待 await this.getOssToken('direct/', file) }, uploadVideoProcess (event, file, fileList) { this.uploadFlag = true this.videoFlag = true this.videoUploadPercent = parseInt(file.percentage.toFixed(0)) }, handleVideoSuccess1 ({data}, file) { this.videoUploadPercent = 100 this.uploadFlag = false console.log(data, 'uploadVideoProcess') this.$emit('getOssUrl', this.url + '/' + data.OssUrl) this.$message({type: 'success', message: '上传成功'}) }, uploadFail () { this.uploadFlag = false this.videoUploadPercent = 0 } } } </script>
|
如何通过Java在服务端签名直传并设置上传回调_对象存储(OSS)-阿里云帮助中心
使用阿里云OSS的服务端签名后直传功能 - Johnson木木 - 博客园