Облачное хранилище файлов

Проект “Облачное хранилище файлов” #

Многопользовательское файловое облако. Пользователи сервиса могут использовать его для загрузки и хранения файлов. Источником вдохновения для проекта является Google Drive.

Что нужно знать #

  • Python - коллекции, ООП
  • pip/Poetry
  • Backend
    • Django
    • Upload файлов, заголовки HTTP запросов, cookies, cессии
  • Базы данных
    • PostreSQL
    • DjangoORM
    • Миграции
    • Представление о NoSQL хранилищах
  • Frontend - HTML/CSS, Bootstrap
  • Docker - контейнеры, образы, volumes, Docker Compose
  • Тесты - интеграционное тестирование, TestCase, LiveServerTestCase
  • Деплой - облачный хостинг, командная строка Linux

Мотивация проекта #

  • Использование возможностей Django
  • Практика с Docker и Docker Compose
  • Первый проект, где студент самостоятельно разрабатывает структуру БД
  • Знакомство с NoSQL хранилищем S3 для хранения файлов, Redis для хранения сессий

Функционал приложения #

Работа с пользователями:

  • Регистрация
  • Авторизация
  • Logout

Работа с файлами и папками:

  • Загрузка файлов и папок
  • Создание новой пустой папки (аналогично созданию новой папки в проводнике)
  • Удаление
  • Переименование

Интерфейс приложения #

Главная страница #

Адрес - /?path=$path_to_subdirectory. Параметр $path задаёт путь просматриваемой папки. Если параметр отсутствует, подразумевается корневая папка. Пример - /path=Projects%2FJava%2FCloudFileStorage (параметр закодирован через URL Encode).

  • Заголовок
    • Для неавторизованных пользователей - кнопки регистрации и авторизации
    • Для авторизованных пользователей - логин текущего пользователя и кнопка Logout
  • Контент (только для авторизованных пользователей)
    • Форма поиска файлов и папок по названию
    • Навигационная цепочка (breadcrumbs), содержащая путь из папок до текущей папки. Каждый элемент является ссылкой на свою папку. Пример - цепочка из папок, ведущая к - Projects/Java/CloudFileStorage содержала бы 3 папки - корневую, Projects и Projects/Java
    • Список файлов в текущей директории. Для каждого файла отображаем имя и кнопку, вызывающее меню действий (удаление, переименование)
    • Формы (или drop areas) для загрузки файлов и папок

Страница поиска файлов #

Адрес - /search/?query=$search_query.

  • Заголовок
    • Для неавторизованных пользователей - кнопки регистрации и авторизации
    • Для авторизованных пользователей - логин текущего пользователя и кнопка Logout
  • Контент
    • Форма поиска файлов и папок по названию
    • Список найденных файлов. Для каждого найденного файла отображаем имя и кнопку для перехода в папку, содержащую данный файл

Неавторизованные пользователя не имеют доступа к данной странице, приложение должно редиректить их на форму авторизации.

Контроллер для доступа к конкретному файлу #

Остальное #

  • Страницы с формами регистрации и авторизации

Работа с сессиями, авторизацией, регистрацией #

В предыдущем проекте мы хранили сессии в SQL базе данных, используя SessionMiddleware.

В этом проекте воспользуемся SessionMiddleware в связке с Redis.

SQL база данных #

В этом проекте студент самостоятельно разрабатывает структуру базы данных для хранения пользователей (файлы и сессии располагаются в других хранилищах). Предлагаю использовать MySQL.

Использовать поддержку токенов, базовой аутентификации, аутентификации OAuth и других методов аутентификации.

Важно помнить о создании необходимых индексов в таблице Users. Например, логин пользователя должен быть уникальным. Схема БД в этом проекте очень простая, но тем не менее рекомендую попрактиковаться с миграциями.

Хранилище файлов S3 #

Для хранения файлов будем пользоваться S3 - simple storage service. Проект, разработанный Amazon Cloud Services, представляет из себя облачный сервис и протокол для файлового хранилища. Чтобы не зависеть от платных сервисов Amazon в этом проекте, воспользуемся альтернативным S3-совместимым хранилищем, которое можно запустить локально - https://min.io/

Структура S3 хранилища #

В SQL мы оперируем таблицами, в S3 таблиц не существует, вместо этого S3 оперирует бакетами (bucket - корзина) с файлами. Чтобы понять что такое бакет, можно провести аналогию с диском или флешкой.

Внутри бакета можно создавать файлы и папки.

Для хранения файлов всех пользователей в проекте создадим для них бакет под названием user-files. В корне бакета для каждого пользователя будет создана папка с именем в формате user-${id}-files, где id является идентификатором пользователя из SQL базы.

Каждая из таких папок является корнем для хранения папок данного пользователя. Пример - файл docs/test.txt пользователя с id 1 должен быть сохранён в путь user-1-files/docs/test.txt.

Работа с S3 из Python #

Как было упомянуто выше, для работы с S3 воспользуемся AWS Python SDK - boto3. Необходимо будет научиться пользоваться этой библиотекой, чтобы:

  • Создавать файлы
  • Переименовывать файлы
  • “Переименовывать” папки. Насколько знаю в S3 нет такой операции, переименование папки по сути представляет собой создание папки под новым именем и перенос туда файлов
  • Удалять файлы

Upload файлов #

Для загрузки файлов необходимо воспользоваться HTML file input - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file. Распространённый подход оформить это в виде зоны, на которую можно перетягивать файлы из проводника, пример - https://codepen.io/dcode-software/pen/xxwpLQo.

На уровне HTTP, передача файлов осуществляется с помощью multipart/form-data.

Со стороны Django необходимо будет реализовать контроллер(ы) для обработки загруженных файлов через модуль django.core.files. Важно иметь в виду, что по-умолчанию лимит на загрузку файлов в Django равен 2.5 мегабайтам, но его можно увеличить с помощью переменной FILE_UPLOAD_MAX_MEMORY_SIZE.

Загрузка папок #

File input может быть использован для загрузки либо отдельных файлов, либо папок (если у input установлен атрибут webkitdirectory), но не одновременно.

Получается, что необходимо иметь 2 input’а - для файлов, и для папок. Возможно, существуют Javascript библиотеки, которые решают этот вопрос и реализуют единый input для обоих случаев.

Тесты #

Интеграционные тесты сервиса по работе с пользователями #

Как и в прошлом проекте, покроем тестами связку слоя данных с классами-сервисами, отвечающими за пользователей.

Предлагаю воспользоваться Testcontainers для запуска тестов в контексте полноценной (а не in-memory) базы данных. Для этого использовать библиотеку django-test-plus, которая включает в себя поддержку работы с контейнерами Docker для запуска тестов в контексте реальной базы данных. Это позволяет приблизить окружение тестов к рабочему окружению, и тестировать нюансы, специфичные для конкретных движков БД.

Примеры тест кейсов:

  • Вызов метода “создать пользователя” в сервисе, отвечающем за работу с пользователями, приводит к появлению новой записи в таблице users
  • Создание пользователя с неуникальным username приводит к ожидаемому типу исключения

Интеграционные тесты сервиса по работе с файлами и папками #

Опциональное задание повышенной сложности - покрыть тестами взаимодействие с сервисом хранения данных, работающим Minio.

Примеры тест кейсов:

  • Загрузка файла приводит к его появлению в bucket’е Minio в корневой папке текущего пользователя
  • Переименование, удаление файлов и папок приводит к ожидаемому результату
  • Проверка прав доступа - пользователь не должен иметь доступа к чужим файлам
  • Поиск - пользователь может находить свои файлы, но не чужие

Docker #

В данном проекте впервые воспользуемся Docker для удобного запуска необходимых приложений - SQL базы, файлового хранилища MinIO, Redis.

Необходимо:

  • Найти образы для каждого нужного приложения из списка выше
  • Написать Docker Compose файл для запуска стека с приложениями (по контейнеру для каждого)
  • Знать Docker Compose команды для работы со стеком

Как будет выглядеть работа с Docker:

  • Для работы над проектом запускаем стек из контейнеров
  • Уничтожаем или останавливаем контейнеры (с сохранением данных на volumes), когда работа не ведётся
  • По необходимости уничтожаем данные на volumes, если хотим очистить то или иное хранилище, запустить

Деплой #

Будем вручную деплоить приложение на удалённый сервер. Все остальные приложения этого проекта (SQL, MinIO) запускаем через Docker Compose.

Шаги:

  • В хостинг-провайдере по выбору арендовать облачный сервер на Linux
  • Установить Python, Docker
  • Скопировать на удалённый сервер Docker Compose файл для запуска MySQL, MinIO

Ожидаемый результат - приложение доступно по адресу http://$server_ip:8080/.

План работы над приложением #

  • Docker Compose - добавить MySQL, Redis
  • Django - с помощью django.contrib.auth реализовать регистрацию и авторизацию пользователей, хранить сессии в Redis
  • Интеграционные тесты для сервиса регистрации
  • Docker Compose - добавить MinIO
  • Django - интегрировать AWS Python SDK и научиться совершать операции с файлами в бакете, написать сервис, инкапсулирующий необходимые для приложения операции
  • Реализовать загрузку файлов и папок через форму (формы) на главной странице
  • Реализовать отображение файлов и навигацию по структуре директорий, действия с файлами (удаление, переименование)
  • Поиск файлов - сервис, контроллер и Jinja2 шаблон
  • (Опционально) интеграционные тесты для сервиса, отвечающего за работу с файлами и папками
  • Деплой

Ресурсы для работы над ошибками #

  • Готовый проект можете отправить мне на ревью - https://t.me/zhukovsd