更新:OSS在私有云环境文件上传失败
代码在私有云执行时,总是出现以下错误
[ErrorCode]: NoSuchBucket
[RequestId]: 000000000000000000000000
[HostId]: oss***********.cn
[ResponseError]:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>NoSuchBucket</Code>
<Message>The specified bucket does not exist.</Message>
<RequestId>000000000000000000000000</RequestId>
<HostId>oss***********.cn</HostId>
<BucketName>abc</BucketName>
</Error>
但实际bucketName在配置信息中填写的是abc-oss,而abc是在存储空间下一个自定义的存放文件的路径,起初一直以为是配置信息读取错位的问题.....
查不出来原因,代码debug之后才有进一步发现...
错误信息如下:
errorMessage:SignatureDoesNotMatch
errorCode:The request signature we calculated does not match the signature you provided. Check your key and signing method.
这篇博客(传送门:解决私有云环境OSS报SignatureDoesNotMatch错误)挺有参考意义的,也成功解决了问题。
其实主要是CNAME的问题,关闭就好了
ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
// 私有云要关闭CNAME
conf.setSupportCname(false);
OSS ossClient = new OSSClientBuilder().build(endPoint, accessKeyId, accessKeySecret, conf);
//OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret,conf);
下载txt格式的文件时如果出现同样的问题,仍可以用以上方案解决问题
一、相关配置
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OssConfig implements InitializingBean {
@Value("${oss.end.point}")
private String ossEndPoint;
@Value("${oss.access.key.id}")
private String ossAccessKeyId;
@Value("${oss.access.key.secret}")
private String ossAccessKeySecret;
@Value("${oss.bucket.name}")
private String ossBucketName;
@Value("${oss.folder.name}")
private String ossFolderName;
@Value("${oss.visit.url}")
private String ossVisitUrl;
public static String OSS_END_POINT;
public static String OSS_ACCESS_KEY_ID;
public static String OSS_ACCESS_KEY_SECRET;
public static String OSS_BUCKET_NAME;
public static String OSS_FOLDER_NAME;
public static String OSS_VISIT_URL;
@Override
public void afterPropertiesSet() throws Exception {
OSS_END_POINT = ossEndPoint;
OSS_ACCESS_KEY_ID = ossAccessKeyId;
OSS_ACCESS_KEY_SECRET = ossAccessKeySecret;
OSS_BUCKET_NAME = ossBucketName;
OSS_FOLDER_NAME = ossFolderName;
OSS_VISIT_URL = ossVisitUrl;
}
}
1、OSS对外服务的访问域名(必要)OSS_END_POINT
传送门:公共云下OSS Region和Endpoint对照表
2、访问秘钥(必要)OSS_ACCESS_KEY_ID & OSS_ACCESS_KEY_SECRET
3、存储空间名称(必要)OSS_BUCKET_NAME
自由创建,符合命名规范都可以
4、存储文件夹名称(非必要)OSS_FOLDER_NAME
可要可不要,有一个配置的文件夹名称,方便文件管理
5、获取文件访问地址前缀(非必要)OSS_VISIT_URL
满足 bucketName + endPoint 的一个http地址,比如:http://bucketname.oss-cn-hangzhou.aliyuncs.com/
二、相关依赖
<!-- OSS -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.5.0</version>
</dependency>
三、上传功能
@RequestMapping("/upload")
@ApiOperation(value = "文件上传", notes = "上传文件")
public Result upload(@RequestParam(value = "file") MultipartFile multipartFile) {
String filename = System.currentTimeMillis() + "-" + multipartFile.getOriginalFilename();
log.info("=========>OSS文件上传开始:" + filename);
String endpoint = OssConfig.OSS_END_POINT;
String accessKeyId = OssConfig.OSS_ACCESS_KEY_ID;
String accessKeySecret = OssConfig.OSS_ACCESS_KEY_SECRET;
String bucketName = OssConfig.OSS_BUCKET_NAME;
OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
try {
//容器不存在,就创建
//if (!ossClient.doesBucketExist(bucketName)) {
// ossClient.createBucket(bucketName);
// CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
// createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
// ossClient.createBucket(createBucketRequest);
//}
//设置权限 这里是公开读
//ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
//创建文件路径
String fileUrl = OssConfig.OSS_FOLDER_NAME + "/" + filename;
//上传文件
PutObjectResult result = null;
try {
result = ossClient.putObject(new PutObjectRequest(bucketName, fileUrl, multipartFile.getInputStream()));
} catch (IOException e) {
log.error("IOException occurred: ", e);
}
if (null != result) {
log.info("==========>OSS文件上传成功,OSS完整地址:" + fileUrl);
return Result.success(OssConfig.OSS_VISIT_URL + fileUrl);
}
} catch (OSSException oe) {
log.error("OSSException occurred: ", oe);
} catch (ClientException ce) {
log.error("ClientException occurred: ", ce);
} finally {
//关闭
ossClient.shutdown();
}
return Result.fail("上传失败");
}
其中注释掉的代码:
bucketName 对应的存储空间已经创建了,在校验是否存在时,如果运维没有给访问者配备权限,是会报错的。
如果需要保留注释部分代码且运行时不报错,就需要给 accessKey 的拥有者配备相应权限
报错情况如下(示范):
[ErrorCode]: AccessDenied
[RequestId]: FFFFFFFFFFFFFFFFFFFFFFFF
[HostId]: bucketName + endPoint
[ResponseError]:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>The bucket you access does not belong to you.</Message>
<RequestId>FFFFFFFFFFFFFFFFFFFFFFFF</RequestId>
<HostId>bucketName + endPoint</HostId>
</Error>
还有一个需要注意的地方是捕获异常对象 ClientException ,选择阿里云的jar包
import com.aliyun.oss.ClientException;
四、下载功能
@RequestMapping(value = "download")
@ApiOperation(value = "文件下载", notes = "下载文件")
public void download(String ossUrl, HttpServletResponse response) {
ossUrl = ossUrl.substring(OssConfig.OSS_VISIT_URL.length());
InputStream in = null;
try {
OSSClient ossClient = new OSSClient(OssConfig.OSS_END_POINT, OssConfig.OSS_ACCESS_KEY_ID, OssConfig.OSS_ACCESS_KEY_SECRET);
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
OSSObject ossObject = ossClient.getObject(OssConfig.OSS_BUCKET_NAME, ossUrl);
in = ossObject.getObjectContent();
response.reset();
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(ossUrl.substring(ossUrl.lastIndexOf("-") + 1), "UTF-8"));
OutputStream outputStream = response.getOutputStream();
byte buf[] = new byte[2048];
int length = 0;
// 输出文件
while ((length = in.read(buf)) > 0) {
outputStream.write(buf, 0, length);
}
// 关闭输出流
outputStream.close();
} catch (Exception e) {
log.error("获取文件错误:", e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e) {
log.error("出错");
}
}
}
五、更新:记录一次OSS上线问题
刚申请下来的私有云上的OSS,在测试环境中配置用的公司的OSS是没有问题的,但是上到生产环境上时却一直报错,情况跟在GitHub上讨论区中的一位网友发言类似,但是并没有提到正确的解决方案。
传送门:分片上传的是否可以指定到目前,目前之前上传到bucketName的根目录 · Issue #211 · aliyun/aliyun-oss-java-sdk · GitHub
分析:
代码层面,跟其他用到OSS的项目使用的代码,抛却业务逻辑外,基本是一模一样的
配置层面,比对其他项目都是没有问题的
但是生产环境却一直报错
解决:
①找了OSS售后,将版本从2.5.0更新到3.4.1版本,测试还是同样的报错
②对比他们给的demo,以及把相关配置发给送对方去测验,遂发现ent.point的不一致,对方提供测试截图中ent.point前缀拼接了bucket名称,形如:bucket.entpoint
③修改配置测试后一切正常
④严重怀疑2.5.0版本如果改成同样的配置,功能也能正常使用。但最根本的是,这个的配置,跟其他有OSS项目的配置完全不一样,就怀疑该OSS在申请的时候什么东西漏了。但问题已经解决了,这些猜测就没有去验证了。