開発中のDjangoアプリケーションにおいて、画像などのファイルをAWS S3で取り扱う方法について説明します。
DjangoアプリをHerokuで運用する場合、Git管理していないファイルが削除されてしまうため、別のファイルサーバーに保管しておく必要があり、今回はAWS S3を用いました。
参考:Heroku公式
環境
- Python 3.8.8
- Django 3.2.2
手順
S3の設定とDjangoの設定に分けて説明します。
S3の設定
すでにAWSアカウントは作成済みとして説明します。
IAMユーザーを作成する
S3にアクセスするために、まずはAWS Identity and Access Management (IAM) ユーザーを作成します。
直接S3バケット内のオブジェクト(=ファイル)にアクセスするIAMユーザーの認証情報をDjangoアプリに設定することで、DjangoアプリからS3内のファイルにアクセスすることができる、という仕組みです。
「サービス > IAM > ユーザー」から、ユーザーを追加します。
STEP1では、ユーザー情報を入力します。
ユーザー名はなんでも良いです。AWS認証情報タイプは、アクセスキーにチェックを入れてください。パスワードは任意です。
STEP2では、このユーザーに与えるアクセス権限を設定します。
既存のポリシーを直接アタッチから、s3で検索し、"AmazonS3FullAccess"にチェックを入れます。
STEP3ではタグを設定しますが、ここは飛ばして大丈夫です。
入力情報を確認して、ユーザーの作成をクリック。
ここで、認証情報(アクセスキーとシークレットアクセスキー)が表示されるので、CSVファイルのダウンロードも活用し、保管します。後ほどDjangoに設定します。
S3バケットを作成する
S3バケットを作成します。
テスト環境と本番環境について、1つのバケット内のディレクトリで分けて管理する方法でも実現可能ですが、環境変数のみの変更で対応したかったため、今回はバケットを2つ作成しました。
「サービス > S3 > バケット」から、バケットを作成します。
設定は全てデフォルトでOKです。バケット名は、後ほどDjangoに設定します。
Djangoの設定
必要モジュールのインストール
boto3モジュールをインストールします。
pip install boto3
settings.py
ここで先ほどのS3の設定中に登場した、アクセスキー、シークレットアクセスキー、バケット名を環境変数に設定します。
AWS_ACCESS_KEY_ID = アクセスキー
AWS_SECRET_ACCESS_KEY = シークレットアクセスキー
AWS_STORAGE_BUCKET_NAME = バケット名
今回、環境に依らずIAMユーザーは1つのみのため、アクセスキー、シークレットアクセスキーも環境に依らず同じものとなります。一方、バケット名は環境によって変えることを想定しているため、local_settings.pyを活用して分けることとします。
Djangoの環境変数については、下記記事で紹介していますのでよければご参考にしてください。
views.py
実際の処理を書いていきます。urls.pyは省略します。
### ライブラリ読み込み ###
from pathlib import Path
import boto3
### S3設定 ###
s3 = boto3.resource('s3')
bucket_name = settings.AWS_STORAGE_BUCKET_NAME
bucket = s3.Bucket(bucket_name)
### ファイル名、パス ###
FILE = 'sample.png'
ORIGIN_PATH = Path(FILE)
SAVE_PATH = Path(FILE)
### ファイルをS3にアップロードする ###
def upload_file_to_s3(request):
bucket.upload_file(str(ORIGIN_PATH), FILE)
return render(request, 'complete.html')
### S3内のファイルをローカルに持ってくる ###
def download_file_from_s3(request):
bucket.download_file(FILE, str(SAVE_PATH))
return render(request, 'complete.html')
### S3内のファイルをブラウザ上でダウンロードする ###
def download_file_from_s3_directly(request):
url = client.generate_presigned_url(
'get_object',
Params = {
'Bucket': bucket_name,
'Key': str(SAVE_PATH),
},
ExpiresIn = 600,
)
return HttpResponseRedirect(url)
全部で3つの処理を紹介しています。
1つ目と2つ目の処理については、DjangoのファイルシステムとS3間でファイルを移動させる処理です。
3つ目は、ブラウザ上でファイルを直接ダウンロードするレスポンスを返す処理です。元々の目的が「Djangoのファイルシステムにファイルを置きたくない!」ですので、2つ目より3つ目のダウンロードの方が使いやすいと思います。
S3バケットのコストについて
S3を利用するには必ずコストが発生してしまいます。(条件を満たせば、AWS無料枠を使用することもできます。)
私の場合は、コストを最小限に抑えるために、画像ファイルをユーザーやプロジェクトごとにまとめてzip化した上でアップロード・ダウンロードする実装とし、S3へのアクセス回数及びファイル容量を最小化しています。
まずはテスト環境で、サイズが小さいファイルを用いて、基本的なアップロード・ダウンロードを実装し、アプリのパフォーマンスを損なわない範囲でコストを最小化することも検討してみてください。
また、S3のコストの可視化については、対象バケットにコスト配分タグを付与することで可能になります。詳しくはAWS公式の下記QAを参考にしてください。
参考:AWS公式「Amazon S3 バケットのコストを確認するにはどうすればよいですか?」
まとめ
本記事では、DjangoアプリのファイルをAWS S3で取り扱う方法を解説しました。手順は煩雑ではなく、一つ一つ抑えていけば難しくないと感じました。
S3については、ライフサイクルを詳細に設定して、よりコストを最小化したいと考えています。サービスが盛り上がってきたら検討します。