OSS实现文件上传

一、文件存储方式

OSS实现文件上传_第1张图片
OSS实现文件上传_第2张图片

二、OSS

阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。OSS可用于图片、音视频、日志等海量文件的存储。各种终端设备、Web网站程序、移动应用可以直接向OSS写入或读取数据。
https://www.aliyun.com/

OSS中相关概念

  • Endpoint:访问域名,通过该域名可以访问OSS服务的API,进行文件上传、下载等操作。
  • Bucket:存储空间,是存储对象的容器,所有存储对象都必须隶属于某个存储空间。
  • Object:对象,对象是 OSS 存储数据的基本单元,也被称为 OSS 的文件。
  • AccessKey:访问密钥,指的是访问身份验证中用到的 AccessKeyId 和 AccessKeySecret。

OSS相关设置

  1. 开通OSS服务
  • 登录阿里云官网;
  • 将鼠标移至产品标签页,单击对象存储 OSS,打开OSS 产品详情页面;
  • 在OSS产品详情页,单击立即开通。
  1. 创建存储空间
  • 点击网页右上角控制台按钮进入控制台
    在这里插入图片描述
  • 选择我的云产品中的对象存储OSS
    在这里插入图片描述
  • 点击左侧存储空间的加号新建存储空间
    OSS实现文件上传_第3张图片
  • 新建存储空间并设置读写权限为公共读
    OSS实现文件上传_第4张图片

跨域资源共享(CORS)的设置

由于浏览器处于安全考虑,不允许跨域资源访问,所以我们要设置OSS的跨域资源共享。

  • 选择一个存储空间,打开其基础设置
    OSS实现文件上传_第5张图片
  • 点击跨越设置的设置按钮
    OSS实现文件上传_第6张图片
  • 点击创建规则
    在这里插入图片描述
  • 进行跨域规则设置
    OSS实现文件上传_第7张图片

服务器直传

每个OSS的用户都会用到上传服务。Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS
具体流程如下图所示。
OSS实现文件上传_第8张图片
和数据直传到OSS相比,以上方法有三个缺点:

  • 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
  • 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。
  • 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。

服务端签名后前端直传的相关说明

OSS实现文件上传_第9张图片
Web端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。但本示例中的服务端无法实时了解用户上传了多少文件,上传了什么文件。如果想实时了解用户上传了什么文件,可以采用服务端签名直传并设置上传回调。
OSS实现文件上传_第10张图片
流程介绍:

  1. Web前端请求应用服务器,获取上传所需参数(如OSS的accessKeyId、policy、callback等参数)
  2. 应用服务器返回相关参数
  3. Web前端直接向OSS服务发起上传文件请求
  4. 等上传完成后OSS服务会回调应用服务器的回调接口(不实现)
  5. 应用服务器返回响应给OSS服务(不实现)
  6. OSS服务将应用服务器回调接口的内容返回给Web前端

整合OSS实现文件上传

1.在pom.xml中添加相关依赖

1 <!‐‐ OSS SDK 相关依赖 ‐‐>
2 <dependency>
3 <groupId>com.aliyun.ossgroupId>
4 <artifactId>aliyun‐sdk‐ossartifactId>
5 <version>2.5.0version>
6 dependency>

2.修改SpringBoot配置文件

修改application.yml文件,添加OSS相关配置。
注意:endpoint、accessKeyId、accessKeySecret、bucketName、callback、prefix都要改为你自己帐号OSS相关的,callback需要是公网可以访问的地址。

1 # OSS相关配置信息
2 aliyun:
3  oss:
4   endpoint: oss‐cn‐shenzhen.aliyuncs.com # oss对外服务的访问域名
5   accessKeyId: test # 访问身份验证中用到用户标识
6   accessKeySecret: test # 用户用于加密签名字符串和oss用来验证签名字符串的密钥
7   bucketName: macro‐oss # oss的存储空间
8  policy:
9   expire: 300 # 签名有效期(S)
10  maxSize: 10 # 上传文件大小(M)
11  callback: http://localhost:8080/aliyun/oss/callback # 文件上传成功后的回调地址
12  dir:
13   prefix: mall/images/ # 上传文件夹路径前缀

3.添加OSS的相关Java配置

用于配置OSS的连接客户端OSSClient。

1 package com.macro.mall.tiny.config;
2
3 import com.aliyun.oss.OSSClient;
4 import org.springframework.beans.factory.annotation.Value;
5 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration;
7
8 /**
9 * Created by macro on 2018/5/17.
10 */
11 @Configuration
12 public class OssConfig {
13 @Value("${aliyun.oss.endpoint}")
14 private String ALIYUN_OSS_ENDPOINT;
15 @Value("${aliyun.oss.accessKeyId}")
16 private String ALIYUN_OSS_ACCESSKEYID;
17 @Value("${aliyun.oss.accessKeySecret}")
18 private String ALIYUN_OSS_ACCESSKEYSECRET;
19 @Bean
20 public OSSClient ossClient(){
21 return new OSSClient(ALIYUN_OSS_ENDPOINT,ALIYUN_OSS_ACCESSKEYID,ALIYUN_OSS_ACCESSKEYSECRET);
22 }
23 }

4.添加OSS上传策略封装对象OssPolicyResult

前端直接上传文件时所需参数,从后端返回过来。

1 package com.macro.mall.tiny.dto;
2
3 import io.swagger.annotations.ApiModelProperty;
4
5 /**
6 * 获取OSS上传文件授权返回结果
7 * Created by macro on 2018/5/17.
8 */
9 public class OssPolicyResult {
10 @ApiModelProperty("访问身份验证中用到用户标识")
11 private String accessKeyId;
12 @ApiModelProperty("用户表单上传的策略,经过base64编码过的字符串")
13 private String policy;
14 @ApiModelProperty("对policy签名后的字符串")
15 private String signature;
16 @ApiModelProperty("上传文件夹路径前缀")
17 private String dir;
18 @ApiModelProperty("oss对外服务的访问域名")
19 private String host;
20 @ApiModelProperty("上传成功后的回调设置")
21 private String callback;
22
23 //省略了所有getter,setter方法
24 }

5.添加OSS上传成功后的回调参数对象OssCallbackParam

当OSS上传成功后,会根据该配置参数来回调对应接口。

1 package com.macro.mall.tiny.dto;
2
3 import io.swagger.annotations.ApiModelProperty;
4
5 /**
6 * oss上传成功后的回调参数
7 * Created by macro on 2018/5/17.
8 */
9 public class OssCallbackParam {
10 @ApiModelProperty("请求的回调地址")
11 private String callbackUrl;
12 @ApiModelProperty("回调是传入request中的参数")
13 private String callbackBody;
14 @ApiModelProperty("回调时传入参数的格式,比如表单提交形式")
15 private String callbackBodyType;
16
17 //省略了所有getter,setter方法
18 }

6.OSS上传成功后的回调结果对象OssCallbackResult

1 package com.macro.mall.tiny.dto;
2
3 import io.swagger.annotations.ApiModelProperty;
4
5 /**
6 * oss上传文件的回调结果
7 * Created by macro on 2018/5/17.
8 */
9 public class OssCallbackResult {
10 @ApiModelProperty("文件名称")
11 private String filename;
12 @ApiModelProperty("文件大小")
13 private String size;
14 @ApiModelProperty("文件的mimeType")
15 private String mimeType;
16 @ApiModelProperty("图片文件的宽")
17 private String width;
18 @ApiModelProperty("图片文件的高")
19 private String height;
20
21 //省略了所有getter,setter方法
22 }

7.添加OSS业务接口OssService

1 package com.macro.mall.tiny.service;
2
3 import com.macro.mall.tiny.dto.OssCallbackResult;
4 import com.macro.mall.tiny.dto.OssPolicyResult;
5
6 import javax.servlet.http.HttpServletRequest;
7
8 /**
9 * oss上传管理Service
10 * Created by macro on 2018/5/17.
11 */
12 public interface OssService {
13 /**
14 * oss上传策略生成
15 */
16 OssPolicyResult policy();
17
18 /**
19 * oss上传成功回调
20 */
21 OssCallbackResult callback(HttpServletRequest request);
22 }

8.添加OSS业务接口OssService的实现类0

1 package com.macro.mall.tiny.service.impl;
2
3 import cn.hutool.json.JSONUtil;
4 import com.aliyun.oss.OSSClient;
5 import com.aliyun.oss.common.utils.BinaryUtil;
6 import com.aliyun.oss.model.MatchMode;
7 import com.aliyun.oss.model.PolicyConditions;
8 import com.macro.mall.tiny.dto.OssCallbackParam;
9 import com.macro.mall.tiny.dto.OssCallbackResult;
10 import com.macro.mall.tiny.dto.OssPolicyResult;
11 import com.macro.mall.tiny.service.OssService;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14 import org.springframework.beans.factory.annotation.Autowired;
15 import org.springframework.beans.factory.annotation.Value;
16 import org.springframework.stereotype.Service;
17
18 import javax.servlet.http.HttpServletRequest;
19 import java.text.SimpleDateFormat;
20 import java.util.Date;
21
22 /**
23 * oss上传管理Service实现类
24 * Created by macro on 2018/5/17.
25 */
26 @Service
27 public class OssServiceImpl implements OssService {
28
29 private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class);
30 @Value("${aliyun.oss.policy.expire}")
31 private int ALIYUN_OSS_EXPIRE;
32 @Value("${aliyun.oss.maxSize}")
33 private int ALIYUN_OSS_MAX_SIZE;
34 @Value("${aliyun.oss.callback}")
35 private String ALIYUN_OSS_CALLBACK;
36 @Value("${aliyun.oss.bucketName}")
37 private String ALIYUN_OSS_BUCKET_NAME;
38 @Value("${aliyun.oss.endpoint}")
39 private String ALIYUN_OSS_ENDPOINT;
40 @Value("${aliyun.oss.dir.prefix}")
41 private String ALIYUN_OSS_DIR_PREFIX;
42
43 @Autowired
44 private OSSClient ossClient;
45
46 /**
47 * 签名生成
48 */
49 @Override
50 public OssPolicyResult policy() {
51 OssPolicyResult result = new OssPolicyResult();
52 // 存储目录
53 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
54 String dir = ALIYUN_OSS_DIR_PREFIX+sdf.format(new Date());
55 // 签名有效期
56 long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
57 Date expiration = new Date(expireEndTime);
58 // 文件大小
59 long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;
60 // 回调
61 OssCallbackParam callback = new OssCallbackParam();
62 callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
63 callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=
${imageInfo.width}");
64 callback.setCallbackBodyType("application/x‐www‐form‐urlencoded");
65 // 提交节点
66 String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
67 try {
68 PolicyConditions policyConds = new PolicyConditions();
69 policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
70 policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
71 String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
72 byte[] binaryData = postPolicy.getBytes("utf‐8");
73 String policy = BinaryUtil.toBase64String(binaryData);
74 String signature = ossClient.calculatePostSignature(postPolicy);
75 String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf‐8"));
76 // 返回结果
77 result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
78 result.setPolicy(policy);
79 result.setSignature(signature);
80 result.setDir(dir);
81 result.setCallback(callbackData);
82 result.setHost(action);
83 } catch (Exception e) {
84 LOGGER.error("签名生成失败", e);
85 }
86 return result;
87 }
88
89 @Override
90 public OssCallbackResult callback(HttpServletRequest request) {
91 OssCallbackResult result= new OssCallbackResult();
92 String filename = request.getParameter("filename");
93 filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(fil
ename);
94 result.setFilename(filename);
95 result.setSize(request.getParameter("size"));
96 result.setMimeType(request.getParameter("mimeType"));
97 result.setWidth(request.getParameter("width"));
98 result.setHeight(request.getParameter("height"));
99 return result;
100 }
101
102 }

9.添加OssController定义接口

1 package com.macro.mall.tiny.controller;
2
3
4 import com.macro.mall.tiny.common.api.CommonResult;
5 import com.macro.mall.tiny.dto.OssCallbackResult;
6 import com.macro.mall.tiny.dto.OssPolicyResult;
7 import com.macro.mall.tiny.service.impl.OssServiceImpl;
8 import io.swagger.annotations.Api;
9 import io.swagger.annotations.ApiOperation;
10 import org.springframework.beans.factory.annotation.Autowired;
11 import org.springframework.stereotype.Controller;
12 import org.springframework.web.bind.annotation.RequestMapping;
13 import org.springframework.web.bind.annotation.RequestMethod;
14 import org.springframework.web.bind.annotation.ResponseBody;
15
16 import javax.servlet.http.HttpServletRequest;
17
18 /**
19 * Oss相关操作接口
20 * Created by macro on 2018/4/26.
21 */
22 @Controller
23 @Api(tags = "OssController", description = "Oss管理")
24 @RequestMapping("/aliyun/oss")
25 public class OssController {
26 @Autowired
27 private OssServiceImpl ossService;
28
29 @ApiOperation(value = "oss上传签名生成")
30 @RequestMapping(value = "/policy", method = RequestMethod.GET)
31 @ResponseBody
32 public CommonResult<OssPolicyResult> policy() {
33 OssPolicyResult result = ossService.policy();
34 return CommonResult.success(result);
35 }
36
37 @ApiOperation(value = "oss上传成功回调")
38 @RequestMapping(value = "callback", method = RequestMethod.POST)
39 @ResponseBody
40 public CommonResult<OssCallbackResult> callback(HttpServletRequest request) {
41 OssCallbackResult ossCallbackResult = ossService.callback(request);
42 return CommonResult.success(ossCallbackResult);
43 }
44
45 }

10.进行接口测试

测试获取上传策略的接口
OSS实现文件上传_第11张图片
OSS实现文件上传_第12张图片

11.启动mall­admin­web前端项目来测试上传接口

  • 点击添加商品品牌的上传按钮进行测试
    OSS实现文件上传_第13张图片
  • 会调用两次请求,第一次访问本地接口获取上传的策略
    OSS实现文件上传_第14张图片
    OSS实现文件上传_第15张图片
  • 第二次调用oss服务 的接口进行文件上传
    OSS实现文件上传_第16张图片
  • 可以看到上面接口调用并没有传入回调参数callback,所以接口返回了204 no content,这次我们传入回调参数callback试试,可以发现oss服务回调了我们自己定义的回调接口,并返回了相应结果。

OSS实现文件上传_第17张图片
OSS实现文件上传_第18张图片

参考资料

参考资料
开通OSS服务:https://help.aliyun.com/document_detail/31884.html?spm=a2c4g.11186623.6.566.74b87eaebrfQno
创建存储空间:https://help.aliyun.com/document_detail/31885.html?spm=a2c4g.11186623.6.567.496228bcVZUZqB
跨域资源共享(CORS):https://help.aliyun.com/document_detail/31928.html?
spm=5176.11065259.1996646101.searchclickresult.4d1a5607Pf3e9i

服务端签名直传并设置上传回调:https://help.aliyun.com/document_detail/31927.html?
spm=a2c4g.11186623.6.1268.2c256506mNqV1t

你可能感兴趣的:(java,经验分享,策略模式)