开源一夏 | 如何使用 Java 操作华为对象存储 OBS 删除一个目录?
![开源一夏 | 如何使用Java操作华为对象存储OBS删除一个目录?](https://static001.geekbang.org/infoq/df/dfd00477576cd68608665795b6bdfece.png)
华为 OBS 是华为云存储的一个产品,它众多对象存储产品中的一员,遵循亚马逊开源的 S3 协议,国内类似的产品还有阿里云的 OSS、腾讯云的 COS 等。我们通常会把非结构性数据存储到对象存储中,比如图片、视频等,那么管理这些对象就成了重重之重了。
在文章开始之前呢,先给大家介绍一下华为 OBS 的使用,华为 OBS 的官网地址是:
![](https://static001.geekbang.org/infoq/cf/cff6257e818527dee28a428548f0bb75.png)
一般使用的话,肯定是先购买:
![](https://static001.geekbang.org/infoq/1f/1fe554d2dd6114b9c0173563e7addc24.png)
在购买的界面,可以根据自己的需求进行定制化,然后立即购买就好。
购买后,可以点击“管理控制台”,进入控制台对我们购买的 OBS 产品进行配置管理。
![](https://static001.geekbang.org/infoq/2c/2cd3948e0bbf372da96d6b0f89e47b6f.png)
由于本文的重点不是教大家如何使用华为云 OBS,所以这块到此为止。
我们开发的时候,最需要看的就是其开发文档,华为 OBS 开发文档的地址是:
![](https://static001.geekbang.org/infoq/89/89d0940c6122535d9bf91e91921dc974.png)
在此页面,不仅可以看到 OBS 产品的介绍,也能看到 OBS 产品如何使用、配置等,那我们要看的就是其中这两部分:
![](https://static001.geekbang.org/infoq/b1/b16afafb6ec7fcf8de720e202ef131f1.png)
所以在开发的时候,遇到问题,首先来这里看看文档再下手,这里不仅要华为 OBS 支持的功能,功能介绍,还有每种语言的例子,大多数都是直接拿来改改配置就能跑的。
项目背景介绍
最近我涉及的一个项目就是用到了华为 OBS 作为对象存储,我们有个网站存储的是各类全景图,每张全景图上传到华为 OBS 后会通过全景图所属组织、上传人进行分类,也就是目录管理:
![](https://static001.geekbang.org/infoq/5e/5eb92a743b3208475631c64c156f02aa.png)
结构就是这样,假如华为 OBS 下创建了三个全景图的桶,分别是:
全景图桶 1
全景图桶 2
全景图桶 3
每个桶下有多个组织的目录,比如这里是:
组织 1
组织 2
每个组织目录下又有多个上传人的目录,比如这里是:
上传人 A
上传人 B
上传人 C
上传人 D
每个上传人目录下又有多张全景图。
现在有个需求就是前端界面有个踢出组织的功能,一旦踢出这个组织,要求这个组织下所有的全景图全部删除。
有人会觉得万一这个组织是虚踢,也就是表面上踢了,但是数据库记录没有完全删除,只是标记了一下状态,这个时候其组织下的全景图还要清吗?
需求确定后,考虑了一下还是清,因为考虑到了 OBS 成本的问题,我们在购买 OBS 的时候会选择容量,而且每张全景图大小都很大,我看到的最小的也有 100M 左右的,而且既然是踢出的组织,肯定是没有价值了,那么其数据也没必要存在。
开始分析
确定需求后,我们肯定首先去 OBS 文档中搜索一下相关关键词:
![](https://static001.geekbang.org/infoq/26/267795bf5fdf0df38db862048340eec2.png)
我们在搜索框模糊搜索一下“目录”,从搜索的结果来看,没有看到我们想要的。
那么我们只能去文档中去找了,首先明确的是目录也是一种对象,那么删除目录及目录下的文件应该属于对象管理模块,带着这个信息我们翻文档:
![](https://static001.geekbang.org/infoq/ba/bada80ac15d14a973e875224e25ddf30.png)
我们看到支持两个操作:
删除对象
批量删除对象
到这里,我们确定了,OBS 是支持删除操作的,下一步要做的就是去找一下 sdk 例子,我用的是 java 语言,那么我们就去找一下,有没有删除目录的例子:
![](https://static001.geekbang.org/infoq/3a/3a03a711c91e63ae1441c7081d1532ca.png)
我们看到只有删除对象和批量删除对象的例子。
这个就很难受了,没有直接的例子,意味着这个操作不是调用一下 API 那么简单了,我们得要思考一下我们接下来应该怎么去写这个代码了。
源码分析
首先我们要明确的是这里使用的官方的 api 肯定是批量删除的 api,所以我们先去看下deleteObjects
方法中做了哪些操作:
![](https://static001.geekbang.org/infoq/fe/fe678edec1000878bdbed7c22d0aa640.png)
我们注意到 deleteObjects 没有重载函数,只有这一个。
deleteObjects 最终调用的是AbstractObjectClient.this.deleteObjectsImpl
方法,也就是父类中的deleteObjectsImpl
方法:
![](https://static001.geekbang.org/infoq/ef/efffa60d1b7b69c4ff948107ee71b016.png)
这个方法中将子类传过来的 DeleteObjectsRequest 对象就行一次性删除,那么下面的重点就是要分析一下这个 DeleteObjectsRequest 对象是什么样子的:
![](https://static001.geekbang.org/infoq/77/77240d093ec963d38ae2aaaa40a36193.png)
我们注意到除了默认构造函数外还有三个构造函数,我们挑个参数最多的看下:
![](https://static001.geekbang.org/infoq/38/382abfe1b883460b63bb2ec84bae045b.png)
bucketName:桶名
quiet:是否静默删除
keyAndVersions:将要删除的对象 key 数组
encodingType:对象 key 编码
我们注意到四个参数中,最重要的应该就是keyAndVersions
,所以我们继续挖源码:
![](https://static001.geekbang.org/infoq/fe/fe179d328d22e69732fcc4d5ae533d30.png)
终于看到了庐山真面目,keyAndVersions 对象就是一个 key 和 key 的版本,那么其实在我们的需求中,版本我们并不关心。
那么挖完这些源码,我们可以仔细思考一下,接下来应该怎么做??
我当时是这样分析的:
匹配要删除的目录即组织目录;
遍历出这个目录下所有的文件;
将遍历出来的文件取出它们的 key 组成一个列表;
将组装好的 keys 封装成 keyAndVersions 对象,然后进一步封装成 DeleteObjectsRequest 对象;
最后调用 deleteObjects 方法即可。
开始编码
经过上面的分析,我们已经明确自己应该怎么做了,那么下面就进入到编码环节:
1、匹配要删除的目录即组织目录
PanoController:
![](https://static001.geekbang.org/infoq/3c/3cfa5ad9f6ae389a1927f950c5b4a1ee.png)
大家可以不用关心我是怎么定义出 prefix 的,prefix 就是前缀,我们这里可以理解为你要删除的目录的根目录,比如组织 1.
2、遍历出这个目录下所有的文件
遍历目录下所有的文件,在 OBS 的文档是有的,我们来看下:
![](https://static001.geekbang.org/infoq/54/54ec8da600e7109e9675857bf5fdb9c3.png)
在“管理对象”中的“列举对象”模块中就有根据前缀列举目录的方法,我们来看下其代码示例:
![](https://static001.geekbang.org/infoq/76/76bc513a79d6eb08d2b54cc7a403a99a.png)
你看,这里的文档中也提到了,OBS 没有文件夹的概念,它把文件夹当作是对象了,所以我们终于能明白为啥删除这块没有删除对象的说法了,但是为啥有列举目录对象的方法示例而没有删除目录的方法示例呢,有点搞不明白官方的想法。
仔细看看这段代码,也非常简单,通过 ListObjectsRequest 对象,创建出列举某个桶下所有文件的对象,然后将目录名作为 prefix 前缀参数传入,最后再调用obsClient.listObjects
的方法即可。
还是非常简单的,那么我的代码也是这样写的:
3、将遍历出来的文件取出它们的 key 组成一个列表
![](https://static001.geekbang.org/infoq/4c/4cfedc0461663704bc9ae0f103ef3276.png)
最后一行代码的意思就是将文件夹下所有对象信息中 objectKey 重新组装成一个新的 list,这个 list 中都是 objectKey。
4、将组装好的 keys 封装成 keyAndVersions 对象,然后进一步封装成 DeleteObjectsRequest 对象
这个很简单,我们直接上代码:
5、最后调用 deleteObjects 方法
这个也比较简单,到最后一步:
至此删除文件夹的功能就写完了。
总结
在使用类似遵循开源的 S3 协议的产品的时候,我们可以多去看下本身这个开源协议支持哪些功能,顺着开源协议的功能,我们可以去推理自己使用的产品应该如何去实现自己想要的功能,有时候可能没有直接方法可以调用,这个时候我们需要结合几个方法去实现,在实现的过程中,我们可以将其封装成工具类。
版权声明: 本文为 InfoQ 作者【wljslmz】的原创文章。
原文链接:【http://xie.infoq.cn/article/da586ee3193c693d33396b737】。未经作者许可,禁止转载。
评论