S3 から特定のファイルを持ってきて Zip に固めてダウンロードさせる

AWS, Python, S3

大変なのかと思っていたのですが、ライブラリが揃っていて思っていたより簡単に実現できました。

環境

  • 言語
    • Python 3.5.2
  • ライブラリ
    • Flask 0.11
    • boto3 1.3.1

AWS 側の設定として、 Web サーバから 対象となる S3 のファイルへ読み込みアクセスの許可が必要な場合があります。

コード

解説

コントローラー

/downlaod/1 のように ID を指定してアクセスすると、ファイルがダウンロードされます。

Flask には send_file という API があり、
サーバ内のファイルへの PATH を渡すと、クライアントへダウンロードさせるレスポンスを簡単に返すことが出来ます。
mimetype などの設定も Flask が自動判断してくれるので、凝ったことしない限り便利に使えそうです。

一点ハマったこととして、ファイルをダウンロードさせたければ send_file のオプションで、 as_attachment=True と指定しなければいけません。
このオプションはレスポンスヘッダに Content-Disposition: attachment と追加し、
添付ファイルであると明示的に指定します。
これがないと、ブラウザでファイルの中身が表示されてしまったり、
ダウンロードファイル名が変になってしまったりします。

モデル

ここでは2つの処理を行っています。

  • ファイルをS3から取得する
  • 取得したファイルをZipアーカイブに入れる

ファイルをS3から取得する

S3 からの取得には Python 用 SDK boto3 を使用しています。

resource というのは、公式によると

Boto 3 consists of the following major features:
Resources: a high level, object oriented interface
Collections: a tool to iterate and manipulate groups of resources
Clients: low level service connections
Paginators: automatic paging of responses
Waiters: a way to block until a certain state has been reached

とのことで、 S3に関しての高度な操作まで揃ったインターフェイスを返してくれる…みたいです。

そして、リソース s3 中の download_file メソッドを呼び出してローカルにS3のファイルをダウンロードします。
第二引数で保存先のファイル名を指定出来ます。
ここで一定の規則に従ったファイルにしておけば、後でまとめて消すときに楽です。

取得したファイルをZipアーカイブに入れる

Python 標準ライブラリーの zipfileを使用しています。
(あんまり使われてないのか、まだドキュメントの翻訳が完了しておらず、一部が英語のままです)

これで Zip ファイルを作成し、同時に読み込みモード(w) で開いています。
result_zip という名前で作成した Zip ファイルにアクセスできるようになります。
本来は close を明示的に書く必要がありますが、 with を使っていれば句の終わりで自動的に close してくれるのでこちらを使いましょう。

先ほど開いた zip ファイルに S3 から取得したファイルを書き込みます。
filename に書き込むファイルのフルパスを、 arcname に zip ファイルで実際に書き込まれる時の名前を指定します。
このとき、arcname を指定しないと、フルパスのディレクトリ構造そのままでZipファイルが作成されてしまいます。注意しましょう。

AWS, Python, S3