AWS S3 の putObject API でメタデータを設定する
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であらかじめ定義されているシステムメタデータです。
リンク
mochaとBabelでESモジュールをテストする
npm内のESモジュールをmochaでテストしようとしたのですが、上手くいかない。 Babelが必要なんですね。mochaのテストスクリプトをbabelで変換しないと import / export が構文エラーになってしまうのです。
事前に変換しなくても、@babel/register を使ってmocha実行時に変換しながらテストできるらしいのですが、 そのやり方を調べてみると、断片的な情報がバラバラとある状態で「ココだけ見てやったらバッチリOK」ってな情報源には出会えずじまい。 あっちやこっちを見ながら最終的には何とかなりましたが、結構苦労したので備忘録としてまとめてここに書いておきます。
前提条件
- 使用するBabelのバージョンは7。
- テストランナーは mocha。
- テストはnode.jsで動かします(ブラウザで行うテストじゃないです)。
- テスト対象はnpm内の ESモジュールのクラスです。
必要なモジュール
上記の変換を行う為に最低限必要なモジュールは以下4つ。 各モジュールのバージョンは現在確認したワタシの環境のものです。おそらく現時点での最新バージョン。
- @babel/core - 7.3.4
- @babel/register - 7.0.0
- babel-preset-env - 1.7.0
- mocha - 6.0.2
飽くまでも最低限のモジュールです。ほかに必要になるモジュールがあるかもしれません。 例えばワタシはアサーションにchaiを使いますし、分割代入などを使う場合はまた別のが必要になったりするんじゃないかなと思います。
npmに上記モジュールをインストールするには以下のコマンドでOK。
npm i -D @babel/core @babel/register babel-preset-env mocha
注意:ここに上げた最低限のモジュールは、次項の .babelrc
の記述内容に依存しているかも知れません。
そういう意味で、この記事は「こうやればできた」に過ぎないかもしれないので注意してください。
今のところこれ以上深堀をしていないので。
Babel の設定
Babelの変換を定義する .babelrc も必要です。
babel-preset-env
を指定しています。
このため依存モジュールにbabel-preset-env
が必要だったのだと思います。
.babelrc
{ "presets": [ [ "env", { "targets": { "node": true }, "useBuiltIns": true } ] ] }
売り上げランキング: 5,755
テスト対象のESモジュールとテストスクリプト例
テスト対象のESモジュール
src/my-class.js
"use strict"; /** * MyClass. * @constructor */ export function MyClass() {} /** * bar. * @returns {string} "foo" を返す */ MyClass.prototype.bar = function() { return "foo"; };
mochaでESモジュールをテストするスクリプト
test/my-class.test.js
※ 以下、chaiを使っているので、npm i -D chai
が必要ですよ。
"use strict"; import { assert } from "chai"; import { MyClass } from "../src/my-class.js"; describe("MyClass", () => { describe("#bar", () => { it("should returns 'foo'", () => { assert.equal( "foo", (new MyClass()).bar() ); }); }); });
mocha実行時のオプション
mocha実行時に変換するためには、以下のように --require
オプションで @babel/register
を指定します。
mocha --require @babel/register test/**/*.test.js
テスト対象はnpmなので、package.json のscripts を以下のように設定しておけば、npm test
でテスト実行できますね。
{ ~略~ "scripts": { "test": "mocha --require @babel/register test/**/*.test.js" } ~略~ }
npmパッケージのダウンロード数をアカウント別に一括取得
Nervously Peeking Much Because I Love NPM
自分で公開しているパッケージのダウンロード数をまとめて取得したかったので、 npmのユーザーアカウントを指定して、そのアカウントが公開している全パッケージの直近ダウンロード数(前日・前週・前月)を表示するnpmパッケージを公開しました(ちょっとややこしいですね)。
概要
npm-dlc
というコマンドラインのツールです。npmユーザーの名前を指定して実行するとそのユーザーが公開しているパッケージの直近のダウンロード数を表示します。
npmではダウンロード数を取得するAPIはありますが、それ以外の情報を取得するのはありませんので、指定されたユーザーページをスクレイピングして、公開されているパッケージのリストを取得しています。
インストール
npmでグローバルにインストールしてください。
$ npm install -g npm-dlc
使い方
グローバルインストールすると、‘npm-dlc‘ というコマンドが使えるようになります。 npmのユーザー名を指定すれば、そのユーザーが公開しているパッケージのダウンロード数を表形式で表示します。
$ npm-dlc <user-name>[ <user-name> ... ]
出力サンプル
以下は実際の出力結果(vzg03566は私です)。 各パッケージの最新バージョンと公開された大まかな時期も一緒に表示します。
$ npm-dlc vzg03566 Download count of public package published by vzg03566. (https://www.npmjs.com/~vzg03566) -------------------- ------- ------------ ----- ------ ------- NAME VERSION PUBLISHED DAILY WEEKLY MONTHLY -------------------- ------- ------------ ----- ------ ------- hash-arg 0.3.3 a year ago 0 17 65 list-it 0.4.1 2 years ago 17 101 324 aws-node-util 0.9.9 7 months ago 0 42 168 mz700-js 1.0.6 13 days ago 0 224 1042 yea-stringify 1.0.1 2 years ago 2 5 9 minty-mocha 1.0.0 2 years ago 0 3 6 fractional-timer 1.0.2 2 years ago 0 4 19 transworker 1.2.1 a month ago 12 19 103 b-box 0.1.2 9 months ago 0 7 23 dock-n-liquid 0.5.3 9 months ago 0 15 50 fullscrn 1.3.1 2 years ago 0 9 40 svg-z-order 1.2.1 2 years ago 2 16 55 parse-int-array 0.9.0 2 years ago 0 3 8 rough-name 1.0.0 2 years ago 1 3 15 exl 0.1.0 8 months ago 1 2 5 local-lambda-invoker 1.0.0 7 months ago 0 1 4 lex-bnf 0.2.0 7 months ago 0 7 16 gdrive-fs 1.1.2 3 months ago 0 5 17 -------------------- ------- ------------ ----- ------ ------- This list was created at 2019-2-9 12:40:34
あとがき
このプログラム、実は数年前にPHPで書いていたので今回Node.jsへ移植した感じです。 驚いたのはその速度。一瞬で終了するではありませんか。PHPは非同期処理が書けない(?)からでしょうかね。あー驚いた。
コンソールへの出力は、list-it を使用してます。
Gitbookのライブ・リロードがWindowsで異常終了する問題を(とりあえず)回避する
photo credit: tgrauros Helsingin yliopiston kirjasto / Biblioteca de la Universitat de Hèlsinki via photopin (license)
売り上げランキング: 2,254
マークダウンで複数ページからなるドキュメントを綺麗に書けるGitbookですけれど、仕事では先日初めて使うことになりまして「Windowsではライブ・リロードが正しく動かない」ってことを思い出しましたので、とりあえずの回避方法をさらっと書いておきます。
Gitbook?
GitbookはMarkdownで書いた文書を電子文書的にキレイにまとめてくれるツールです。
章立てされた一連の文書を相互にリンクを貼って並べてくれるので便利です。
Markdownを書いて、gitbook build
でHTMLを出力します。
また、gitbook serve
で、ローカルWEBサーバーを起動するので、ブラウザで出力された文書を確認できます( http://localhost:4000/ )。
Gitbookのライブ・リロード?
Gitbookのライブ・リロード(live reload)は、出力したドキュメントを gitbook serve
で確認している時、ソースファイル(Markdown)の更新を監視し、自動的にビルドし、ページをリロードしてくれる標準のプラグインです。
Windowsでは上手く動かない
めっちゃ便利な機能ですけど、残念ながらWindowsでは正しく動いてくれません。 ソースファイルを更新するとローカルWEBサーバーが異常終了してしまうのです。
PS C:\Users\Takami\rd\gitbook-on-win> gitbook serve Live reload server started on port: 35729 Press CTRL+C to quit ... info: 8 plugins are installed info: 7 explicitly listed info: loading plugin "livereload"... OK (中略) info: loading plugin "theme-default"... OK info: found 1 pages info: found 3 asset files info: >> generation finished with success in 1.0s ! Starting server ... Serving book on http://localhost:4000 #このあとソースファイルを再保存すると・・・ Restart after change in file README.md Stopping server #停止した events.js:167 throw er; // Unhandled 'error' event ^ Error: EPERM: operation not permitted, watch at FSEvent.FSWatcher._handle.onchange (internal/fs/watchers.js:123:28) Emitted 'error' event at: at FSWatcher._handleError (C:\Users\Takami\.gitbook\versions\3.2.3\node_modules\chokidar\index.js:236:10) (中略) at FSReqWrap.oncomplete (fs.js:141:20)
とりあえずの回避方法
この挙動、GitHubでIssueが上がっていますが完全放置状態。しかしIssueのコメントにとりあえずの対処法が書かれていました。
「git serve
でWEBサーバーが起動したあと、出力フォルダ(デフォルトで _book
)を一度消せば、その後正常動作する」というものです。
実際にやってみると確かに解決。 _bookディレクトリは、消した直後にビルドされてすぐに復活。 ファイルを何度更新しても自動ビルドが正しく行われ、元気に動き続けてました。
しかし、この操作もちょっと面倒。
コンソールから gitbook serve
した後は、同じコンソールが使えませんから別コンソールを起動して(またはエクスプローラでフォルダを開いて)_bookを消すことになりますからね。
そこで作業開始時にコンソールでgitbook serve
しておいて、別コンソールを立ち上げて rm -r _book
。そのままファイルを編集しはじめれば、それほど手間には感じません。
npm scripts でチョットだけ楽をする
ちなみにワタシは npm scriptsからgitbookを叩いていますので、以下のように設定しています。
・・・ "scripts": { "serve": "gitbook serve", "open": "rm -rf _book && opn http://localhost:4000", }, ・・・
npm run serve
の後、別コンソールで、npm run open
としてページを開きます。
(※ npm内の opnコマンドは npm install --save-dev opn-cli
で使用できるようにしています)
gitbook serve --open
でWEBサーバー起動後にドキュメントを開けますが、これをするとファイルを更新する度にタブが追加されて最新ページが表示されてタブだらけになるのです。