写点什么

开源一夏 | 如何使用 Java 操作华为对象存储 OBS 删除一个目录?

作者:wljslmz
  • 2022 年 8 月 15 日
    江苏
  • 本文字数:3356 字

    阅读完需:约 11 分钟

开源一夏 | 如何使用Java操作华为对象存储OBS删除一个目录?

华为 OBS 是华为云存储的一个产品,它众多对象存储产品中的一员,遵循亚马逊开源的 S3 协议,国内类似的产品还有阿里云的 OSS、腾讯云的 COS 等。我们通常会把非结构性数据存储到对象存储中,比如图片、视频等,那么管理这些对象就成了重重之重了。


在文章开始之前呢,先给大家介绍一下华为 OBS 的使用,华为 OBS 的官网地址是:


https://www.huaweicloud.com/product/obs.html
复制代码



一般使用的话,肯定是先购买:



在购买的界面,可以根据自己的需求进行定制化,然后立即购买就好。


购买后,可以点击“管理控制台”,进入控制台对我们购买的 OBS 产品进行配置管理。



由于本文的重点不是教大家如何使用华为云 OBS,所以这块到此为止。


我们开发的时候,最需要看的就是其开发文档,华为 OBS 开发文档的地址是:


https://support.huaweicloud.com/obs/index.html
复制代码



在此页面,不仅可以看到 OBS 产品的介绍,也能看到 OBS 产品如何使用、配置等,那我们要看的就是其中这两部分:



所以在开发的时候,遇到问题,首先来这里看看文档再下手,这里不仅要华为 OBS 支持的功能,功能介绍,还有每种语言的例子,大多数都是直接拿来改改配置就能跑的。

项目背景介绍

最近我涉及的一个项目就是用到了华为 OBS 作为对象存储,我们有个网站存储的是各类全景图,每张全景图上传到华为 OBS 后会通过全景图所属组织、上传人进行分类,也就是目录管理:



结构就是这样,假如华为 OBS 下创建了三个全景图的桶,分别是:


  • 全景图桶 1

  • 全景图桶 2

  • 全景图桶 3


每个桶下有多个组织的目录,比如这里是:


  • 组织 1

  • 组织 2


每个组织目录下又有多个上传人的目录,比如这里是:


  • 上传人 A

  • 上传人 B

  • 上传人 C

  • 上传人 D


每个上传人目录下又有多张全景图。


现在有个需求就是前端界面有个踢出组织的功能,一旦踢出这个组织,要求这个组织下所有的全景图全部删除。


有人会觉得万一这个组织是虚踢,也就是表面上踢了,但是数据库记录没有完全删除,只是标记了一下状态,这个时候其组织下的全景图还要清吗?

需求确定后,考虑了一下还是清,因为考虑到了 OBS 成本的问题,我们在购买 OBS 的时候会选择容量,而且每张全景图大小都很大,我看到的最小的也有 100M 左右的,而且既然是踢出的组织,肯定是没有价值了,那么其数据也没必要存在。

开始分析

确定需求后,我们肯定首先去 OBS 文档中搜索一下相关关键词:



我们在搜索框模糊搜索一下“目录”,从搜索的结果来看,没有看到我们想要的。


那么我们只能去文档中去找了,首先明确的是目录也是一种对象,那么删除目录及目录下的文件应该属于对象管理模块,带着这个信息我们翻文档:



我们看到支持两个操作:


  • 删除对象

  • 批量删除对象


到这里,我们确定了,OBS 是支持删除操作的,下一步要做的就是去找一下 sdk 例子,我用的是 java 语言,那么我们就去找一下,有没有删除目录的例子:



我们看到只有删除对象和批量删除对象的例子。


这个就很难受了,没有直接的例子,意味着这个操作不是调用一下 API 那么简单了,我们得要思考一下我们接下来应该怎么去写这个代码了。

源码分析

首先我们要明确的是这里使用的官方的 api 肯定是批量删除的 api,所以我们先去看下deleteObjects方法中做了哪些操作:



我们注意到 deleteObjects 没有重载函数,只有这一个。


deleteObjects 最终调用的是AbstractObjectClient.this.deleteObjectsImpl方法,也就是父类中的deleteObjectsImpl方法:



这个方法中将子类传过来的 DeleteObjectsRequest 对象就行一次性删除,那么下面的重点就是要分析一下这个 DeleteObjectsRequest 对象是什么样子的:



我们注意到除了默认构造函数外还有三个构造函数,我们挑个参数最多的看下:



  • bucketName:桶名

  • quiet:是否静默删除

  • keyAndVersions:将要删除的对象 key 数组

  • encodingType:对象 key 编码


我们注意到四个参数中,最重要的应该就是keyAndVersions,所以我们继续挖源码:



终于看到了庐山真面目,keyAndVersions 对象就是一个 key 和 key 的版本,那么其实在我们的需求中,版本我们并不关心。


那么挖完这些源码,我们可以仔细思考一下,接下来应该怎么做??


我当时是这样分析的:


  1. 匹配要删除的目录即组织目录;

  2. 遍历出这个目录下所有的文件;

  3. 将遍历出来的文件取出它们的 key 组成一个列表;

  4. 将组装好的 keys 封装成 keyAndVersions 对象,然后进一步封装成 DeleteObjectsRequest 对象;

  5. 最后调用 deleteObjects 方法即可。

开始编码

经过上面的分析,我们已经明确自己应该怎么做了,那么下面就进入到编码环节:

1、匹配要删除的目录即组织目录

PanoController:


    @ApiOperation("删除全景图")    @ApiImplicitParams({            @ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String", dataTypeClass = String.class),            @ApiImplicitParam(name = "asName", value = "场站名", required = true, dataType = "String", dataTypeClass = String.class)    })    @DeleteMapping(value = "delete")    public ResponseResult delete(@RequestParam("username") String username, @RequestParam("asName") String asName) {
if (!StringUtils.hasText(panoService.objectKey(username, asName))) { return ResponseResult.success(""); }
// 删除obs文件 String prefix = username + "-" + asName; DeleteObjectsResult deleteObjectsResult = obsService.deleteDir(obsConfig.getBucketName(), prefix);
if (OK.code() != deleteObjectsResult.getStatusCode()) { return ResponseResult.error("删除失败"); }
// 删除upload记录 Integer delete = panoService.delete(username, asName);
return delete > 0 ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败"); }
复制代码



大家可以不用关心我是怎么定义出 prefix 的,prefix 就是前缀,我们这里可以理解为你要删除的目录的根目录,比如组织 1.

2、遍历出这个目录下所有的文件

遍历目录下所有的文件,在 OBS 的文档是有的,我们来看下:



在“管理对象”中的“列举对象”模块中就有根据前缀列举目录的方法,我们来看下其代码示例:



你看,这里的文档中也提到了,OBS 没有文件夹的概念,它把文件夹当作是对象了,所以我们终于能明白为啥删除这块没有删除对象的说法了,但是为啥有列举目录对象的方法示例而没有删除目录的方法示例呢,有点搞不明白官方的想法。


仔细看看这段代码,也非常简单,通过 ListObjectsRequest 对象,创建出列举某个桶下所有文件的对象,然后将目录名作为 prefix 前缀参数传入,最后再调用obsClient.listObjects的方法即可。


还是非常简单的,那么我的代码也是这样写的:


ObsClient obsClient = obsConfig.getObsClient();ListObjectsRequest request = new ListObjectsRequest(bucketname);request.setPrefix(prefix);
ObjectListing result;
// 列举文件夹中所有的对象do { result = obsClient.listObjects(request); request.setMarker(result.getNextMarker());} while (result.isTruncated());
List<ObsObject> resultObjects = result.getObjects();List<String> objectKeys = resultObjects.stream().map(ObsObject::getObjectKey).collect(Collectors.toList());
复制代码

3、将遍历出来的文件取出它们的 key 组成一个列表


最后一行代码的意思就是将文件夹下所有对象信息中 objectKey 重新组装成一个新的 list,这个 list 中都是 objectKey。

4、将组装好的 keys 封装成 keyAndVersions 对象,然后进一步封装成 DeleteObjectsRequest 对象

这个很简单,我们直接上代码:


DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketname);objectKeys.forEach(deleteObjectsRequest::addKeyAndVersion);
复制代码

5、最后调用 deleteObjects 方法

这个也比较简单,到最后一步:


DeleteObjectsResult deleteObjectsResult = obsClient.deleteObjects(deleteObjectsRequest);
复制代码


至此删除文件夹的功能就写完了。

总结

在使用类似遵循开源的 S3 协议的产品的时候,我们可以多去看下本身这个开源协议支持哪些功能,顺着开源协议的功能,我们可以去推理自己使用的产品应该如何去实现自己想要的功能,有时候可能没有直接方法可以调用,这个时候我们需要结合几个方法去实现,在实现的过程中,我们可以将其封装成工具类。

发布于: 刚刚阅读数: 3
用户头像

wljslmz

关注

极致主义者,追求技术的路上,勇往直前! 2021.05.24 加入

订阅号:网络技术联盟站 个站:https://www.wljslmz.cn

评论

发布
暂无评论
开源一夏 | 如何使用Java操作华为对象存储OBS删除一个目录?_Java_wljslmz_InfoQ写作社区