Flask-Uploader

Latest Version MIT GitHub stars Documentation Status

DOWNLOADS DOWNLOADS_M DOWNLOADS_W

Document version: 0.2.1.dev11

Flask-Uploader - загрузчик файлов для Flask с гибкой возможностью расширения.

Быстрый старт

Установка

Установите последнюю стабильную версию, выполнив команду:

pip install Flask-Uploader

Или установите последнюю тестовую версию, которая может и будет содержать баги =) Она автоматически собирается и публикуется на test.pypi.org:

pip install \
    -i https://test.pypi.org/simple/ \
    -i https://pypi.python.org/simple \
        Flask-Uploader

Конфигурация

Flask-Uploader использует для инициализации функцию init_uploader() (это разрешено правилами разработки расширений для Flask). Вызвать эту фукнцию можно в любом месте, обычно это:

  • фабричная функция, которая возвращает экземпляр приложения (предпочтительный вариант)

  • модуль с экземплярами всех используемых расширений

  • модуль, в котором вы создаете экземпляр приложения (худший вариант)

# Module that contains a factory function

from flask import Flask
from flask_uploader import init_uploader


def create_app():
    app = Flask(__name__, instance_relative_config=True)
    # Reading and setting configuration options
    init_uploader(app)
    # Initializing everything you need
    return app

Следующие конфигурационные опции доступны для установки:

Опция

Описание

UPLOADER_ROOT_DIR

Корневая директория для загруженных файлов. Должна существовать и иметь права на запись. По-умолчанию '', но обязательна для FileSystemStorage.

UPLOADER_INSTANCE_RELATIVE_ROOT

Если истина, то UPLOADER_ROOT_DIR может быть задан как относительный путь от корня директории экземпляра. По-умолчанию False.

UPLOADER_BLUEPRINT_NAME

Программное имя, используемое внутренним Blueprint. По-умолчанию _uploader.

UPLOADER_BLUEPRINT_URL_PREFIX

URL префикс, используемый внутренним Blueprint. По-умолчанию /media.

UPLOADER_BLUEPRINT_SUBDOMAIN

Имя поддомена, используемое внутренним Blueprint. По-умолчанию None.

UPLOADER_DEFAULT_ENDPOINT

Имя входной точки для доступа к загруженному файлу, используемое внутренним Blueprint. По-умолчанию download.

Создание загрузчика

Загрузчик - это экземпляр класса Uploader. Цели загрузчика:

  • валидация загруженного файла

  • сохранение загруженного файла в хранилище

  • чтение файла из хранилища по уникальному идентификатору

  • удаление файла из хранилища по уникальному идентификатору

  • получение URL-адреса для доступа к загруженному файлу

В первом аргументе конструктора нужно передать уникальное имя. Это имя используется в маршруте по-умолчанию и для получения ранее созданного экземпляра загрузчика.

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

Остальные аргументы конструктора являются необязательными, однако помните, что первое правило разработчика - «не доверять пользователю», поэтому любые входные данные должны быть отвалидированы.

По-умолчанию именованный аргумент validators конструктора пустой. Это означает, что загрузчик разрешает любой файл. Обязательно передайте значение этого аргумента в зависимости от вашей задачи.

В примере мы создаем загрузчик с именем photos, который будет сохранять загруженные файлы на жестком диске относительно корня директории, заданной конфигурационной опцией UPLOADER_ROOT_DIR в поддиректории photos. Разрешены только файлы изображений не более 10Mb и размером 1920х1080px, для всех остальных файлов будет выброшено исключение ValidationError.

# Module with endpoint handlers, for example - routes/photos.py

from flask_uploader import Uploader
from flask_uploader.storages import FileSystemStorage
from flask_uploader.validators import (
    Extension,
    ImageSize,
    FileRequired,
    FileSize,
)


photos_storage = FileSystemStorage(dest='photos')
photos_uploader = Uploader(
    'photos',
    photos_storage,
    validators=[
        FileRequired(),
        FileSize('10Mb'),
        Extension(Extension.IMAGES),
        ImageSize(max_width=1920, max_height=1080),
    ]
)

Поиск загрузчика

Экземпляр загрузчика можно создать в любом удобном для вас месте, а затем в обработчике входной точки получить ранее созданный экземпляр с помощью статического метода get_instance():

from flask_uploader import Uploader

photos_uploader = Uploader.get_instance('photos')

Входная точка

Дополним наш пример обработчиком входной точки для загрузки изображений:

# Continuation of the routes/photos.py module

from flask import Blueprint, flash, redirect, request
from flask_uploader.exceptions import UploadNotAllowed


bp = Blueprint('photos', __name__, url_prefix='/photos')


@bp.route('/', methods=['POST'])
def upload():
    if 'file' not in request.files:
        flash('No file part.')
        return redirect(request.url)

    try:
        lookup = photos_uploader.save(request.files['file'])
        flash(f'Photo saved successfully - {lookup}.')
    except UploadNotAllowed as err:
        flash(str(err))

    return redirect(request.url)

Доступ к файлу

Flask-Uploader создает экземпляр Blueprint для регистрации обработчиков конечных точек по-умолчанию.

Доступ по-умолчанию

/<name>/<path:lookup> - маршрут по-умолчанию для доступа к загруженному файлу, где name это уникальное имя загрузчика, а lookup - уникальный идентификатор файла, используемый для поиска в выбранном хранилище. В примере с фотографиями, загруженный файл будет доступен для скачивания по адресу:

http://127.0.0.1:5000/media/photos/<lookup>

lookup - имеет строковой тип даных, в большинстве случаев это относительный путь к файлу, поэтому в маршруте используется URL-конвертер PathConverter.

Запрет доступа

Если вам нужно запретить публичный доступ к загруженным файлам для маршрута по-умолчанию, то в момент создания экземпляра Uploader в конструктор передайте аргумент use_auto_route со значением False:

# Module with endpoint handlers, for example - routes/payments.py

from flask_uploader import Uploader
from flask_uploader.storages import FileSystemStorage
from flask_uploader.validators import Extension


payments_uploader = Uploader(
    'payments',
    FileSystemStorage(dest='payments'),
    use_auto_route=False,
    validators=[
        Extension(
            Extension.IMAGES | Extension.EDOCUMENTS
        ),
    ]
)

Контроль доступа

Доступ к загруженному файлу можно контролировать, это может быть полезно в следующих случаях:

  • нужно изменить публичный URL-адрес

  • запретить доступ для неаутентифицированных пользователей

  • использовать промежуточное ПО или HTTP-сервер для обслуживания файлов

Для этого в момент создания экземпляра Uploader в конструктор передайте аргумент endpoint с именем конечной точки, включая имена всех Blueprint. Используйте представление DownloadView для описания конечной точки:

# Module with endpoint handlers, for example - routes/invoices.py

from flask import Blueprint
from flask_login import login_required
from flask_uploader import Uploader
from flask_uploader.storages import FileSystemStorage
from flask_uploader.validators import Extension
from flask_uploader.views import DownloadView


bp = Blueprint('invoices', __name__, url_prefix='/invoices')

invoices_storage = FileSystemStorage(dest='invoices')
invoices_uploader = Uploader(
    'invoices',
    invoices_storage,
    endpoint='invoices.download',
    validators=[
        Extension(
            Extension.OFFICE
        ),
    ]
)


class DownloadInvoiceView(DownloadView):
    decorators = [login_required]
    uploader_or_name = invoices_uploader


download_endpoint = DownloadInvoiceView.as_view('download')

bp.add_url_rule('/<path:lookup>', view_func=download_endpoint)

Промежуточное ПО

Чтобы отдать загруженные файлы, используя промежуточное ПО, например Nginx, в конфигурационном файле вирутального хоста определите новое правило (location), которое перекрывает маршрут по-умолчанию:

# Part of the virtual host configuration file

client_max_body_size 100m;

location /media/photos/ {
    rewrite ^/media/(.*)$ /$1 break;
    root /path/to/uploader_root_dir;
}

Удаление файла

По-умолчанию удаление файла недоступно, это сделано из соображений безопасности. Используйте представление DestroyView для описания конечной точки:

# Continuation of the routes/invoices.py module

from flask_uploader.views import DownloadView


class DeleteInvoiceView(DestroyView):
    decorators = [login_required]
    uploader_or_name = invoices_uploader


delete_endpoint = DeleteInvoiceView.as_view('remove')

bp.add_url_rule('/remove/<path:lookup>', view_func=delete_endpoint)