从控制台与 CLI 创建时如何确保 S3 前缀相同?

How to ensure S3 prefixes are same when created from console vs CLI?

我想在 S3 中创建以下文件夹结构,

/demo/app/a.txt

通过控制台:

demoapp 使用“创建文件夹”,然后 a.txt 文件上传

通过 CLI:

aws s3 sync . s3://<my-bucket>/,其中 .当前目录)有 demo 个文件夹

--

现在当我运行,

aws s3 ls s3://<my-bucket> --recursive,结果相当interesting/puzzling!

输出:

# created & uploaded from console
demo/
demo/app/
demo/app/a.txt

# from CLI
demo/app/a.txt

显然,demo/demo/app/ 没有任何 prefixes/objects 使用 CLI 上传创建。

--

帮助我理解了当文件夹是空的。但是通过 CLI 上传时就不是这样了。

那么,如何模仿 CLI 上传的 0 字节 文件行为?也就是说,匹配前缀!

另一个用例是,由于前缀不可用,无法从浏览器浏览目录。例如

  1. 从控制台上传:https://<domain>/demo/ 显示 app 目录。
  2. 从 CLI 上传:https://<domain>/demo/ 结果为 NoSuchKeyError

--

注意:我正在使用 CloudFront 分发来访问 S3 数据(如果有帮助的话)!

Amazon S3 是一个平面对象存储系统。它不是文件系统,也没有文件夹或目录的概念。相反,对象的 Key(文件名)包含对象的 完整路径

使用 S3 的最简单方法是假装文件夹存在,但实际上并不创建它们。例如,您可以像这样将文件复制到 S3:

aws s3 cp a.txt s3://my-bucket/demo/app/a.txt

这将成功运行,即使没有名为 demoapp 的目录,因为 directories/folders 不存在 .

相反,Amazon S3 提供了 CommonPrefix 的概念,您将在 ListObjects() API 调用的底部看到它。此 returns 由 分隔符 (通常为 /)分隔的类似文件夹的名称列表。这提供了目录的编程等效性,而不需要它们实际存在。

如果您希望呈现一系列分层目录,请使用 CommonPrefixes 的列表来构建该视图。即使没有零长度文件,这也会起作用,因为 S3 查看对象的键,而不是实际目录。

这里有一些例子:

aws s3 cp a.txt s3://my-bucket/demo/app/a.txt
upload: ./a.txt to s3://my-bucket/demo/app/a.txt                  

aws s3api list-objects-v2 --bucket my-bucket

{
    "Contents": [
        {
            "Key": "demo/app/a.txt",
            "LastModified": "2020-04-22T01:11:20+00:00",
            "ETag": "\"802776735eb3ddcf03962ae47e08ed13\"",
            "Size": 211,
            "StorageClass": "STANDARD"
        }
    ]
}

aws s3api list-objects-v2 --bucket my-bucket --delimiter '/'

{
    "CommonPrefixes": [
        {
            "Prefix": "demo/"
        }
    ]
}

aws s3api list-objects-v2 --bucket jstack-b --delimiter '/' --prefix 'demo/'
{
    "CommonPrefixes": [
        {
            "Prefix": "demo/app/"
        }
    ]
}

请注意末尾的命令如何提供 delimiter,因此会返回 CommonPrefixes 的列表。您可以通过这种方式遍历目录列表(不存在)。

如果您确实需要在 S3 中存在一个反映本地文件夹结构的文件夹结构,那么您可以尝试以下 shell 脚本和 Linux 或 [=45 上的 awscli 的一些变体=]:

  1. 列出所有本地文件夹
  2. 将该列表转换为与 S3 命名约定匹配的内容
  3. 在 S3 中的每个文件夹名称处创建一个零字节文件

列出本地文件夹:

find . -type d

示例输出:

.
./dogs
./dogs/cute
./dogs/fierce
./cats
./cats/white
./cats/black

转换该列表以删除 .,以便 ./dogs/cute 变为 dogs/cute:

find . -type d | grep -v '^\.$' | sed 's/^\.\///g'

示例输出:

dogs
dogs/cute
dogs/fierce
cats
cats/white
cats/black

最后将它们放在一起并在每个文件夹中创建零字节文件:

find . -type d \
    | grep -v '^\.$' \
    | sed 's/^\.\///g' \
    | xargs -L 1 -I % \
    aws s3api put-object --bucket mybucket --key %/ --content-length 0

检查 S3 中的结果:

aws s3 ls s3://mybucket --recursive

结果:

2020-04-21 21:00:05          0 cats/black/
2020-04-21 21:00:05          0 cats/white/
2020-04-21 21:00:04          0 cats/
2020-04-21 21:00:03          0 dogs/cute/
2020-04-21 21:00:04          0 dogs/fierce/
2020-04-21 21:00:02          0 dogs/