[小ネタ]各S3クライアントからキーの末尾が`/`のオブジェクトがどう見えるか

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

S3のオブジェクトのキー末尾にスラッシュを指定した場合に、クライアントによってオブジェクトの見え方が異なっていてちょっとハマったのでまとめました。

ざっくり言ってしまえば、s3://cm-xxxx-bucket/test.json/のようなキーの末尾にスラッシュを指定したオブジェクトをアップロードした際に、クライアントによって

  • 通常のオブジェクトとして扱ってくれるもの
  • ディレクトリ扱いとなるもの

があったので、本記事はいくつかのクライアントで試してみた結果です。

以下のクライアントで試してみました。

  • AWSのマネジメントコンソール
  • CLI(aws-cli/1.11.139 Python/2.7.10 Darwin/17.4.0 botocore/1.8.46)
    • s3
    • s3api
  • REST API
  • Python SDK(boto3 (1.5.32))
  • Java SDK(1.10.60)
  • Node.js SDK(5.6.0)
  • Cyberduck(6.4.0)

末尾がスラッシュのオブジェクトはPythonのSDKでアップロードしました。

import json
import boto3

bucket_name = "cm-xxxx-bucket"
object_key = "test.json/"
s3 = boto3.resource('s3')
obj = s3.Object(bucket_name,object_key)

test_json = {'key': 'value'}
r = obj.put(Body = json.dumps(test_json))

※オブジェクトのキーの末尾をスラッシュにしています。test.json/

参考:Python(boto3)でS3にデータをファイル保存せず直接アップロードする方法

クライアントからみてみる

通常のオブジェクトとして扱ってくれるもの

SDK

import json
import boto3

bucket_name = "cm-xxxx-bucket"
json_key = "test.json/"
s3 = boto3.resource('s3')
obj = s3.Object(bucket_name,json_key)
print obj.get()['Body'].read()

末尾がスラッシュのキーを指定してオブジェクトを取得します。

$ python test.py
{"key": "value"}

取得できました。

Java, Node.jsも同様に取得可能だったのでコードなど割愛します。おそらく他のSDKも同様の動きとなるのではないかと思います。

REST API

$ curl s3.us-west-2.amazonaws.com/cm-xxxx-bucket/test.json/
{"test":"value"}

REST API経由でもオブジェクトを取得できるようです。S3そのものは純粋なKVSで特に末尾のスラッシュを意識しないんだと思います。

CLI(s3api)

s3s3apiでCLIの挙動は異なるとのコメントを頂きs3apiについて追記致しました。 ご指摘ありがとうございます。

aws s3api get-object --bucket cm-xxxx-bucket --key test.json/ test.json
{
    "AcceptRanges": "bytes",
    "ContentType": "application/json",
    "LastModified": "Tue, 20 Feb 2018 08:36:51 GMT",
    "ContentLength": 16,
    "ETag": "\"1c623102e25ffbd59a0e5709c503902e\"",
    "Metadata": {}
}
$ cat test.json
{"test":"value"}

こちらではダウンロード可能でした。(後述のs3ではエラー。)

ディレクトリ扱いとなるもの

AWSのマネジメントコンソール

マネジメントコンソール上ではtest.jsonという名前のディレクトリがあるような見え方になります。 test.json配下には何もありません。

CLI(s3)

ダウンロードに失敗しました。

$ aws --version
aws-cli/1.11.139 Python/2.7.10 Darwin/17.4.0 botocore/1.8.46
$ aws s3 cp --region us-west-2 s3://cm-xxxx-bucket/test.json/ .
download failed: s3://cm-xxxx-bucket/test.json/ to ./ [Errno 21] Is a directory

Cyberduck

空のディレクトリ扱いになります。

このディレクトリをtest.jsonという名前でデスクトップにダウンロードしたらデスクトップにtest.jsonという空のディレクトリが作成されました。

まとめ

キーの末尾がスラッシュのオブジェクトをS3にアップロードすると、クライアントによってはオブジェクトを空のディレクトリ扱いします。

このようなクライアントからオブジェクトを見ると、「アップロードに失敗、もしくは空のオブジェクトがアップロードされた」ような感じがしますが、実際にはオブジェクトが正常にアップロードできています。

通常のオブジェクト扱いとなるもの

  • REST API
  • Python SDK(boto3 (1.5.32))
  • Java SDK(1.10.60)
  • Node.js SDK(5.6.0)
  • CLIのs3api(aws-cli/1.11.139 Python/2.7.10 Darwin/17.4.0 botocore/1.8.46)

ディレクトリ扱いするもの

  • AWSのマネジメントコンソール
  • CLIのs3(aws-cli/1.11.139 Python/2.7.10 Darwin/17.4.0 botocore/1.8.46)
  • Cyberduck(6.4.0)

Amazon S3 ではバケットとオブジェクトをサポートし、Amazon S3 には階層がありません。ただし、オブジェクトキー名のプレフィックスと区切り記号により、Amazon S3 コンソールや AWS SDK で階層を暗示し、フォルダの概念を導入できます。 引用元:オブジェクトキーとメタデータ - Amazon Simple Storage Service

上記の通りS3自体には階層の概念がありませんが、クライアント側でキー内のスラッシュにて階層を表現した結果、キーの末尾がスラッシュの実データのあるオブジェクトが空のディレクトリのように見えている、という状況かと思います。

おそらくS3そのものは純粋なKVSで、末尾がスラッシュかどうかは特に意識しないんですが、一部のクライアントは使いやすさのためにスラッシュを「ディレクトリのデリミタ」と認識しているようです。 ※ご指摘いただき上記記載を修正いたしました。

参考:Amazon S3における「フォルダ」という幻想をぶち壊し、その実体を明らかにする

ちなみにハマった経緯は「アプリケーションがアップロードしたキーの末尾がスラッシュのオブジェクトを、マネジメントコンソールから参照して、アップロードに失敗しているのでは?と判断し、失敗してるのにアプリケーションはSDK経由でオブジェクトを取得できていているように見えた」です。

そもそもそうする必要がないのであれば末尾をスラッシュにしないのが良いかと思います。

私からは以上です。