试图通过aws listObjects调用找出怪异之处

时间:2019-10-08 16:32:03

标签: amazon-s3

当尝试围绕aws-sdk S3 listobjects方法构建函数时,我注意到一些奇怪的行为。具体来说,我正在尝试转换一个旧的unix文件系统工具,该工具具有包含每个客户ID的文件夹,用于他们上传的图像。

我已经写了一些工具,允许用户创建“目录”,这些目录基本上执行与Amazon S3 Web gui相同的操作,创建具有尾随定界符的对象。 (例如,“ myfolder /”,在这种情况下为用户名,例如“ 12345 / myfolder /”)

由于我们正在将现有图像迁移到该服务器,因此我们使用滚雪球将当前共置一台服务器上近10Tb的文件传输。我不确定此过程创建的项目是否不同,但是尝试检测目录时,我发现listObjects出现一些奇怪的行为。

在我的测试文件夹中,我有一个由雪球导入创建的子文件夹以及一个我创建的子文件夹。当我做一个listObjects时,我得到了一个带有尾部'/'分隔符的子文件夹的列表项,但是我没有得到一个带有尾部'/'的雪球子文件夹的列表项。

子文件夹中有一个对象,当我第一次注意到此行为时,我创建的文件夹没有。当我试图弄清楚发生了什么时,我尝试在params中指定一个Delimiter,但是后来我只看到对象(根本没有子文件夹)。因此,我正在研究一种使用给定前缀对整个列表进行正则表达式的方法,并尝试从其中包含。+ /。*的路径中解析出所有子文件夹,当我注意到我在“测试文件夹”下创建了一个子文件夹时,带有“文件中的文件”的条目以“ /”结尾(不同于雪球文件夹)

我知道,很难想象,所以让我给出一个基本的布局:

当我第一次注意到它时,这就是存在的文件:

12345/snowballdir/someimage.jpg
12345/test/

ListObjectsV2(无分隔符,前缀“ of 12345 /”)返回以下信息:

12345/snowballdir/someimage.jpg
12345/test/

现在我看到的是以下文件:

12345/snowballdir/someimage.jpg
12345/test/foo/someimage.jpg
12345/test/bar/
12345/test/anotherimage.jpg

获得结果:

12345/snowballdir/someimage.jpg
12345/test/
12345/test/foo/
12345/test/foo/someimage.jpg
12345/test/bar/
12345/test/anotherimage.jpg

注意:结果中不包含12345 / snowballdir /

如果listObjects中的目录是独立于其中创建的对象创建的,它是否仅显示目录?对于没有显示的内容,我有什么办法可以“解决”?

1 个答案:

答案 0 :(得分:0)

我今天重新审视了这个问题,因为在完成我们的用户界面(文件上传器/管理器)时,我发现如果用户删除由雪球导入创建的文件(其中“未”创建的空对象代表任何目录)在键名中定界),如果它是代表给定目录结构的最后一个对象,则该目录结构实际上将不复存在!

我使用SDK进行了一些测试,并证实了我的怀疑。如果我创建一个空对象来表示目录,然后在其中创建一个对象,则删除该对象不会删除带有尾随定界符的空对象所代表的目录。但是,如果我创建一个对象时没有先创建一个空的“目录”对象,则删除该对象会删除“目录路径”曾经存在的任何证据。

我还确认,您可以创建“空”目录表示形式,因为它不会影响键名中包含[pseudo]目录的任何对象。但是删除所有包含该“路径”的对象将不会删除该目录的存在。

所以看来,我要么必须在所有“删除”函数中创建变通办法,否则将检查它是否包含“目录”表示形式,并创建一个空对象来表示其文件夹(如果以前不存在)删除对象,否则我将不得不遍历整个系统,并为所有由于其键名而似乎位于“文件夹”中的对象创建空对象。

好的,如果AWS在某个地方进行记录,那就太好了。我没有在雪球文档中立即看到任何东西可以清楚地证明是这种情况。

其他一些后果:

可以在[伪]目录中创建表示文件的对象,例如:

myfolder/path/myfile.txt

然后创建另一个看起来与文件夹使用相同名称的对象:

myfolder/path/myfile.txt/anotherfile.txt

甚至可以创建[伪]目录占位符“

myfolder/path/myfile.txt/

还应注意,由于目录实际上不存在(除非您创建占位符以表示其存在),因此如果对象键中表示的路径很长,例如:

some/really/long/path/myfile.txt

如果您不隐式创建以下“目录”,则它们实际上并不存在:

some/
some/really/
some/really/long/
some/really/long/path/

这意味着如果删除该对象,则不包含占位符(或其他代表相同伪路径的对象)的“目录”的任何痕迹将不复存在!

还应注意,如果要删除表示为该伪树的子路径的任何内容,例如删除“ some / really / ”下的所有内容,则必须使用实用程序SDK函数:前缀为“ some / really” 或“ some / really /”或等效正则表达式的deleteMatchingObjects。仅对“ some / really /”执行“ deleteObject”将仅删除键为“ some / really”的任何现有占位符,而不会像上述示例那样跟踪和删除文件。

注意:不带斜杠的“ some / really”可能与其他文件匹配,例如“ some / reallybig.jpg”

SW

附录: 我没有经历整个存储桶为键名表示的每个伪目录创建占位符的过程,而是意识到唯一的潜在问题是删除包含在键名中的路径表示形式的对象。如果我随后在带有子键的对象代表子目录中的文件时测试并创建一个“父”目录,则可以保留这些文件夹的模拟,因为尝试删除在删除对象时创建的“父”目录将然后测试并创建其他任何父目录,直到键'path'中不再显示父目录为止。

以下功能允许您传递原始密钥,然后使用正则表达式查找任何子目录(假定使用unix“正斜杠”定界符):

/**
 * run prior to a delete to preserve any directories represented in the
 *   pathname due to the 'flat' nature of the S3 filesystem.
 * Checks if the 'key' represents a file in a 'pseudo' directory.
 *   If yes, it checks if an empty placeholder exists for the directory.
 *   If no, it creates one to preserve the directory in the bucket.
 * @param string $target
 * @param null|string $bucket
 * @return \Aws\Result|S3Exception|\Exception
 */
private function _preDeletePersistSubdir($target, $bucket=null) {
    if(preg_match('#^([^/]+.*/)[^/]+/?$#', $target, $m)) {
        $subDir = $m[1];
        try {
            if((!$this->getS3ClientCached()->doesObjectExist($bucket,$subDir))) {
                $params = array(
                    "Bucket" => $bucket,
                    "Key" => $subDir,
                    "Body" => ""
                );
                return $putDirResult = $this->getS3ClientCached()->putObject( $params );
            }
        } catch(S3Exception $e) { return $e; }
    }
}

类似地,您可以使用类似的函数递归构建整个树,将preg_match上的“ if”更改为“ while”,确保在每个循环结束时分配$ target = $ subDir:

/**
 * @param string $target
 * @param null|string $bucket
 * @return \Aws\Result|S3Exception|\Exception
 */
private function createSubdirPlaceholders($target, $bucket=null) {
    while(preg_match('#^([^/]+.*/)[^/]+/?$#', $target, $m)) {
        $subDir = $m[1];
        try {
            if((!$this->getS3ClientCached()->doesObjectExist($bucket,$subDir))) {
                $params = array(
                    "Bucket" => $bucket,
                    "Key" => $subDir,
                    "Body" => ""
                );
                return $putDirResult = $this->getS3ClientCached()->putObject( $params );
            }
        } catch(S3Exception $e) { return $e; }
        $target = $subDir;
    }
}