Це стаття-посібник, що допоможе вам навчитись використовувати сервіс Amazon S3 для обробки статичних ресурсів та файлів, завантажених користувачем, тобто медіа-ресурсів.
Спершу я розповім про базові поняття, встановлення та налаштування. Потім ви побачите три розділи, що розкривають:
Залежності (dependencies)
Вам знадобляться дві бібліотеки Python:
- boto3
- django-storages
Бібліотека boto3 – клієнт публічного API для доступу до послуг Amazon Web Services (AWS), наприклад Amazon S3. Це офіційний дистрибутив, що підтримується Amazon.
Django-storages – бібліотека з відкритим кодом для керування сховищами на бекенді, такими як Dropbox, OneDrive та Amazon S3. Вона підключає вбудоване API самого Django для роботи зі сховищами. Інакше кажучи, ця бібліотека спростить вам життя, бо не змінюватиме спосіб вашої взаємодії зі статикою та медіа ресурсами. Нам лише треба додати кілька параметрів у конфігурацію і вона зробить за нас усю важку роботу.
Встановлення Amazon S3
Перш ніж ми перейдемо до Django, розберемося з частиною S3. Ми маємо створити користувача із доступом до наших ресурсів S3. Зайшовши до AWS, знайдемо у списку послуг IAM. Воно має бути під Security, Identity & Compliance:
Перейдіть на вкладку Users та натисніть на кнопку Add user, щоб додати користувача:
Дайте користувачу ім'я (user name) та виберіть опцію програмного доступу (Access type: programmatic access).
Натисніть next, щоб перейти до дозволів. На цій стадії нам треба створити нову групу з правильними правами доступу до S3, і додати до неї нашого нового користувача. Натисніть на кнопку Create group, щоб створити групу:
Визначтесь з ім'ям для групи та знайдіть одну з «політик» (колонка Policy name), що надає повний доступ. Вона називається AmazonS3FullAccess:
Натисніть "Create group" щоб завершити процес створення групи. На наступній сторінці з'явиться щойно створена група. Вона одразу матиме галочку, залиште її такою та натисніть Next: Review внизу:
Перевірте інформацію та, якщо все вірно, рухайтесь далі, щоб створити нового користувача. Ви маєте побачити наступну інформацію:
Запишіть поля User, Access key ID та Secret access key. Вони знадобляться вам у майбутньому. Натисніть на кнопку Close і давайте йти далі. Тепер час створити наш перший кошик!
Кошиком ми називаємо контейнер сховищ у S3. Ми можемо працювати з кількома кошиками в рамках одного проекту Django. Однак для більшості випадків Вам знадобиться лише один кошик для одного сайту.
Відкрийте меню Services та пошукайте там S3. Воно знаходиться під пунктом Storage. Якщо ви бачите щось таке як на цьому скріншоті – значить, ви там де треба:
Натисніть на + Create bucket, щоб почати процес. Встановіть для вашого кошика DNS-сумісне ім'я. Воно використовуватиметься щоб ідентифікувати ваші ресурси. У моєму випадку це буде sibtc-static, тож шлях до моїх ресурсів виглядатиме якось так: https://sibtc-static.s3.amazonaws.com/static/.
Нехай усі інші налаштування залишаться як є, переходьте до наступних кроків зі значеннями за замовчанням, і нарешті натисніть кнопку Create bucket. Ви маєте побачити щось таке, як на скріншоті внизу:
Залишимо це як воно є і почнемо працювати над нашою Django-частиною.
Встановлення бібліотек
Найпростіший спосіб - встановити їх через pip
:
pip install boto3
pip install django-storages
Додамо storages
до ваших встановлених застосунків – INSTALLED_APPS
всередині файлу settings.py
:
settings.py
INSTALLED_APPS = [
'django.contrib.auth', '
django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'storages',
]
Робота виключно зі статикою
Це найпростіший випадок. Усе працює «з коробки» із мінімальними налаштуваннями. Отак виглядають усі налаштування всередині модуля settings.py
:
settings.py
AWS_ACCESS_KEY_ID = 'AKIAIT2Z5TDYPX3ARJBA'
AWS_SECRET_ACCESS_KEY = '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'
AWS_STORAGE_BUCKET_NAME = 'sibtc-static'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'mysite/static'),
]
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
Зверніть увагу, що у нас тут є певна чутлива інформація, наприклад AWS_ACCESS_KEY_ID
та AWS_SECRET_ACCESS_KEY
. Ви не повинні вставляти це просто у ваш файл settings.py
або відправляти до публічного репозиторія. Замість цього використовуйте змінні середовища або пітонівську бібліотеку Python Decouple. Керівництво з Python Decouple (англ.) можна знайти тут.
Для зрозумілості я створив мінімальний проект Django:
mysite/
|-- mysite/
| |-- static/
| | |-- css/
| | | +-- app.css
| | +-- img/
| | +-- thumbs-up.png
| |-- templates/
| | +-- home.html
| |-- __init__.py
| |-- settings.py
| |-- urls.py
| +-- wsgi.py
+-- manage.py
Як бачите, обробка статичних файлів має проходити без проблем:
home.html
{% load static %}<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>S3 Example Static Only</title>
<link rel="stylesheet" type="text/css" href="{% static 'css/app.css' %}">
</head>
<body>
<header>
<h1>S3 Example Static Only</h1>
</header>
<main>
<img src="{% static 'img/thumbs-up.png' %}">
<h2>It's working!</h2>
</main>
<footer>
<a href="https://simpleisbetterthancomplex.com">www.SimpleIsBetterThanComplex.com</a>
</footer>
</body>
</html>
Навіть не дивлячись на використання нами локальної машини, ми мусимо запустити команду collectstatic
, бо наш код посилатиметься на віддалене розташування:
python manage.py collectstatic
Ви помітите, що процес копіювання займе більше часу ніж зазвичай. Це нормально. Я видалив зі списку INSTALLED_APPS
застосунок Django Admin, тож це «чистий» приклад. Та якщо ви спробуєте робити це локально – побачите безліч файлів, що копіюються до вашого кошика S3.
Якщо тепер ми перевіримо сайт AWS, то побачимо там наші статичні ресурси:
І, нарешті, результат:
Як ви можете бачити, сховище на бекенді бере на себе переклад тегу шаблона {% static 'img/thumbs-up.png' %}
у https://sitbtc-static.s3.amazonaws.com/static/img/thumbs-up.png і віддає картинку з кошика S3.
У наступному прикладі ви навчитесь працювати зі статичними та медіаресурсами.
Робота зі статикою та медіа
Для цього прикладу я створив новий кошик на ім'я sibtc-assets.
Налаштування у settings.py
будуть дуже простими. От тільки ми розширимо storages.backends.s3boto3.S3Boto3Storage
, додавши кілька власних параметрів, щоб мати змогу зберігати файли користувачів (медіаресурси) в іншому місці, а також щоб сказати нашому S3 не перезаписувати файли з однаковими іменами.
Що мені зазвичай подобається робити, то це створювати файл storage_backends.py
у тій же директорії що й мій settings.py
, і тоді ви можете визначити новий Storage Backend отаким чином:
storage_backends.py
from storages.backends.s3boto3 import S3Boto3Storage
class MediaStorage(S3Boto3Storage):
location = 'media'
file_overwrite = False
Тепер у settings.py
ми мусимо додати цей новий бекенд до опції DEFAULT_FILE_STORAGE
:
settings.py
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'mysite/static'),
]
AWS_ACCESS_KEY_ID = 'AKIAIT2Z5TDYPX3ARJBA'
AWS_SECRET_ACCESS_KEY = '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'
AWS_STORAGE_BUCKET_NAME = 'sibtc-assets'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
DEFAULT_FILE_STORAGE = 'mysite.storage_backends.MediaStorage' # <-- тут ми на нього посилаємось
Щоб продемонструвати завантаження файлу, я створив джанго-застосунок із назвою core та визначив для нього наступну модель:
models.py
from django.db import models
class Document(models.Model):
uploaded_at = models.DateTimeField(auto_now_add=True)
upload = models.FileField()
А тепер погляньте, як виглядатиме мій view:
views.py
from django.contrib.auth.decorators import login_required
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .models import Document
class DocumentCreateView(CreateView):
model = Document
fields = ['upload', ]
success_url = reverse_lazy('home')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
documents = Document.objects.all()
context['documents'] = documents
return context
Ось шаблон document_form.html:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
<table>
<thead>
<tr>
<th>Name</th>
<th>Uploaded at</th>
<th>Size</th>
</tr>
</thead>
<tbody>
{% for document in documents %}
<tr>
<td><a href="{{ document.upload.url }}" target="_blank">{{ document.upload.name }}</a></td>
<td>{{ document.uploaded_at }}</td>
<td>{{ document.upload.size|filesizeformat }}</td>
</tr>
{% empty %}
<tr>
<td colspan="3">No data.</td>
</tr>
{% endfor %}
</tbody>
</table>
Як бачите, я використовую в шаблоні лише вбудовані у Django можливості. Отак цей шаблон виглядає в браузері:
Я не збираюся закопуватись у деталі щодо завантаження файлів, ви можете прочитати про це детальніше окремо. Тепер протестуємо файли, завантажені користувачем:
Я створив свій шаблон, щоб виводити список завантажених файлів, тож коли юзер завантажить якесь зображення чи документ – вони з'являться у списку.
Тепер якщо ми клікнемо по посиланню, яке насправді звичайнісіньке {{ document.upload.url }}
у Джанго, ми побачимо зображення з кошика на Amazon S3:
А якщо ми тепер зазирнемо до нашого кошика, то побачимо там директорії static та media:
Змішування публічних ресурсів із приватними
Використовуючи дуже схожі принципи, ви призначаєте деякі ресурси для приватного зберігання у кошику S3. Погляньте на конфігурацію внизу:
storage_backends.py
from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage
class StaticStorage(S3Boto3Storage):
location = settings.AWS_STATIC_LOCATION
class PublicMediaStorage(S3Boto3Storage):
location = settings.AWS_PUBLIC_MEDIA_LOCATION
file_overwrite = False
class PrivateMediaStorage(S3Boto3Storage):
location = settings.AWS_PRIVATE_MEDIA_LOCATION
default_acl = 'private'
file_overwrite = False
custom_domain = False
settings.py
AWS_ACCESS_KEY_ID = 'AKIAIT2Z5TDYPX3ARJBA'
AWS_SECRET_ACCESS_KEY = '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'
AWS_STORAGE_BUCKET_NAME = 'sibtc-assets'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
AWS_STATIC_LOCATION = 'static'
STATICFILES_STORAGE = 'mysite.storage_backends.StaticStorage'
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, AWS_STATIC_LOCATION)
AWS_PUBLIC_MEDIA_LOCATION = 'media/public'
DEFAULT_FILE_STORAGE = 'mysite.storage_backends.PublicMediaStorage'
AWS_PRIVATE_MEDIA_LOCATION = 'media/private'
PRIVATE_FILE_STORAGE = 'mysite.storage_backends.PrivateMediaStorage'
Тепер ми можемо визначити новий PrivateMediaStorage
просто у визначенні моделі:
models.py
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from mysite.storage_backends import PrivateMediaStorage
class Document(models.Model):
uploaded_at = models.DateTimeField(auto_now_add=True)
upload = models.FileField()
class PrivateDocument(models.Model):
uploaded_at = models.DateTimeField(auto_now_add=True)
upload = models.FileField(storage=PrivateMediaStorage())
user = models.ForeignKey(User, related_name='documents')
Після завантаження приватного файлу, якщо ви спробуєте отримати URL контенту, API згенерує довгий URL із коротким терміном життя у кілька хвилин:
Якщо ви спробуєте пробитися до ресурсу напряму, без параметрів – отримаєте повідомлення про помилку від AWS:
Висновки
Сподіваюсь, цей посібник допоміг Вам зрозуміти певні базові принципи Amazon S3 та хоча б взятись до роботи. Нехай вас не лякає офіційна документація обох бібліотек, boto3 та django-storages:
Я також підготував три приклади (я їх використовував у цьому посібнику), тож ви можете досліджувати їх і взяти щось для себе з моєї імплементації: https://github.com/sibtc/simple-s3-setup.
У цьому репозиторії ви знайдете три проекти Django, кожен для свого випадку:
- s3-example-public-and-private – публічні та приватні ресурси
- s3-example-static-and-media – статика та медіа
- s3-example-static-only – лише статика.
Не забувайте додати власні ключі та іншу інформацію, щоб все запрацювало! Я залишив відповідні поля пустими спеціально для цього.
Ще немає коментарів