1.背景
采用服务端签名后直传方案有个问题:大多数情况下,用户上传数据后,应用服务器需要知道用户上传了哪些文件以及文件名;如果上传了图片,还需要知道图片的大小等。为此OSS提供了上传回调方案。OSS回调完成后,应用服务器再将结果返回给客户端,以便服务端实时了解用户上传了什么文件。
2.流程介绍
流程如下:
1.用户向应用服务器请求上传Policy和回调。
2.应用服务器返回上传Policy和回调设置。
3.用户直接向OSS发送文件上传请求。
4.OSS根据用户的回调设置,发送回调请求给应用服务器。
5.应用服务器返回响应给OSS。
6.OSS将应用服务器返回的内容返回给用户。
当用户要上传一个文件到OSS,而且希望将上传的结果返回给应用服务器,这时就需要设置一个回调函数,将请求告知应用服务器。用户上传完文件后,不会直接得到返回结果,而是先通知应用服务器,再把结果转达给用户。
官方文档地址:服务端签名直传并设置上传回调
3.编码
这里我们只需要在上一篇文章中的代码进行更改就好
阿里oss服务端签名后直传
修改SpringBoot配置文件,添加如下:
# 需要外网地址
callback: http://tuanzi.natapp1.cc/aliyun/oss/callback
这里必须是外网能访问到的。推荐使用内网穿透!
OssPolicyResult类添加如下:
@ApiModelProperty("上传成功后的回调设置")
private String callback;
添加OSS上传成功后的回调参数对象OssCallbackParam
当OSS上传成功后,会根据该配置参数来回调对应接口。
package com.tuanzi.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* oss上传成功后的回调参数
* @author 团子
* @date 2019-07-31 11:31
*/
@Data
public class OssCallbackParam {
@ApiModelProperty("请求的回调地址")
private String callbackUrl;
@ApiModelProperty("回调是传入request中的参数")
private String callbackBody;
@ApiModelProperty("回调时传入参数的格式,比如表单提交形式")
private String callbackBodyType;
}
OSS上传成功后的回调结果对象OssCallbackResult
回调接口中返回的数据对象,封装了上传文件的信息。
package com.tuanzi.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* oss上传文件的回调结果
* @author 团子
* @date 2019-07-31 11:31
*/
@Data
public class OssCallbackResult {
@ApiModelProperty("文件名称")
private String filename;
@ApiModelProperty("文件大小")
private String size;
@ApiModelProperty("文件的mimeType")
private String mimeType;
@ApiModelProperty("图片文件的宽")
private String width;
@ApiModelProperty("图片文件的高")
private String height;
}
添加OSS业务接口OssService
/**
* oss上传成功回调
*/
OssCallbackResult callback(HttpServletRequest request);
添加OSS业务接口OssService的实现类OssServiceImpl
/**
* 上传成功回调
*/
@Override
public OssCallbackResult callback(HttpServletRequest request) {
OssCallbackResult result= new OssCallbackResult();
String filename = request.getParameter("filename");
filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename);
result.setFilename(filename);
result.setSize(request.getParameter("size"));
result.setMimeType(request.getParameter("mimeType"));
result.setWidth(request.getParameter("width"));
result.setHeight(request.getParameter("height"));
return result;
}
原代码中添加如下:
@Value("${aliyun.oss.callback}")
private String ALIYUN_OSS_CALLBACK;
/**
* 签名生成
*/
@Override
public OssPolicyResult policy() {
OssPolicyResult result = new OssPolicyResult();
// 存储目录
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dir = ALIYUN_OSS_DIR_PREFIX+sdf.format(new Date());
// 签名有效期
long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
Date expiration = new Date(expireEndTime);
// 文件大小
long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;
// 回调
OssCallbackParam callback = new OssCallbackParam();
callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
callback.setCallbackBodyType("application/x-www-form-urlencoded");
// 提交节点
String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
try {
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String policy = BinaryUtil.toBase64String(binaryData);
String signature = ossClient.calculatePostSignature(postPolicy);
String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8"));
// 返回结果
result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
result.setPolicy(policy);
result.setSignature(signature);
result.setDir(dir);
result.setCallback(callbackData);
result.setHost(action);
} catch (Exception e) {
LOGGER.error("签名生成失败", e);
}
return result;
}
添加OssController定义接口
@ApiOperation(value = "oss上传成功回调")
@RequestMapping(value = "/callback", method = RequestMethod.POST)
@ResponseBody
public CommonResult<OssCallbackResult> callback(HttpServletRequest request) {
logger.info("callback --------------> start");
OssCallbackResult ossCallbackResult = ossService.callback(request);
logger.info("callback --------------> end");
return CommonResult.success(ossCallbackResult);
}
好了这样就完成了
我们还是一样用postman进行测试
还是会调用两次请求,第一次访问本地接口获取上传的策略
我们可以看到返回参数多了callback
第二次调用oss服务 的接口进行文件上传
把callback加入进去
上传成功返回图片的信息
我们再来看看oss管理控制台
源码地址
Q.E.D.