【问题标题】:trying to figure out weirdness with aws listObjects call试图通过 aws listObjects 调用找出奇怪之处
【发布时间】:2020-02-05 23:39:48
【问题描述】:

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

我已经编写了允许用户创建“目录”的工具,这与 amazon S3 web gui 所做的基本相同,即创建一个带有尾部分隔符的对象。 (例如“myfolder/”,在这种情况下,在用户 ID 下,例如“12345/myfolder/”)

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

在我的测试文件夹中,我有一个由雪球导入创建的子文件夹和一个我创建的子文件夹。当我执行 listObjects 时,我会得到一个带有尾随 '/' 分隔符的子文件夹的列表项,但我没有得到一个带有尾随 '/' 的雪球子文件夹的列表项。

子文件夹中有一个对象,当我第一次注意到这种行为时,我创建的文件夹没有。当我试图弄清楚发生了什么时,我尝试在参数中指定一个分隔符,但后来我只看到了对象(根本没有子文件夹)。因此,我正在研究一种方法来正则表达式具有给定前缀的完整列表,并尝试从路径中解析任何子文件夹有一个文件,其中有一个以“/”结尾的条目(与雪球文件夹不同)

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

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

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 中的目录是独立于其中创建的对象而创建的,它是否只显示目录?有什么办法可以“修复”那些没有显示的问题吗?

【问题讨论】:

    标签: amazon-s3


    【解决方案1】:

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

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

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

    看来,要么我必须在任何和所有“删除”函数中创建一个解决方法,该函数将检查它是否包含“目录”表示,并创建一个空对象表示它的文件夹(如果它之前不存在)删除该对象,否则我将不得不遍历整个系统并为由于其键名而出现在“文件夹”中的任何和所有对象创建空对象。

    呃,如果 AWS 在某个地方记录了这一点,那就太好了。我没有立即在雪球文档中看到任何明确说明这种情况的内容。

    其他一些后果:

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

    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

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

    以下函数允许您传递原始密钥,然后使用正则表达式查找任何子目录(假设使用 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;
        }
    }
    

    【讨论】:

    • 用你的 createS3() 值替换 $this->getS3ClientCached。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    相关资源
    最近更新 更多