AWS CLIでS3オブジェクトのContentTypeを設定する

aws_logo
aws が提供するコマンドラインツールの aws cli を使って、S3オブジェクトのContentTypeを設定する方法をメモ。

aws s3 cp の場合

default

aws s3 cp コマンドを使うと、デフォルトでは ContentType が guess される。

$ aws s3 cp song.mp3 s3://02a8/song.mp3
upload: ./song.mp3 to s3://02a8/song.mp3
$ aws s3api head-object --bucket 02a8 --key song.mp3
{
    "LastModified": "Tue, 13 May 2014 15:19:57 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "audio/mpeg",
    "ContentLength": "0"
}

guess の仕様は Python 標準ライブラリー の mimetypes.guess_type に準拠。

guess 例

$ python
Python 2.7.3 (default, Aug  1 2012, 05:14:39)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mimetypes
>>> mimetypes.guess_type('foo.mp3')
('audio/mpeg', None)
>>> mimetypes.guess_type('foo.json')
('application/json', None)

ContentType の guess をやめる

--no-guess-mime-type をつけて cp する。

$ aws s3 cp --no-guess-mime-type song.mp3 s3://02a8/noguess.mp3
upload: ./song.mp3 to s3://02a8/noguess.mp3
$ aws s3api head-object --bucket 02a8 --key noguess.mp3
{
    "LastModified": "Tue, 13 May 2014 15:21:32 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "binary/octet-stream",
    "ContentLength": "0"
}

ContentType を明示的に指定

--content-type で明示的に指定して cp する。

$ aws s3 cp --content-type text/plain song.mp3 s3://02a8/text_plain.mp3
upload: ./song.mp3 to s3://02a8/text_plain.mp3
$ aws s3api head-object --bucket 02a8 --key text_plain.mp3
{
    "LastModified": "Tue, 13 May 2014 15:22:32 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "text/plain",
    "ContentLength": "0"
}

別バケットからコピーした時の挙動

コピー元がローカルのファイルシステムではなく、S3 オブジェクト(すでに Content-Type を持っている)だったらどうなるのか?

$ aws s3 cp --no-guess-mime-type s3://02a8/text_plain.mp3 s3://02a8/cp_noguess.mp3
copy: s3://02a8/text_plain.mp3 to s3://02a8/cp_noguess.mp3
$ aws s3api head-object --bucket 02a8 --key cp_noguess.mp3
{
    "LastModified": "Tue, 13 May 2014 15:54:04 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "text/plain",
    "ContentLength": "0"
}
$ aws s3 cp --content-type text/html s3://02a8/text_plain.mp3 s3://02a8/cp_text_html.mp3
copy: s3://02a8/text_plain.mp3 to s3://02a8/cp_text_html.mp3
$ aws s3api head-object --bucket 02a8 --key cp_text_html.mp3
{
    "LastModified": "Tue, 13 May 2014 15:54:14 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "text/plain",
    "ContentLength": "0"
}

--no-guess-mime-type オプションをつけようと --content-type オプションをつけようと、コピー元の ContentType が引き継がれる。

aws s3 sync の場合

動きは aws s3 cpの時と同じ。

  • sync 元がローカルのファイルシステムの場合、デフォルトで ContentType は guess され、--no-guess-mime-type--content-type で guess を上書きできる。
  • sync 元が S3 オブジェクトの場合(バケット間コピーとか)、コピー元の ContentType がそのまま引き継がれる。

上で 02a8 バケットに作成したオブジェクトを 02a9 バケットに aws s3 sync してみると

$ aws s3 sync s3://02a8 s3://02a9
copy: s3://02a8/copied_song.mp3 to s3://02a9/copied_song.mp3
copy: s3://02a8/song.mp3 to s3://02a9/song.mp3
copy: s3://02a8/cp_noguess.mp3 to s3://02a9/cp_noguess.mp3
copy: s3://02a8/text_plain.mp3 to s3://02a9/text_plain.mp3
copy: s3://02a8/copied_noguess.mp3 to s3://02a9/copied_noguess.mp3
copy: s3://02a8/cp_text_html.mp3 to s3://02a9/cp_text_html.mp3
copy: s3://02a8/noguess.mp3 to s3://02a9/noguess.mp3

$ aws s3api head-object --bucket 02a9 --key song.mp3
{
    "LastModified": "Tue, 13 May 2014 15:55:30 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "audio/mpeg",
    "ContentLength": "0"
}
$ aws s3api head-object --bucket 02a9 --key text_plain.mp3
{
    "LastModified": "Tue, 13 May 2014 15:55:30 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "text/plain",
    "ContentLength": "0"
}

というように、コピー元の ContentType がそのまま引き継がれている。

aws s3api put-object の場合

次に aws s3 cp コマンドのローレベル API である aws s3api put-object を触ってみる。
ローカルのファイルシステムにあるオブジェクトをコピーするためにつかう。
S3 の Put Object API の aws cli 対応版

デフォルト

$ aws s3api put-object --bucket 02a8 --key song.mp3 --body song.mp3
{
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
}
$ aws s3api head-object --bucket 02a8 --key song.mp3
{
    "LastModified": "Tue, 13 May 2014 16:02:17 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "binary/octet-stream",
    "ContentLength": "0"
}

デフォルトでは ContentType は guess されない。

ContentType を指定

--content-type をつけて ContentType を指定する。

$ aws s3api put-object --body song.mp3 --bucket 02a8 --key audio_mpeg.mp3 --content-type audio/mpeg
{
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
}
$ aws s3api head-object --bucket 02a8 --key audio_mpeg.mp3
{
    "LastModified": "Tue, 13 May 2014 16:03:35 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "audio/mpeg",
    "ContentLength": "0"
}

copy-object の場合

次に aws s3 cp コマンドのもう一つのローレベル API である aws s3api copy-object を触ってみる。

S3 にあるオブジェクトをコピーするためにつかう。
S3 の Put Object – Copy API の aws cli 対応版

<pre>$ aws s3api head-object --bucket 02a8 --key audio_mpeg.mp3
{
    "LastModified": "Tue, 13 May 2014 16:23:13 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "audio/mpeg",
    "ContentLength": "0"
}
$ aws s3api copy-object --copy-source 02a8/audio_mpeg.mp3 --bucket 02a8 --key copied_audio_mpeg.mp3
{
    "CopyObjectResult": {
        "LastModified": "2014-05-13T16:36:57.000Z",
        "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
    }
}
$ aws s3api head-object --bucket 02a8 --key copied_audio_mpeg.mp3
{
    "LastModified": "Tue, 13 May 2014 16:36:57 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "audio/mpeg",
    "ContentLength": "0"
}

デフォルトでは、コピー元の ContentType が引き継がれる。

ContentType を指定

--content-type をつけて ContentType を指定してみる。

$ aws s3api copy-object --copy-source 02a8/song.mp3 --bucket 02a8 --key copied_text_html.mp3 --content-type text/html
{
    "CopyObjectResult": {
        "LastModified": "2014-05-13T16:06:01.000Z",
        "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
    }
}
$ aws s3api head-object --bucket 02a8 --key copied_text_html.mp3                                                 {
    "LastModified": "Tue, 13 May 2014 16:06:01 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "binary/octet-stream",
    "ContentLength": "0"
}

なぜか ContentType が “text/html” に変わっていない。

原因は、コピー元が S3 オブジェクトの場合、 --metadata-directive を REPLACE にしないとコピー元のメタデータが引き継がれるため。(デフォルトのモードは COPY でメタデータが引き継がれる)
詳細は PUT Object – Copy API x-amz-metadata-directive ディレクティブの説明を参照

--metadata-directive REPLACE をつけて ContentType を指定してみる。

$ aws s3api copy-object --copy-source 02a8/song.mp3 --bucket 02a8 --key copied_metadata_replace.mp3 --content-type text/html  --metadata-directive REPLACE
{
    "CopyObjectResult": {
        "LastModified": "2014-05-13T16:07:14.000Z",
        "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
    }
}
$ aws s3api head-object --bucket 02a8 --key copied_metadata_replace.mp3                                          {
    "LastModified": "Tue, 13 May 2014 16:07:14 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "text/html",
    "ContentLength": "0"
}

期待どおり ContentType が変わった。

S3 オブジェクトの ContentType だけを更新する

最終目標にやりたかったのがこれ。

初期データの投入方法がまずくて、ContentType を付け直したいときにどうすればよいのか?
残念なことに AWS S3 の RestAPI にはそんな都合のよい API は存在しない。

自分自身にコピーして、ContentType を上書きすれば OK

“ContentType”: “binary/octet-stream” な s3://02a8/song.mp3 の ContentType を “audio/mpeg” に変えてみる。

$ aws s3api head-object --bucket 02a8 --key song.mp3
{
    "LastModified": "Tue, 13 May 2014 16:02:17 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "binary/octet-stream",
    "ContentLength": "0"
}
$ aws s3api copy-object --bucket 02a8 --copy-source 02a8/song.mp3 --key song.mp3 --content-type audio/mpeg --metadata-directive REPLACE
{
    "CopyObjectResult": {
        "LastModified": "2014-05-13T16:08:54.000Z",
        "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
    }
}
$ aws s3api head-object --bucket 02a8 --key song.mp3                                                             {
    "LastModified": "Tue, 13 May 2014 16:08:54 GMT",
    "AcceptRanges": "bytes",
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "audio/mpeg",
    "ContentLength": "0"
}

ということで無事更新出来た。
めでたし、めでたし。

One thought on “AWS CLIでS3オブジェクトのContentTypeを設定する

Leave a comment