本文主要讲解整合 OSS 实现文件上传的过程,采用的是服务端签名后前端直传的方式。阿里云对象存储服务(Object Storage Service,简称OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。OSS 可用于图片、音视频、日志等海量文件的存储。各种终端设备、Web 网站程序、移动应用可以直接向 OSS 写入或读取数据。
OSS 简介
阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。OSS 可用于图片、音视频、日志等海量文件的存储。各种终端设备、Web网站程序、移动应用可以直接向 OSS 写入或读取数据。
OSS中的相关概念
Endpoint
:访问域名,通过该域名可以访问OSS服务的API,进行文件上传、下载等操作。
Bucket
:存储空间,是存储对象的容器,所有存储对象都必须隶属于某个存储空间。
Object
:对象,对象是 OSS 存储数据的基本单元,也被称为 OSS 的文件。
AccessKey
:访问密钥,指的是访问身份验证中用到的 AccessKeyId 和 AccessKeySecret。
OSS的相关设置
#### 开通 OSS 服务
- 登录阿里云官网;
- 将鼠标移至产品标签页,单击对象存储 OSS,打开 OSS 产品详情页面;
- 在 OSS 产品详情页,单击立即开通。
创建存储空间
点击网页右上角控制台按钮进入控制台:

选择我的云产品中的对象存储 OSS:

点击左侧存储空间的加号新建存储空间:

新建存储空间并设置读写权限为公共读:

跨域资源共享(CORS)的设置
由于浏览器处于安全考虑,不允许跨域资源访问,所以我们要设置 OSS 的跨域资源共享。
选择一个存储空间,打开其基础设置:

点击跨越设置的设置按钮:

点击创建规则:

进行跨域规则设置:

服务端签名后前端直传的相关说明
流程示例图

流程介绍
- Web前端请求应用服务器,获取上传所需参数(如 OSS 的 accessKeyId、policy、callback 等参数)
- 应用服务器返回相关参数
- Web前端直接向 OSS 服务发起上传文件请求
- 等上传完成后 OSS 服务会回调应用服务器的回调接口
- 应用服务器返回响应给 OSS 服务
- OSS 服务将应用服务器回调接口的内容返回给 Web 前端
整合 OSS 实现文件上传
#### 引入依赖
在 pom.xml 中添加相关依赖:
1 2 3 4 5 6
| <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>2.5.0</version> </dependency>
|
相关配置
修改 application.yml 文件,添加 OSS 相关配置。
注意:endpoint、accessKeyId、accessKeySecret、bucketName、callback、prefix 都要改为你自己帐号 OSS 相关的,callback 需要是公网可以访问的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13
| aliyun: oss: endpoint: oss-cn-shenzhen.aliyuncs.com accessKeyId: test accessKeySecret: test bucketName: macro-oss policy: expire: 300 maxSize: 10 callback: http://localhost:8080/aliyun/oss/callback dir: prefix: mall/images/
|
创建 OSS 的相关 Java 配置,用于配置 OSS 的连接客户端 OSSClient:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration public class OssConfig { @Value("${aliyun.oss.endpoint}") private String ALIYUN_OSS_ENDPOINT; @Value("${aliyun.oss.accessKeyId}") private String ALIYUN_OSS_ACCESSKEYID; @Value("${aliyun.oss.accessKeySecret}") private String ALIYUN_OSS_ACCESSKEYSECRET; @Bean public OSSClient ossClient(){ return new OSSClient(ALIYUN_OSS_ENDPOINT,ALIYUN_OSS_ACCESSKEYID,ALIYUN_OSS_ACCESSKEYSECRET); } }
|
创建 OSS 上传策略封装对象 OssPolicyResult,前端直接上传文件时所需参数,从后端返回过来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class OssPolicyResult { @ApiModelProperty("访问身份验证中用到用户标识") private String accessKeyId; @ApiModelProperty("用户表单上传的策略,经过base64编码过的字符串") private String policy; @ApiModelProperty("对policy签名后的字符串") private String signature; @ApiModelProperty("上传文件夹路径前缀") private String dir; @ApiModelProperty("oss对外服务的访问域名") private String host; @ApiModelProperty("上传成功后的回调设置") private String callback;
}
|
创建 OSS 上传成功后的回调参数对象 OssCallbackParam,当 OSS 上传成功后,会根据该配置参数来回调对应接口。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
public class OssCallbackParam { @ApiModelProperty("请求的回调地址") private String callbackUrl; @ApiModelProperty("回调是传入request中的参数") private String callbackBody; @ApiModelProperty("回调时传入参数的格式,比如表单提交形式") private String callbackBodyType;
}
|
创建 OSS 回调结果对象
创建 OssCallbackResult 类封装了上传文件的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
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 接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
public interface OssService {
OssPolicyResult policy();
OssCallbackResult callback(HttpServletRequest request); }
|
创建 OSS 业务接口实现类
创建 OssServiceImpl 接口实现类:
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
|
@Service public class OssServiceImpl implements OssService {
private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class); @Value("${aliyun.oss.policy.expire}") private int ALIYUN_OSS_EXPIRE; @Value("${aliyun.oss.maxSize}") private int ALIYUN_OSS_MAX_SIZE; @Value("${aliyun.oss.callback}") private String ALIYUN_OSS_CALLBACK; @Value("${aliyun.oss.bucketName}") private String ALIYUN_OSS_BUCKET_NAME; @Value("${aliyun.oss.endpoint}") private String ALIYUN_OSS_ENDPOINT; @Value("${aliyun.oss.dir.prefix}") private String ALIYUN_OSS_DIR_PREFIX;
@Autowired private OSSClient ossClient;
@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; }
@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; }
}
|
创建 OSS 控制器
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
|
@Controller @Api(tags = "OssController", description = "OSS 管理") @RequestMapping("/aliyun/oss") public class OssController { @Autowired private OssServiceImpl ossService;
@ApiOperation(value = "OSS 上传签名生成") @RequestMapping(value = "/policy", method = RequestMethod.GET) @ResponseBody public CommonResult<OssPolicyResult> policy() { OssPolicyResult result = ossService.policy(); return CommonResult.success(result); }
@ApiOperation(value = "OSS 上传成功回调") @RequestMapping(value = "callback", method = RequestMethod.POST) @ResponseBody public CommonResult<OssCallbackResult> callback(HttpServletRequest request) { OssCallbackResult ossCallbackResult = ossService.callback(request); return CommonResult.success(ossCallbackResult); }
}
|
进行接口测试



上传会调用两次请求,第一次访问本地接口获取上传的策略:


第二次调用 OSS 服务的接口进行文件上传:


可以看到上面接口调用并没有传入回调参数 callback,所以接口返回了 204 no content,这次我们传入回调参数 callback 试试,可以发现 OSS 服务回调了我们自己定义的回调接口,并返回了相应结果:



更多干货请移步:https://antoniopeng.com
如果你喜欢这个博客或发现它对你有用,欢迎你点击右下角 “OPEN CHAT” 进行评论。也欢迎你分享这个博客,让更多的人参与进来。如果在博客中的内容侵犯了您的版权,请联系博主删除它们。谢谢你!