文章目录
- 1.简介
- 1.1 普通上传方式
- 1.2 服务端签名后直传
- 3.服务端签名后直传文档
- 3.1 用户向应用服务器请求上传Policy和回调。
- 3.2 应用服务器返回上传Policy和签名给用户。
- 3.3 用户使用Post方法向OSS发送文件上传请求。
- 4.实战开发-后端
- 4.1 pom.xml核心配置
- 4.2 application.yml核心配置
- 4.3 OssClientUtils工具类
- 4.4 OssConfig配置类
- 4.5 OssConstants常量类
- 4.6 OssServiceController控制器
- 4.7 Apifox测试
- 5.注意事项和细节说明
- 5.1 解决后端跨域问题
- 5.2 解决阿里云跨域问题
1.简介
阿里云对象存储 OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,提供数据高可用性, 多种存储类型供选择,全面优化存储成本。
1.1 普通上传方式
- 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。
- 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。
- 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。
1.2 服务端签名后直传
- Web端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。
- 但服务端无法实时了解用户上传了多少文件,上传了什么文件。如果想实时了解用户上传了什么文件,可以采用服务端签名直传并设置上传回调。
- 但存在着恶意上传的风险,造成存储空间的浪费
3.服务端签名后直传文档
🏠 https://help.aliyun.com/document_detail/31926.html
基于Post Policy的使用规则在服务端通过各种语言代码完成签名,然后通过表单直传数据到OSS。由于服务端签名直传无需将AccessKey暴露在前端页面,相比JavaScript客户端签名直传具有更高的安全性。
3.1 用户向应用服务器请求上传Policy和回调。
请将客户端源码中的upload.js
文件的如下代码片段的变量serverUrl
的值设置为应用服务器的URL。
// serverUrl是用户获取签名和Policy等信息的应用服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
serverUrl = 'http://88.88.88.88:8888'
设置完成后,客户端会向该serverUrl
发送Get请求来获取需要的信息。客户端源码下载地址,请参见aliyun-oss-appserver-js-master.zip。
本场景为服务端签名后直传,不涉及上传回调。因此,您需要注释客户端源码的upload.js
文件内的'callback' : callbackbody
字段,以关闭上传回调功能。
{
'key' : key + '${filename}',
'policy': policyBase64,
'OSSAccessKeyId': accessid,
// 设置服务端返回200状态码,默认返回204。
'success_action_status' : '200',
'callback' : callbackbody,
'signature': signature,
}
3.2 应用服务器返回上传Policy和签名给用户。
应用服务器侧的签名直传服务会处理客户端发送的Get请求消息,您可以设置对应的代码让应用服务器能够给客户端返回正确的消息。
以下是签名直传服务返回给客户端消息Body内容的示例:
{
"accessid":"LTAI5tBDFVar1hoq****",
"host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
"policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8i****",
"signature":"VsxOcOudx******z93CLaXPz+4s=",
"expire":1446727949,
"dir":"user-dirs/"
}
Body中的各字段说明如下:
字段 | 描述 |
---|---|
accessid | 用户请求的AccessKey ID。 |
host | 用户发送上传请求的域名。 |
policy | 用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串。详情请参见Post Policy。 |
signature | 对Policy签名后的字符串。详情请参见Post Signature。 |
expire | 由服务器端指定的Policy过期时间,格式为Unix时间戳(自UTC时间1970年01月01号开始的秒数)。 |
dir | 限制上传的文件前缀。 |
3.3 用户使用Post方法向OSS发送文件上传请求。
new_multipart_params = {
// key表示上传到Bucket内的Object的完整路径,例如exampledir/exampleobject.txtObject,完整路径中不能包含Bucket名称。
// filename表示待上传的本地文件名称。
'key' : key + '${filename}',
'policy': policyBase64,
'OSSAccessKeyId': accessid,
// 设置服务端返回状态码为200,不设置则默认返回状态码204。
'success_action_status' : '200',
'signature': signature,
};
4.实战开发-后端
4.1 pom.xml核心配置
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
<!--<version>2.1.0.RELEASE</version>-->
</dependency>
4.2 application.yml核心配置
server:
port: 9101
max-http-header-size: 2MB
servlet:
context-path: /service
spring:
cloud:
alicloud:
access-key: LTAI5tKcWoeuxTK8********
secret-key: TQXiDtEnaR8Y7tXetvYZ0t********
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com
bucket: waveedu
4.3 OssClientUtils工具类
package com.zhulang.waveedu.sms.util;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.zhulang.waveedu.common.entity.Result;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author 狐狸半面添
* @create 2023-01-29 1:36
*/
public class OssClientUtils {
private OSS ossClient;
private String accessId;
private String endpoint;
private String bucket;
public OssClientUtils(OSS ossClient, String accessId, String endpoint, String bucket) {
this.ossClient = ossClient;
this.accessId = accessId;
this.endpoint = endpoint;
this.bucket = bucket;
}
public Result policy(String dirType) {
// 1.指定填写Host地址,格式为https://bucketname.endpoint
String host = "https://" + bucket + "." + endpoint;
// 2.设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
String format = new SimpleDateFormat("/yyyy/MM/").format(new Date());
String dir = dirType + format;
Map<String, String> respMap = null;
try {
// 3.指定默认超时时间是30s
long expireTime = 30;
// 4.得到最终的截止时间
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
// 5.封装签名
PolicyConditions policyConds = new PolicyConditions();
// 5.1 设置可上传文件的大小,这里设置为 0 - 10MB
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 10485760);
// 5.2 设置上传文件的前缀、可忽略
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
// 5.3 对Policy签名后的字符串。
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
String postSignature = ossClient.calculatePostSignature(postPolicy);
// 6.用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串。
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
// 7.封装签名直传服务返回给客户端消息Body内容
respMap = new LinkedHashMap<>();
// 7.1 用户请求的AccessKey ID。
respMap.put("accessid", accessId);
// 7.2 用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串。
respMap.put("policy", encodedPolicy);
// 7.3 对Policy签名后的字符串。
respMap.put("signature", postSignature);
// 7.4 限制上传的文件前缀。
respMap.put("dir", dir);
// 7.5 用户发送上传请求的域名。
respMap.put("host", host);
// 7.6 由服务器端指定的Policy过期时间,格式为Unix时间戳(自UTC时间1970年01月01号开始的秒数)。
respMap.put("expire", String.valueOf(expireEndTime / 1000));
} catch (Exception e) {
return Result.error();
}
return Result.ok(respMap);
}
}
4.4 OssConfig配置类
package com.zhulang.waveedu.sms.config;
import com.aliyun.oss.OSS;
import com.zhulang.waveedu.sms.util.OssClientUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* @author 狐狸半面添
* @create 2023-01-29 1:41
*/
@Configuration
public class OssConfig {
@Resource
private OSS ossClient;
@Value("${spring.cloud.alicloud.access-key}")
private String accessId;
@Value("${spring.cloud.alicloud.oss.endpoint}")
private String endpoint;
@Value("${spring.cloud.alicloud.oss.bucket}")
private String bucket;
@Bean
public OssClientUtils ossClientUtils() {
return new OssClientUtils(ossClient, accessId, endpoint, bucket);
}
}
4.5 OssConstants常量类
package com.zhulang.waveedu.sms.constant;
/**
* 与Oss相关的常量
*
* @author 狐狸半面添
* @create 2023-01-29 1:55
*/
public class OssConstants {
public static final String HEAD_IMAGE_DIR = "head-image";
}
4.6 OssServiceController控制器
package com.zhulang.waveedu.sms.controller;
import com.zhulang.waveedu.common.entity.Result;
import com.zhulang.waveedu.sms.constant.OssConstants;
import com.zhulang.waveedu.sms.util.OssClientUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author 狐狸半面添
* @create 2023-01-29 1:30
*/
@RestController
@RequestMapping("/oss")
public class OssServiceController {
@Resource
private OssClientUtils ossClientUtils;
/**
* 获取头像的签名
*
* @return 签名信息
*/
@RequestMapping("/headImage")
public Result headImage(){
return ossClientUtils.policy(OssConstants.HEAD_IMAGE_DIR);
}
}
4.7 Apifox测试
5.注意事项和细节说明
5.1 解决后端跨域问题
package com.zhulang.waveedu.basic.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 解决跨域问题
*
* @author 狐狸半面添
* @create 2023-01-15 23:08
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}
}