Java整合阿里云OSS,授权链接给前端上传文件报错签名异常解决
·
博主最近接入阿里云的OSS对象存储,踩了两个很无语的坑。官方文档地址
- 问题一
文件上传报错SignatureDoesNotMatch:The request signature we calculated does not match the signature you provided. Check your key and signing method。 - 问题二
object_name问题,加上/object_name后上传失败。(这里的路径不能以 ‘/’ 开头,需要以 '路径/路径/路径…/文件名称.后缀’格式)
排查了跨域设置(没问题),排查了防盗链(没问题)。最后访问官方给的问题排查页面,也没有找到问题。官方问题参考文档地址
最后联系阿里技术才摸到问题
总结一下上述问题,在接入COS时:
其一:COS创建授权链接时,不需要设置’Content-type’,而OSS是必须要设置的。
其二:COS配置project_name时,需要以’ / '开头,而OSS不能以’ / '开头。
下面会以Java+前端axios为例进行OSS集成。通过调用后端接口获取授权上传链接,前端使用授权链接进行文件上传。
优点:使用授权后,上传是通过前端上传的,将流量压力全部交给了对象存储服务器,不再需要将压力放在业务服务器上面。OSS服务器的流量费用比业务服务器等便宜的多。
- 加入maven依赖
<!--阿里OSS对象存储-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
- 写入配置到yml
aliOss:
endpoint: https://oss-cn-beijing.aliyuncs.com # 上传到的OSS地址
access_key_id: 你的账号keyid# 主账户的keyId
access_key_secret: 你账号的keySecret
bucket_name: 你创建的bucket的name
path: data 你要存储的路径(可选)
- Config实体
@Component
public class AliOssConfig {
public static String endpoint;
@Value("${sdk.aliOss.endpoint}")
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public static String accessKeyId;
@Value("${sdk.aliOss.access_key_id}")
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public static String accessKeySecret;
@Value("${sdk.aliOss.access_key_secret}")
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public static String bucketName;
@Value("${sdk.aliOss.bucket_name}")
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
public static String path;
@Value("${sdk.aliOss.path}")
public void setPath(String path) {
this.path = path;
}
}
- 公共实体
@Data
public class AliOssPublicEntity {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
private String objectKey;
public static AliOssPublicEntity build(String objectKey) {
AliOssPublicEntity entity = new AliOssPublicEntity();
entity.setEndpoint(AliOssConfig.endpoint);
entity.setAccessKeyId(AliOssConfig.accessKeyId);
entity.setAccessKeySecret(AliOssConfig.accessKeySecret);
entity.setBucketName(AliOssConfig.bucketName);
entity.setObjectKey(objectKey);
return entity;
}
}
- 工具类
package com.ych.SDK.alibaba.ossSDK;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.ych.SDK.alibaba.ossSDK.entity.AliOssPublicEntity;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
* Author: Usopp.tsui
* Date: 2021/1/20
* Time: 11:46
* Description:阿里云OSS工具类
*/
@Component
public class AliOssUtil {
/**
* 文件直传
*
* @param objectKey 上传路径
* @param inputStream 上传流
* @throws RuntimeException
*/
public void fileUpload(String objectKey, InputStream inputStream) throws RuntimeException {
Map map = getCommon(objectKey);
OSS ossClient = null;
try {
ossClient = (OSS) map.get(0);
AliOssPublicEntity model = (AliOssPublicEntity) map.get(1);
if (ossClient.doesObjectExist(model.getBucketName(), model.getObjectKey())) {
throw new RuntimeException("此文件重名,请更改文件名重试!");
}
PutObjectRequest putObjectRequest = new PutObjectRequest(model.getBucketName(), model.getObjectKey(), inputStream);
PutObjectResult putObjectResult = ossClient.putObject(putObjectRequest);
String eTag = putObjectResult.getETag();
if (StringUtils.isBlank(eTag)) {
throw new RuntimeException("文件直传失败");
}
} catch (Exception e) {
throw new RuntimeException("文件直传失败:" + e.getMessage());
} finally {
ossClient.shutdown();
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* OSS获取下载签名URL
*
* @param objectKey 文件对象key
* @return 签名URL
*/
public String getOssObjectDownAuthUrl(String objectKey) throws RuntimeException {
Map map = getCommon(objectKey);
OSS ossClient = null;
try {
ossClient = (OSS) map.get(0);
AliOssPublicEntity model = (AliOssPublicEntity) map.get(1);
GeneratePresignedUrlRequest req =
new GeneratePresignedUrlRequest(model.getBucketName(), model.getObjectKey(), HttpMethod.GET);
//这里设置签名在半个小时后过期
Date expireDate = new Date(System.currentTimeMillis() + 30L * 60L * 1000L);
req.setExpiration(expireDate);
URL url = ossClient.generatePresignedUrl(req);
String urlStr = url.toString();
return urlStr;
} catch (Exception e) {
throw new RuntimeException("获取下载签名URL失败");
} finally {
ossClient.shutdown();
}
}
/**
* OSS获取下载签名URL
*
* @param objectKey 文件对象key
* @param expireTime 当前时间加多少毫秒后过期,过期时间(毫秒)
* @return 签名URL
*/
public String getOssObjectDownAuthUrl(String objectKey, long expireTime) throws RuntimeException {
Map map = getCommon(objectKey);
OSS ossClient = null;
try {
ossClient = (OSS) map.get(0);
AliOssPublicEntity model = (AliOssPublicEntity) map.get(1);
GeneratePresignedUrlRequest req =
new GeneratePresignedUrlRequest(model.getBucketName(), model.getObjectKey(), HttpMethod.GET);
//这里设置签名在半个小时后过期
Date expireDate = new Date(System.currentTimeMillis() + expireTime);
req.setExpiration(expireDate);
URL url = ossClient.generatePresignedUrl(req);
String urlStr = url.toString();
return urlStr;
} catch (Exception e) {
throw new RuntimeException("获取下载签名URL失败");
} finally {
ossClient.shutdown();
}
}
/**
* OSS获取上传签名URL
*
* @param objectKey 文件对象key
* @return 签名URL
*/
public String getOssObjectUploadAuthUrl(String objectKey) throws RuntimeException {
Map map = getCommon(objectKey);
OSS ossClient = null;
try {
ossClient = (OSS) map.get(0);
AliOssPublicEntity model = (AliOssPublicEntity) map.get(1);
if (ossClient.doesObjectExist(model.getBucketName(), model.getObjectKey())) {
throw new RuntimeException("此文件重名,请更改文件名重试!");
}
Date expirationTime = new Date(System.currentTimeMillis() + 30L * 60L * 1000L);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(model.getBucketName(), model.getObjectKey(), HttpMethod.PUT);
request.setExpiration(expirationTime);
//必须要!!!!!!,而且前端上传时,也需要在header里面设置,content-type为"application/octet-stream"
request.setContentType("application/octet-stream");
URL url = ossClient.generatePresignedUrl(request);
String urlstr = url.toString();
return urlstr;
} catch (Exception e) {
throw new RuntimeException("获取上传签名URL失败" + e.getMessage());
} finally {
ossClient.shutdown();
}
}
/**
* 删除存储对象
*
* @param objectKey 文件对象key
* @return 签名URL
*/
public void deleteObject(String objectKey) throws RuntimeException {
Map map = getCommon(objectKey);
OSS ossClient = null;
try {
ossClient = (OSS) map.get(0);
AliOssPublicEntity model = (AliOssPublicEntity) map.get(1);
// 指定对象所在的存储桶
ossClient.deleteObject(model.getBucketName(), model.getObjectKey());
} catch (RuntimeException clientException) {
throw new RuntimeException("删除存储对象失败");
} finally {
ossClient.shutdown();
}
}
/**
* 绝对路径更换为相对路径
*
* @param url 绝对路径
* @return 相对路径
*/
public String getRelativePath(String url) {
url = url.substring(url.indexOf(".com") + 5, url.indexOf("?"));
return url;
}
/**
* client公共参数
*
* @param objectKey
* @return
*/
private Map getCommon(String objectKey) {
AliOssPublicEntity entity = AliOssPublicEntity.build(objectKey);
OSS ossClient = new OSSClientBuilder().build(entity.getEndpoint(), entity.getAccessKeyId(), entity.getAccessKeySecret());
Map map = new HashMap();
map.put(0, ossClient);
map.put(1, entity);
return map;
}
}
前端代码:
axios({
method: 'put',
url: fileUrl,
data: file.file,
headers: {
//必须要!!!
'Content-Type': 'application/octet-stream'
}
}).then(() => {
// 成功后你要进行的操作
})
阿里云上OSS配置:
- 进入控制台
- 新增一个Bucket (这里的name就会用作你的yml配置中的name)

- 进入bucket设置跨域(必须设置,不然会报没有权限访问)
- 其他设置:根据个人需要进行防盗链等的设置
至此,整合接入完成,传入以后进行上传即可。
附上Controller:
package com.ych.modules.cms.authorInfc.fileAuth.controller;
import com.ych.SDK.alibaba.ossSDK.AliOssUtil;
import com.ych.SDK.alibaba.ossSDK.config.AliOssConfig;
import com.ych.redis.RedisUtil;
import com.ych.redis.config.RedisKeyConfig;
import com.ych.utils.TLMap;
import com.ych.utils.UUID;
import com.ych.utils.serviceReturn.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@Api(tags = {"CMS--文件上传相关"}, description = "CMS--文件上传相关")
@RestController
@RequestMapping("/cms/auth/fileAuth")
public class FileAuthController {
@Autowired
private RedisUtil redisUtil;
@Autowired
private AliOssUtil aliOssUtil;
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
@ApiOperation(value = "获取文件上传地址", notes = "授权地址", position = 1)
@PostMapping("/getUploadUrl")
public R<String> getUploadUrl(@RequestBody String fileName) {
try {
String userId = TLMap.getUserId();//可空
String suffix = fileName.substring(fileName.lastIndexOf("."));//后缀名
String newFileName = UUID.randomUUID() + suffix; //重新用uuid命名
String path;
if (StringUtils.isBlank(userId)) {
path = AliOssConfig.path + "/public/" + sdf.format(new Date()) + "/" + newFileName;
} else {
path = AliOssConfig.path + "/private/" + userId + "/" + sdf.format(new Date()) + "/" + newFileName;
}
String url = aliOssUtil.getOssObjectUploadAuthUrl(path);
String key = RedisKeyConfig.fileTokenKey + path;
redisUtil.redisTemplate().opsForValue().set(key, key);
redisUtil.redisTemplate().expire(key, 3600, TimeUnit.SECONDS);//30分钟
return new R(0, "授权成功", url);
} catch (RuntimeException e) {
return new R(1, e.getMessage(), null);
} catch (Exception e) {
return new R(2, "系统未知异常" + e.getMessage(), null);
}
}
@ApiOperation(value = "* 马上获取文件下载路径", notes = "条件:首先调用过上传授权接口", position = 2)
@PostMapping("/getDownloadUrl")
@ApiImplicitParam(name = "path", value = "绝对路径", required = true, dataType = "String")
public R<String> getFileUrl(@RequestBody String path) {
path = aliOssUtil.getRelativePath(path);//转换成相对路径
String key = RedisKeyConfig.fileTokenKey + path;
try {
String one = (String) redisUtil.redisTemplate().opsForValue().get(key);
if (StringUtils.isBlank(one)) {
throw new RuntimeException("未授权,获取下载连接失败");
}
String url = aliOssUtil.getOssObjectDownAuthUrl(path);
if (StringUtils.isBlank(url)) {
throw new RuntimeException("地址获取失败");
}
redisUtil.redisTemplate().delete(key);
return new R(0, "地址获取成功", url);
} catch (Exception e) {
return new R(1, e.getMessage(), null);
}
}
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)