如何使用Java操作华为对象存储OBS删除一个目录?【玩转华为云】

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

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

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

https://www.huaweicloud.com/product/obs.html

华为OBS官网

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

华为OBS购买界面

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

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

华为OBS管理控制台

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

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

https://support.huaweicloud.com/obs/index.html

华为OBS文档界面

在此页面,不仅可以看到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协议的产品的时候,我们可以多去看下本身这个开源协议支持哪些功能,顺着开源协议的功能,我们可以去推理自己使用的产品应该如何去实现自己想要的功能,有时候可能没有直接方法可以调用,这个时候我们需要结合几个方法去实现,在实现的过程中,我们可以将其封装成工具类。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。