springboot整合阿里云OSS做服务端签名让微信小程序文件直传OSS
1、需求:
想让小程序直接上传图片、文件到OSS存储,不需要经过我的服务器接收文件再转发到OSS存储。
好处
-
可以减轻自己服务器的压力,和不必要的麻烦。
-
有利于维护和扩展(比如扩容、迁移等等)。
-
可以更加快的存储到OSS中,不需要多次来回传输。
-
不占用自己服务器的带宽,阿里的OSS节点处理更快。
-
等等。。。
2、基本原理介绍(官方描述,我觉得很简单了)
服务端签名后前端(小程序)直传的原理如下:
- 用户发送请求到自己服务器获取上传的Policy和签名。
- 自己的服务器返回前端直传oss需要Policy和签名给用户。
- 用户直接上传数据到OSS。
3、springboot 搞起来
1.新建一个springboot 项目,添加依赖
<!--阿里oss-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.8.3</version>
</dependency>
2.编写一个OSS的配置类(可以根据自己的需求来,注入等方式都可以)
public class AliOssConfig {
/**
* 填写您的AccessKeyId
*/
public final static String accessId = "xxxx";
/**
* 填写您的AccessKeySecret
*/
public final static String accessKey = "xxxxx";
/**
* 填写您的 endpoint(地域节点),就是你OSS的区域节点的域名
*/
public final static String endpoint = "xxxx.aliyuncs.com";
/**
* 填写您的 bucketName ,就是你再OSS创建Bucket的名称
*/
public final static String bucket = "xxx";
/**
* 直传地址,格式为 bucketName.endpoint (Bucket域名)
*/
public final static String host = "https://" + bucket +"."+ endpoint;
/*
* ============= 配置上传过期时间、大小等等 =========
*/
/**
* 上传截止时间(秒)
*/
public final static long expireTime = 30;
/**
* 上传文件最小(字节)
*/
public final static long min = 0;
/**
* 上传文件最大(字节)
*/
public final static long max = 1048576000;
/**
* 上传文件的前缀、可忽略
*/
public final static String dir = "user/";
}
3.创建controller
@RestController
@RequestMapping("/ali/oss")
public class AliOssController {
/**
* 创建OSS客户端
*/
private OSSClient client = new OSSClient(AliOssConfig.endpoint, AliOssConfig.accessId, AliOssConfig.accessKey);
/**
* 前端获取直传的policy信息
* 前端可以使用响应的参数进行直传到oss存储
*
* @return
*/
@GetMapping("/policy")
public AliOssPolicy getPolicy() {
// 直传有效截止时间
long expireEndTime = System.currentTimeMillis() + (AliOssConfig.expireTime * 1000);
Date expiration = new Date(expireEndTime);
PolicyConditions policyConditions = new PolicyConditions();
// 设置可上传文件的大小
policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, AliOssConfig.min, AliOssConfig.max);
// 设置上传文件的前缀、可忽略
policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, AliOssConfig.dir);
// 生成policy
String postPolicy = client.generatePostPolicy(expiration, policyConditions);
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = client.calculatePostSignature(postPolicy);
// 封装policy等信息
AliOssPolicy aliOssPolicy = new AliOssPolicy();
aliOssPolicy.setOssAccessKeyId(AliOssConfig.accessId);
aliOssPolicy.setPolicy(encodedPolicy);
aliOssPolicy.setSignature(postSignature);
aliOssPolicy.setDir(AliOssConfig.dir);
aliOssPolicy.setHost(AliOssConfig.host);
aliOssPolicy.setExpire(String.valueOf(expireEndTime / 1000));
return aliOssPolicy;
}
}
4.创建一个封装类来响应给前端
public class AliOssPolicy implements Serializable {
/**
* 上传认证id
*/
private String ossAccessKeyId;
/**
* policy
*/
private String policy;
/**
* 签名
*/
private String signature;
/**
* 直传文件的开头(路径)
*/
private String dir;
/**
* 直传地址
*/
private String host;
/**
* 上传截止时间
*/
private String expire;
// get和set省略
}
完成!
4、postman请求测试
http://localhost:8080/ali/oss/policy
{
// 认证key
"ossAccessKeyId": "xxxx",
// 签名后的数据
"policy": "xxxx",
// 文件前缀
"dir": "xxx/",
// 前端直传的地址
"host": "https://xxx.xxxx.aliyuncs.com",
// 有效截止时间
"expire": "1583592220"
}
5、小程序 搞起来
// 调用小程序api选择图片上传
wx.chooseImage({
success: function(res) {
var tempFilePaths = res.tempFilePaths
// 上传图片到oss
wx.uploadFile({
// 直传的oss地址
url: 'https://xxxx.xxxx.aliyuncs.com',
// 上传的文件
filePath: tempFilePaths[0],
// 必须为file
name: 'file',
// 表单数据
formData: {
// key:文件路径,如 user/666.png
//直接 key: "${filename}" 是存放在根目录,文件名为上传的文件名
// 如果后端签名时指定了dir则开头必须同后端的一致,如后端指定了 “user/” 开头则必须是 user/xxx.xx
key: "user/666.png",
// 自定义成功响应值 ,默认响应204
success_action_status: '200',
OssAccessKeyId: 'xxxx',
// 后端签名后发回的policy
policy: 'xxxxxx',
// 后端返回的签名
signature: 'xxxxx'
},
success: function(res) {
if (res.statusCode == 200) {
console.log("上传成功!")
console.log(res)
} else {
console.log("上传失败!")
console.log(res)
}
},
fail(error) {
console.log("上传错误")
console.log(error)
}
})
}
})
6、上传测试:
OSS管理客户端
直传OSS中可能出现的错误
Post Object常见错误如下表所示。
序号 | 错误 | 原因 | 解法 |
---|---|---|---|
1 | ErrorCode: MalformedPOSTRequest ErrorMessage: The body of your POST request is not well-formed multipart/form-data | 表单域格式不符合要求 | 表单域格式请查看表后的 Post Object表单域格式 |
2 | ErrorCode: InvalidAccessKeyId ErrorMessage: The OSS Access Key Id you provided does not exist in our records. | AccessKeyID 禁用或不存在,或者过期的临时用户AccessKeyID,或者临时用户没有提供STS Token | 排查方法请查看InvalidAccessKeyId错误排查 |
3 | ErrorCode: AccessDenied ErrorMessage: Invalid according to Policy: Policy expired. | 表单域Policy中的expiration 过期 | 请调整Policy中的expiration ,注意expiration的格式ISO8601 GMT |
4 | ErrorCode: AccessDenied ErrorMessage: SignatureDoesNotMatch The request signature we calculated does not match the signature you provided. Check your key and signing method. | 签名错误 | 签名方法请查看下面的Post Object的签名 |
5 | ErrorCode: InvalidPolicyDocument ErrorMessage: Invalid Policy: Invalid Simple-Condition: Simple-Conditions must have exactly one property specified. | 请求中Policy 至少包含一个condition | 请查看表后的Post Object的Policy格式 |
6 | ErrorCode: InvalidPolicyDocument ErrorMessage: Invalid Policy: Invalid JSON: unknown char e | 请求中的Policy 格式不正确 | 请检查Policy格式," 是否加缺失,转义字符是否加\ |
7 | ErrorCode: InvalidPolicyDocument ErrorMessage: Invalid Policy: Invalid JSON: , or ] expected | 请求中的Policy 格式不正确 | 请检查Policy中是否缺少, 或] |
8 | ErrorCode: AccessDenied ErrorMessage: Invalid according to Policy: Policy Condition failed: [“starts-with”, “$key”, “user/eric/“] | 请求指定的Key 与Policy 限定的不符 | 请检查请求中表单域Key 的值 |
9 | ErrorCode: AccessDenied ErrorMessage: Invalid according to Policy: Policy Condition failed: [“eq”, “$bucket”, “mingdi-bjx”] | 请求指定Bucket 与Policy 限定的不符 | 请检查endpoint中的Bucket 的值 |
10 | ErrorCode: AccessDenied ErrorMessage: Invalid according to Policy: Policy Condition failed: [“starts-with”, “$x-oss-meta-prop”, “prop-“] | 请求指定的文件元数据x-oss-meta-prop 与policy限定的不符 | 请检查请求中的x-oss-meta-prop 的值 |
11 | ErrorCode: AccessDenied ErrorMessage: Invalid according to Policy: Policy Condition failed: [“eq”, “ f i e l d ” , “ {field}”, “ field”,“{value}”] | 表单域中指定的{field} 与Policy 中限定的值不符,或者在请求中没有指定 | 请检查请求中的{field} 的值 |
12 | ErrorCode: AccessDenied ErrorMessage: You have no right to access this object because of bucket acl. | 当前用户无权限 | 请查看OSS权限问题及排查 |
13 | ErrorCode: InvalidArgument ErrorMessage: The bucket POST must contain the specified ‘key’. If it is specified, please check the order of the fields | 表单域没有指定Key ,或者放在了表单域file 后 | 请添加表单域Key 或调整顺序 |