项目场景: 

调用CRM系统(第三方)的OSS服务,下载文件时候,出现了下面的异常:

com.aliyun.oss.OSSException: The specified bucket is not valid

详细的报错我已经截图保存了:

com.aliyun.oss.OSSException: The specified bucket is not valid.
[ErrorCode]: InvalidBucketName
[RequestId]: 61AF1AEDCCBF8C544DE01EAE
[HostId]: oss-cn-hefei-XXXXX-a.ops.t2cloud.cn
[ResponseError]:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>InvalidBucketName</Code>
  <Message>The specified bucket is not valid.</Message>
  <RequestId>61AF1AEDCCBF8C544DE01EAE</RequestId>
  <HostId>oss-cn-hefei-XXXXX-a.ops.t2cloud.cn</HostId>
  <BucketName>president_info_20211203.csv</BucketName>
</Error>

	at com.aliyun.oss.common.utils.ExceptionFactory.createOSSException(ExceptionFactory.java:99)
	at com.aliyun.oss.internal.OSSErrorResponseHandler.handle(OSSErrorResponseHandler.java:69)
	at com.aliyun.oss.common.comm.ServiceClient.handleResponse(ServiceClient.java:248)
	at com.aliyun.oss.common.comm.ServiceClient.sendRequestImpl(ServiceClient.java:130)
	at com.aliyun.oss.common.comm.ServiceClient.sendRequest(ServiceClient.java:68)
	at com.aliyun.oss.internal.OSSOperation.send(OSSOperation.java:94)
	at com.aliyun.oss.internal.OSSOperation.doOperation(OSSOperation.java:149)
	at com.aliyun.oss.internal.OSSOperation.doOperation(OSSOperation.java:113)
	at com.aliyun.oss.internal.OSSObjectOperation.getObject(OSSObjectOperation.java:259)
	at com.aliyun.oss.OSSClient.getObject(OSSClient.java:633)
	at com.aliyun.oss.OSSClient.getObject(OSSClient.java:621)
	at cn.chinapost.zxpt.crc.svc.activity.PresidentInfoService.queryAndSaveFromOss(PresidentInfoService.java:106)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)

核心代码如下:

public static void streamingDownload() {
    try {
        // 1.创建 OSSClient 实例
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

        // 2.判断 要下载的文件 是否存在
        if (!ossClient.doesBucketExist(bucketName)) return;            // bucket 不存在
        // 3.判断 要下载的文件 是否存在
        if (!ossClient.doesObjectExist(bucketName, fileName)) return;  // fileName 不存在

        // 4.获取下载对象
        OSSObject ossObject = ossClient.getObject(bucketName, fileName);
        // 5.按行读取文件内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
        while(true){
            String line = reader.readLine();
            if (line == null) break;
            // ... 结构化数据,业务逻辑
        }

        // 6.数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作
        reader.close();
        // 7.关闭oss
        ossClient.shutdown();
    } catch (IOException e) {
        e.fillInStackTrace();
    }
}

 原因分析:

翻译一下错误提示:指定的存储桶无效!!存储桶指的就是 bucket。

先debug跑一下,看看问题是在哪里报出来的,位置(见上面的【核心代码】): 

// 3.判断 要下载的文件 是否存在
 if (!ossClient.doesObjectExist(bucketName, fileName)) return;  // fileName 不存在

为了解决这个问题,我的整体思路是这样的:

  1. 先检查下自己调用的oss配置,确保没有写错,最终证实没有问题;
  2. 在服务器上(发oss请求的服务器)ping一下对方的 ip 和 域名(endpoint),最终证实都能 ping 通;
  3. 和CRM(第三方)重新核实了一下oss配置,确保我们的配置的没问题的,最终证实我拿到的文件也没有问题;
  4. 我把自己的代码复制到本地,在自己的阿里云服务上申请了一个OSS 的bucket,本地测试了一下文件的上传和下载,证明代码也没有问题;

那只能说明:是生产环境的差异化配置导致的OSS服务问题,毕竟每个项目的环境都是千差万别的。

我所处的环境变量是:crm 和 我对接的 oss 对象存储 bucket 并不在公网上,只能在内网的测试系统上才能访问。

然后,我从阿里云找到OSS对象存储的官方文档 ,把重心放在研究【ClientConfigration】初始化配置上,然后就看到了这个 CNAME 的配置。

// 创建ClientConfiguration实例,按照您的需要修改默认参数
ClientConfiguration conf = new ClientConfiguration();
// 开启支持 CNAME 选项
config.setSupportCname(true);

简单解释下 CNAME 的配置:

就是做一个域名映射,把用户自定义的域名与oss域名进行绑定,然后就可以使用用户自定义的域名(endpoint)进行oss访问。

我遇到的问题,恰好就属于 ClientConfiguration 配置的范畴。


解决方案

创建 OSSClient 实例时,需要初始化 ClientConfiguration 配置,手动关闭 CNAME 选项。

这里,就要换一个 OSSClient 构造方法啦,一个支持 config 的方法:

public static void streamingDownload() {
    try {
        // 创建ClientConfiguration实例,按照您的需要修改默认参数
        ClientConfiguration config = new ClientConfiguration();
        // 关闭支持 CNAME 选项
        config.setSupportCname(false);

        // 1. 创建OSSClient实例
        OSSClient oss = new OSSClient(endpoint, accessKeyId, accessKeySecret, config);

        // 2.判断 要下载的文件 是否存在
        if (!ossClient.doesBucketExist(bucketName)) return;            // bucket 不存在
        // 3.判断 要下载的文件 是否存在
        if (!ossClient.doesObjectExist(bucketName, fileName)) return;  // fileName 不存在

        // 4.获取下载对象
        OSSObject ossObject = ossClient.getObject(bucketName, fileName);
        // 5.按行读取文件内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
        while(true){
            String line = reader.readLine();
            if (line == null) break;
            // ... 结构化数据,业务逻辑
        }

        // 6.数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作
        reader.close();
        // 7.关闭oss
        ossClient.shutdown();
    } catch (IOException e) {
        e.fillInStackTrace();
    }
}

总结:

  • 我遇到的场景很少见,很不好定位,和 ClientConfiguration 配置的 CNAME 属性有关;
  • 除此之外,还遇到了“endpoint 中带有 bucket 名称”时,也会出现上述异常,这个问题博客很多,我就不想洗写了,解决办法:在 endpoint 中 去除 bucket 名称就可以了;
  • 希望能帮你节省大量的测试时间。
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐