[譯] Amazon S3 Delimiter and Prefix

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

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

  1. 如何使用參數 Delimiter 和 Prefix ?
  2. CommonPrefixes 與 Contents 的差別是什麼?
  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

但是什麼是 Delimeter 和 Prefix? 如何使用他們?

我們開始在 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, 你必須使用 Delimiter 和 Prefix. 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 被 di 和 fi 這兩個 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

由於分組字串/directory,directory/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 設成 directory 和 directory/ 是一樣的。
如果我們嘗試一些不一樣的, 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

也許改變 Delimeter 成 i 可以更好的理解:

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 的好處是你不需要顧慮巢狀的資料夾結構,
所有東西都是扁平化的。所以你可以藉由特定的 Prefix 來 loop 出你要的資料。

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/,結尾必須是斜線。此外,這只是一個檔案而不是資料夾。

評論