Django + Heroku + AWS S3で画像表示させる方法

# 概要
Djangoアプリで画像をS3に保存し、アプリに表示させる方法を書いてみます。
想定している処理は、`①ユーザーが画像投稿`→`②S3に保存`→`③S3からアプリに画像表示`です。

なお、環境は`Python 3.7.3`、`Django 2.2`になります。

# 1. AWS設定
### 1-1. AWS登録
S3を使用するのに、AWSアカウントが必要になりますので、お持ちでない方は[AWS Signup](https://portal.aws.amazon.com/billing/signup#/start)からご登録ください。

参照：
[AWS アカウント作成の流れ](https://aws.amazon.com/jp/register-flow/)
[Amazon S3](https://aws.amazon.com/jp/s3/)

### 1-2. バケット作成
#### ①S3を検索してクリック

![s3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/7f7ce368-57a1-2573-965e-da935b879c0e.png)

#### ②バケット作成

![s3-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/32502ff9-80ea-5fd5-795f-46f0927d2f2d.png)

#### ③バケット名とリージョンを選択

![s3-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/1614ff4b-0864-6c63-59ef-4cc787aebc1a.png)

#### ④オプションなし
オプションですが、今回は設定しません

![s3-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/9f4e2e88-022e-727a-7103-f4499c7df658.png)

#### ⑤アクセス権限
こちらの項目を外してください、初期設定ではブロックされてしまいます

![s3-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/795a4404-c906-40ee-7f20-bf1f0cc0eed9.png)

#### ⑥内容を確認して作成

![s3-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/6b0a29dd-25f5-c648-352a-bb1cded93cec.png)

#### ⑦バケット完成

![s3-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/9bc87b64-2aac-bc74-90bc-82f6a9671580.png)

### 1-3. CORS設定
#### ①CORSの設定に移動
作成したバケットをクリックし、「アクセス制限」の「CORSの設定」に移動します

![s3-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/3fa6a16c-218e-e8f5-9538-a83770dc01bf.png)

#### ②CORS構成エディターに追加
下記のように追加してください

```:CORS
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
   <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>
```

参照：
[Cross-Origin Resource Sharing (CORS)](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/cors.html)
[Direct to S3 File Uploads in Python | S3 Setup](https://devcenter.heroku.com/articles/s3-upload-python#s3-setup)

### 1-4. IAM設定
#### ①IAMを検索してクリック

![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/c4a9f162-9021-9977-84d4-f81a2f21e9f4.png)

#### ②ユーザーをクリック

![b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/951ce31a-8c5a-f779-98fd-dcf411892818.png)

#### ③ユーザー追加をクリック

![c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/d81f0f1a-545b-9168-bf8e-60953c08693b.png)

#### ④ユーザー名を入力、プログラムによるアクセスをチェック

![d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/208f6c14-ac92-cf42-1dac-b0d6910fa951.png)

#### ⑤アクセス許可
S3を検索し、AmazonS3FullAccessにチェック

![e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/2d3669f9-a79e-df1f-db6b-fbeb0dbc6ae4.png)

#### ⑥オプションなし
オプションですが、今回は設定しません

![f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/db729fb9-3a84-9eb1-1cf0-f075939b23e4.png)

#### ⑦内容を確認して作成

![g.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/02a32296-7052-0188-777c-da61c43103b4.png)

#### ⑧IAM設定完了
***後ほど、`アクセスキーID`と`シークレットアクセスキー`を使いますので、画面を閉じないでください***

![i.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403981/799e4832-0ee1-6074-f7de-9ae806b2f4bf.png)

参照：[IAMとは](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/introduction.html)

# 2. アプリ設定
### 2-1. インストール
#### ①django-storagesインストール

```terminal:terminal
$ pip install django-storages
```

参照：[django-storages | Amazon S3](https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html)

#### ②boto3インストール

```terminal:terminal
$ pip install boto3
```

参照：
[Boto 3 Documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html)
[AWS SDK for Python (Boto3)](https://aws.amazon.com/jp/sdk-for-python/)

### 2-2. settings.py設定
下記のように追加してください

```python:settings.py
    INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'your_app_name',
    'storages', #追加
]

    #追加
    AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
    AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
    AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']

    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
    S3_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME
    MEDIA_URL = S3_URL

    AWS_S3_FILE_OVERWRITE = False
    AWS_DEFAULT_ACL = None
```

### 2-3. 環境変数
Herokuの環境変数を3つ設定します。

`①AWS_ACCESS_KEY_ID`と`②AWS_SECRET_ACCESS_KEY`は、IAMユーザーを追加した際に表示された、`アクセスキーID`と`シークレットアクセスキー`になります。

`③AWS_STORAGE_BUCKET_NAME`は作成したバケット名です。

```terminal:terminal
$ heroku config:set AWS_ACCESS_KEY_ID="ご自身のアクセスキーIDを記入"
$ heroku config:set AWS_SECRET_ACCESS_KEY="ご自身のシークレットアクセスキーを記入"
$ heroku config:set AWS_STORAGE_BUCKET_NAME="ご自身のバケット名を記入"
```

参照：
[Configuration and Config Vars | Heroku](https://devcenter.heroku.com/articles/config-vars)
[Django Docs | Deployment checklist](https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/#critical-settings)

### 2-4. requirements.txt
インストールしたモジュールを`requirements.txt`に追加します。

```terminal:terminal
$ pip freeze > requirements.txt
```

### 2-5. local_settings.py設定
もし`local_settings.py`を使用している場合は、下記を参考にしてください。

ローカル環境では`MEDIA_ROOT`で指定したディレクトリから読み込み、Herokuでは`S3`から読み込むことができます。

```python:settings.py
###
省略
###

DEBUG = False

ALLOWED_HOSTS = ['*']

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'your_app_name',
    'storages',
]

###
省略
###

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

try:
    from .local_settings import *
except ImportError:
    pass

if not DEBUG:
    SECRET_KEY = os.environ['SECRET_KEY']

    AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
    AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
    AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']

    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
    S3_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME
    MEDIA_URL = S3_URL

    AWS_S3_FILE_OVERWRITE = False
    AWS_DEFAULT_ACL = None

    import django_heroku
    django_heroku.settings(locals())

db_from_env = dj_database_url.config(conn_max_age=600, ssl_require=True)
DATABASES['default'].update(db_from_env)
```

```python:local_settings.py
import os

SECRET_KEY = 'ご自身のSECRET_KEYを記入'

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

DEBUG = True
```

# 最後に
Herokuでのデプロイが曖昧でしたら、下記の記事も参考にしてみてください。

[DjangoアプリをHerokuにデプロイする方法](https://qiita.com/frosty/items/66f5dff8fc723387108c)

以上