[譯] Amazon S3 Delimiter and Prefix

From: Amazon S3 Delimiter and Prefix - realguess
posted at 2014/05/24

AWS S3 是一個不昂貴的線上檔案儲存服務,我們可以透過 javascript SDK 來使用 S3。當我使 SDK 的時候有以下幾件令人疑惑的事情:

  1. 如何使用參數 DelimiterPrefix ?
  2. CommonPrefixesContents 的差別是什麼?
  3. 如何透過 Javascript SDK 建立 folder / directory

取回在 Amazon S3 Bucket 的物件得操作 (operation) 是 listObject. listObjects 不會回傳 (return) 物件的內容, 而會回傳 key 與 meta data 像是物件的大小 (size) 或是物件的擁有者。

使用指令呼叫以取得 bucket 中的物件清單:

1
2
3
s3.listObjects(params, function (err, data) {
// ...
});

params 可以設置以下的參數:

  • Bucket
  • Delimiter
  • EncodingType
  • Marker
  • MaxKeys
  • Prefix

但是什麼是 DelimeterPrefix? 如何使用他們?

我們開始在 S3 中建立一些物件,格式如下方的檔案結構。這可以用 Aws Console 很快的完成。

1
2
3
4
5
6
7
.
├── directory
│ ├── directory
│ │ └── file
│ └── file
└── file
2 directories, 3 files

在 Amazon S3 中,這些物件為:

1
2
3
4
5
directory/
directory/directory/
directory/directory/file
directory/file
file

一個需要留心的事情是 S3 不是檔案系統。S3 並沒有真正的檔案或是資料夾的概念。
從 Console 中看到的是 2 個資料夾和 3 個檔案。但是它們全部都是物件。這些物件
會用他們的 Key 值按照字母排序。

為了讓概念更清楚點,我們來調用 (invoke) 方法 listObjects。而這個操作只需要參數 Bucket

1
2
3
params = {
Bucket: 'example'
};

Response data 包含下列的 callback function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{ Contents:
[ { Key: 'directory/',
LastModified: ...,
ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
Size: 0,
Owner: [Object],
StorageClass: 'STANDARD' },
{ Key: 'directory/directory/',
LastModified: ...,
ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
Size: 0,
Owner: [Object],
StorageClass: 'STANDARD' },
{ Key: 'directory/directory/file',
LastModified: ...,
ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
Size: 0,
Owner: [Object],
StorageClass: 'STANDARD' },
{ Key: 'directory/file',
LastModified: ...,
ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
Size: 0,
Owner: [Object],
StorageClass: 'STANDARD' },
{ Key: 'file',
LastModified: ...,
ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
Size: 0,
Owner: [Object],
StorageClass: 'STANDARD' } ],
CommonPrefixes: [],
Name: 'example',
Prefix: '',
Marker: '',
MaxKeys: 1000,
IsTruncated: false }

如果有檔案結構,你會期待是:

1
2
directory/
file

但是並沒有,因為 bucket 運作的方式不像是 folder 或是 directory, 能夠顯示
directory 中的檔案。檔案在 bucket 中會全部攤開來並且照字母排列。

在 Unit 中, directory 是檔案, 但是在 Amazon S3 中, 所有的東西都是物件,
並且他們用 key 來識別。

所以,該如何讓 Amazon S3 表現的更像是 folder 或是 directory? 或如何顯示 Bucket 中的第一層右邊的內容?

為了讓 S3 顯示的更像是 director, 你必須使用 DelimiterPrefix. Delimeter 讓你可以為 key 分組 (use to group keys)。它必須是一個字元或者一個字串。Prefix 限制 response 開始於特別的 prefix。

Delimiter

我們開始加入下列的 delimiter:

1
2
3
4
params = {
Bucket: 'example',
Delimiter: '/'
};

你會取得像是 directory 清單的一些資訊:

1
2
3
4
5
6
7
8
{ Contents:
[ { Key: 'file' } ],
CommonPrefixes: [ { Prefix: 'directory/' } ],
Name: 'example',
Prefix: '',
MaxKeys: 1000,
Delimiter: '/',
IsTruncated: false }

有一個資料夾叫做 directory/ 和一個檔案叫做 file. 發生的事情是下列的物件接受檔案被 delimeter / 分組。

1
2
3
4
5
directory/
directory/directory/
directory/directory/file
directory/file
file

所以,結果是:

1
2
directory/
file

這感覺像是 directory 或是 folder 的清單。但如果我們把 Delimeter 改成 i, 接著你會取得空的 Contents 和 prefix。

1
2
3
4
5
6
7
{ Contents: [],
CommonPrefixes: [ { Prefix: 'di' }, { Prefix: 'fi' } ],
Name: 'example',
Prefix: '',
MaxKeys: 1000,
Delimiter: 'i',
IsTruncated: false }

所有的 key 被 difi 這兩個 prefix 分組。因此,Amazon S3 不是檔案系統, 但是他們如果可以設定對的參數,那他們看起來就會像是檔案系統。

如之前提到的 Delimeter 不需要是單一的字元:

1
2
3
4
5
6
7
8
9
10
{ Contents:
[ { Key: 'directory/' },
{ Key: 'directory/file' },
{ Key: 'file' } ],
CommonPrefixes: [ { Prefix: 'directory/directory' } ],
Name: 'example',
Prefix: '',
MaxKeys: 1000,
Delimiter: '/directory',
IsTruncated: false }

回想 bucket 的結構:

1
2
3
4
5
directory/
directory/directory/
directory/directory/file
directory/file
file

由於分組字串/directorydirectory/directory/directory/directory/file 被 prefix directory/directory 分組了。

我們來試試其他的 Delimiter: 'directory'

1
2
3
4
5
6
7
8
{ Contents:
[ { Key: 'file' } ],
CommonPrefixes: [ { Prefix: 'directory' } ],
Name: 'example',
Prefix: '',
MaxKeys: 1000,
Delimiter: 'directory',
IsTruncated: false }

好,接著來試試 ry/fi

1
2
3
4
5
6
7
8
9
10
11
12
{ Contents:
[ { Key: 'directory/' },
{ Key: 'directory/directory/' },
{ Key: 'file' } ],
CommonPrefixes:
[ { Prefix: 'directory/directory/fi' },
{ Prefix: 'directory/fi' } ],
Name: 'example,
Prefix: '',
MaxKeys: 1000,
Delimiter: 'ry/fi',
IsTruncated: false }

所以記得 Delimeter 只是提供將 key 分組的功能。如果你想要他們表現的像是
檔案系統,那麼使用 Delimiter: '/'

Prefix

Prefix 更易於理解。它利用前綴來過濾(filter) keys。

相同的結構:

1
2
3
4
5
directory/
directory/directory/
directory/directory/file
directory/file
file

我們來設定 Prefix 參數值為 directory:

1
2
3
4
5
6
7
8
9
10
{ Contents:
[ { Key: 'directory/' },
{ Key: 'directory/directory/' },
{ Key: 'directory/directory/file' },
{ Key: 'directory/file' } ],
CommonPrefixes: [],
Name: 'example',
Prefix: 'directory',
MaxKeys: 1000,
IsTruncated: false }

看看設定成 directory/ 會怎麼樣:

1
2
3
4
5
6
7
{ Contents:
[ { Key: 'directory/' },
{ Key: 'directory/directory/' },
{ Key: 'directory/directory/file' },
{ Key: 'directory/file' } ],
CommonPrefixes: [],
Prefix: 'directory/' }

prefix 設成 directorydirectory/ 是一樣的。
如果我們嘗試一些不一樣的, Prefix: 'directory/d':

1
2
3
4
5
{ Contents:
[ { Key: 'directory/directory/' },
{ Key: 'directory/directory/file' } ],
CommonPrefixes: [],
Prefix: 'directory/d' }

同時使用 Delimiter: 'directory' and Prefix: 'directory':

1
2
3
4
5
6
{ Contents:
[ { Key: 'directory/' },
{ Key: 'directory/file' } ],
CommonPrefixes: [ { Prefix: 'directory/directory' } ],
Prefix: 'directory',
Delimiter: 'directory' }

第一步,列出 prefix 是 directory 的清單:

1
2
3
4
directory/
directory/directory/
directory/directory/file
directory/file

用 delimiter directory 與 prefix directory 來分組:

1
directory/directory

結果的 Contents 會是:

1
2
directory/
directory/file

CommonPrefixes 會是:

1
directory/directory

也許改變 Delimeteri 可以更好的理解:

1
2
3
4
5
{ Contents:
[ { Key: 'directory/' } ],
CommonPrefixes: [ { Prefix: 'directory/di' }, { Prefix: 'directory/fi' } ],
Prefix: 'directory',
Delimiter: 'i' }
1
2
3
4
5
directory/               # key to show
directory/directory/ # group to 'directory/di'
directory/directory/file # group to 'directory/di'
directory/file # Group to 'directory/fi'
file # ignored due to prefix

一個使用 Amazon S3 來列出 directory 的好處是你不需要顧慮巢狀的資料夾結構,
所有東西都是扁平化的。所以你可以藉由特定的 Prefixloop 出你要的資料。

Directory / Folder

如果你使用 Amazon AWS console 來 Create Folder,你可以建立資料夾或是
上傳檔案在該資料夾中。實際上,你使用了下列的 key 建立了兩個物件:

1
2
directory/
directory/file

如果你使用下列指令來上傳檔案,資料夾不會被建立:

1
aws s3 cp file s3://example/directory/file

因為,Amazon S3 不是檔案系統,而是 key/value 的儲存空間。如果你使用 listObjects 方法,你可以看到一個物件。
同理,你無法複製本地的資料夾:

1
2
$ aws s3 cp directory s3://example/directory
upload failed: aws/ to s3://example/directory [Errno 21] Is a directory: u'/home/chao/tmp/directory/'

但我們可以用 JavaScript SDK 來建立 directory/folder:

1
2
3
4
s3.putObject({ Bucket: 'example', Key: 'directory/' }, function (err, data) {
if (err) { return console.error(err); }
console.log(data);
});

需要筆記的地方是: 你必須使用 directory/,結尾必須是斜線。此外,這只是一個檔案而不是資料夾。

評論