AWS S3 の putObject API でバケットにファイルをアップロードするときには、メタデータをきちんと指定しておきましょうという話です。
既にメタデータが設定されてるファイルに上書きする場合、putObject API でメタデータを指定しないと、既に設定されていたメタデータは失われます。
AWS-CLIでコンソールからアップロードする場合はContent-Type
に適切なMIME Type を設定してくれるのですが、APIでは自分で指定しなければなりません。でないとすべて application/octet-stream
に設定されてしまいます。こうなるとバケットをWEBで公開している場合(CloudFrontのDistribution経由でも)、すべてがバイナリファイルの扱いになって、なんでもかんでもダウンロードされてしまいます。
他にも、ブラウザキャッシュを禁止しておきたい場合があると思いますが、これも上書きのたびに失われます(AWS-CLIでは、--cache-control
オプションに、no-cache
を指定する必要があります)。
まあ、とにかくAPIでputObjectする場合には必要なメタデータを毎回設定するべきなんですね。
ということで、以下のコードは、APIでS3にファイルをアップロードする際、同時にメタデータを設定するNode.jsのコードです(AWSへの接続はAWS-CLIのプロファイル(~/.aws/
以下の config
と credentials
)が正しくセットアップされている前提です)。
ここでは putObject のパラメータで、最低限設定しておきたい Content-Type
と、おそらく必要になるであろう Cache-Control
を指定しています。
"use strict"; const AWS = require("aws-sdk"); const mime = require("mime-types"); //拡張子からMIME Type const fs = require("promise-fs"); //地獄に落ちないfsモジュール const { promisify } = require("es6-promisify"); //callbackの非同期をPromise化 const s3 = new AWS.S3(); const promised = { s3: { putObject: promisify(s3.putObject.bind(s3)) } }; /** * ContentTypeとCacheControlを設定してS3のバケットへファイルを * アップロードする。 * @async * @param {string} bucket バケット名 * @param {string} key アップロード先のキー(バケット内のパス) * @param {string} pathname ローカルファイルのパス名 * @returns {Promise<undefined>} アップロード完了で解決するPromise */ const uploadS3Bucket = async (bucket, key, pathname) => { try { const body = await fs.readFile(pathname); const contentType = mime.lookup(pathname); const params = { Body: contentType.match(/^text\//) ? body.toString() : body, Bucket: bucket, Key: key, CacheControl: "no-cache", ContentType: contentType, }; console.log(`Uploading: ${pathname}`); console.log(` [ContentType: ${params.ContentType}]`); console.log(` ==> s3:${'//'}${params.Bucket}/${params.Key}`); await promised.s3.putObject(params); } catch(err) { console.warn(err.message); } };
Content-Type の特定は npm mime-type が使えます
ファイル名(拡張子)からMIME Typeを得るために、 mime-types
というnpmモジュールを使ってみました。
別の npm mime-db
に依存していますが、どちらも週に1500万回ほどダウンロードされていますから実用上の問題はないでしょう。
Cache-Control も設定したい
上のコードでは Cache-Control を no-cache に設定しています。 Webサイトとして公開している場合に、ブラウザキャッシュを無効にする設定です。 CloudFrontでInvalidationを作成してキャッシュを無効化しても、ブラウザキャッシュが効いているとページが更新されなくて「?」となることがありまして。
参考:S3のオブジェクトのメタデータ
下表は S3 Bucket のファイルに設定可能なメタデータです。 メタデータ列は S3 のWEBコンソールで表示される名称で、パラメータキーはputObjectのパラメータで指定する場合のキー名称です。
メタデータ | パラメータキー | 詳細 |
---|---|---|
Cache-Control | CacheControl |
ブラウザキャッシュの指定 (☞MDN) |
Content-Disposition | ContentDisposition |
ファイルの扱い方法を指定 (☞MDN) |
Content-Encoding | ContentEncoding |
圧縮アルゴリズムを指定 (☞MDN) |
Content-Language | ContentLanguage |
閲覧者の言語を指定 (☞MDN) |
Content-Type | ContentType |
ファイルのMIME Typeを指定 (☞MDN) |
Website-Redirect-Location | WebsiteRedirectLocation |
リダイレクト先 (☞DevelopersIO) |
x-amz-meta-<key> | Metadata.<key> |
ユーザー定義メタデータ (☞AWS) |
有効期限 | Expires |
削除される日時(☞AWS) |
※ ユーザー定義メタデータ以外は、APIであらかじめ定義されているシステムメタデータです。