阿里云对象存储**OSS(Object Storage Service)**是一款海量、安全、低成本、高可靠的云存储服务,这也是我们开发过程中较为常用的一个服务。Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS。
而在这里则是基于 Post Policy(用户表单上传的策略) 的使用规则在服务端完成签名,然后通过表单直传数据到OSS。由于服务端签名直传无需将AccessKey暴露在前端页面,相比JavaScript客户端签名直传具有更高的安全性。
参考文档:
另外一种接收文件集合上传返回图片链接的上传方式:【OSS】SpringBoot搭配线程池整合阿里云OSS实现图片异步上传
进入到阿里云控制台,打开左上角的菜单选中对象存储OSS(找不到的可以在产品与服务中搜索)
在开通OSS对象存储服务之后,点击新建Bucket,填写自己的Bucket Name和地狱,优先选择靠近自己所在地的,完成设置之后确认即可记得这里保存后弹出来的界面的概览的访问域名的外网访问的Endpoint(地域节点)。
下面是我个人的一个选项参数供大家参考(个人用途为项目存储图片):
创建子账户获取AccessKey ID
和AccessKey Secret
。这一操作在鼠标停顿在右上角头像 -> AccessKey管理(前提是进入控制台之后头像才会有这一菜单)。
在服务端,主要就是对OSS进行一个配置连接,利用阿里云提供的API生成需要的签名。针对这一需求,我们需要导入阿里云OSS云存储依赖,注意的是这里已忽略Spring web、Lombok等依赖。
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alicloud-ossartifactId>
<version>2.2.0.RELEASEversion>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.6.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
为了数据的统一管理,将服务所需数据统一填写在application.yml文件中,同样也可以直接存放在类中,下面是各个属性数据的获取处:
对象存储/Bucket 列表/所创建的Bucket/概览
下的访问域名的外网访问的Endpoint(地域节点);# 指定端口号
server:
port: 8808
spring:
# 服务名称
application:
name: 你的服务名称
# OSS配置
cloud:
alicloud:
oss:
endpoint: 你的地域节点
bucket: 你的Bucket名
access-key: 你的AK
secret-key: 你的AS
在controller包下新建OssController类,在其中编写生成签名等信息的相关操作。
@RestController
@RequestMapping("/oss")
public class OssController {
@Autowired
OSS ossClient;
@Value("${spring.cloud.alicloud.oss.endpoint}")
String endpoint;
@Value("${spring.cloud.alicloud.oss.bucket}")
String bucket;
@Value("${spring.cloud.alicloud.access-key}")
String accessId;
@Value("${spring.cloud.alicloud.secret-key}")
String accessKey;
@GetMapping("/policy")
public Result policy() {
// host的格式为 bucketname.endpoint,即https://你的bucket名.地域节点/文件名.文件后缀
String host = "https://" + bucket + "." + endpoint;
// 用户上传文件时指定的前缀,即存放在以时间命名的文件夹内
String dir = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
Map<String, String> respMap = null;
try {
// 过期时间
long expireTime = 30;
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 encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
respMap = new LinkedHashMap<String, String>();
// AK
respMap.put("accessid", accessId);
// 用户表单上传的策略(Policy)
respMap.put("policy", encodedPolicy);
// 签名
respMap.put("signature", postSignature);
// 上传文件时指定的前缀
respMap.put("dir", dir);
// oss保存文件的host
respMap.put("host", host);
// 过期时间
respMap.put("expire", String.valueOf(expireEndTime / 1000));
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
} finally {
// 关闭oss客户端流
ossClient.shutdown();
}
return Result.ok().put("data", respMap);
}
}
由于请求的方式为Get,因此可以直接使用浏览器,或使用Postman、Apifox等接口测试工具进行测试,这里使用的是Apifox,请求http://localhost:8808/oss/policy
。
这里使用到的前端技术为Vue+Element-UI,这里只提供了单独的文件上传组件,大家自行导入使用即可,但是记得下载Element-UI以及UUID的依赖包。注意的是上传组件中的action需要你创建的Bucket中的Bucket域名,这个和地域节点获取的地方一致。
点击上传
只能上传jpg/png文件,且不超过10MB
这时候功能按道理来说其实都是可以正常运行的了,只是在前端测试的时候会出现CORS跨域问题。这时因为客户端进行表单直传到OSS时,会从浏览器向OSS发送带有Origin
的请求消息。OSS对带有Origin
头的请求消息会进行跨域规则(CORS)的验证。因此需要为Bucket设置跨域规则以支持Post方法。
将前端项目和后端项目都跑起来之后,我们来到前端界面进行上传测试,下面是测试结果: