.. _uploading-files: Загрузка файлов =============== Ну да, старая добрая проблема загрузки файлов. Основная мысль загрузки файлов на самом деле очень проста. В общих чертах это работает так: 1. Тег ``
`` помечается атрибутом ``enctype=multipart/form-data``, а в форму помещается тег ````. 2. Приложение получает доступ к файлу через словарь :attr:`~flask.request.files` в объекте запроса. 3. Воспользуйтесь методом :meth:`~werkzeug.datastructures.FileStorage.save` для того, чтобы сохранить временный файл в файловой системе для последующего использования. Введение -------- Начнём с простейшего приложения, которое загружает файл в определённый каталог и отображает его пользователю. Вот начало нашего приложения:: import os from flask import Flask, request, redirect, url_for from werkzeug.utils import secure_filename UPLOAD_FOLDER = '/path/to/the/uploads' ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) app = Flask(__name__) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER Сначала нужно выполнить серию импортов. Большая часть понятна, :func:`werkzeug.secure_filename` рассматривается чуть позже. `UPLOAD_FOLDER` - это путь, куда будут загружаться файлы, а `ALLOWED_EXTENSIONS` - это набор допустимых расширений. Теперь вручную добавим в приложение правило для URL. Обычно мы так не делаем, но почему мы делаем это сейчас? Причина в том, что мы хотим заставить веб-сервер (или наш сервер приложения) обслуживать эти файлы и поэтому нам нужно генерировать правила для связывания URL с этими файлами. Почему мы ограничили список допустимых расширений? Наверное вам совсем не хочется, чтобы пользователи могли загружать что угодно, если сервер напрямую отправляет данные клиенту. В таком случае вам нужно быть уверенными в том, что пользователи не загрузят файлы HTML, которые могут вызвать проблему XSS (см. Cross-Site Scripting (:ref:`xss`) - межсайтовый скриптинг). Также убедитесь в том, что запрещены файлы с расширением `.php`, если сервер их выполняет. Правда, кому нужен PHP на сервере? :) Следующая функция проверяет, что расширение файла допустимо, загружает файл и перенаправляет пользователя на URL с загруженным файлом:: def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS @app.route('/', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': file = request.files['file'] if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) return redirect(url_for('uploaded_file', filename=filename)) return ''' Upload new File

Upload new File

''' Что делает функция :func:`~werkzeug.utils.secure_filename`? Мы исходим из принципа "никогда не доверяй тому, что ввёл пользователь". Это справедливо и для имени загружаемого файла. Все отправленные из формы данные могут быть поддельными и имя файла может представлять опасность. Сейчас главное запомнить: всегда используйте эту функцию для получения безопасного имени файла, если собираетесь поместить файл прямо в файловую систему. .. admonition:: Информация для профи Может быть вам интересно, что делает функция :func:`~werkzeug.utils.secure_filename` и почему нельзя обойтись без её использования? Просто представьте, что кто-то хочет отправить следующую информацию в ваше приложение в качестве имени файла:: filename = "../../../../home/username/.bashrc" Если считать, что ``../`` - это нормально, то при соединении этого имени с `UPLOAD_FOLDER`, пользователь может получить возможность изменять на файловой системе сервера те файлы, который он не должен изменять. Нужно немного разбираться в устройстве вашего приложения, но поверьте мне, хакеры настойчивы :) Посмотрим, как отработает функция: >>> secure_filename('../../../../home/username/.bashrc') 'home_username_.bashrc' Осталась последняя вещь: обслуживание загруженных файлов. Начиная с Flask 0.5 для этого можно использовать соответствующую функцию:: from flask import send_from_directory @app.route('/uploads/') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) Другая возможность - зарегистрировать `uploaded_file` с помощью правила `build_only` и воспользоваться :class:`~werkzeug.wsgi.SharedDataMiddleware`. Такой вариант будет работать и в более старых версиях Flask:: from werkzeug import SharedDataMiddleware app.add_url_rule('/uploads/', 'uploaded_file', build_only=True) app.wsgi_app = SharedDataMiddleware(app.wsgi_app, { '/uploads': app.config['UPLOAD_FOLDER'] }) Теперь, если запустить приложение, всё должно работать как положено. Улучшение загрузки ------------------ .. versionadded:: 0.6 Как на самом деле Flask обрабатывает загрузку? Если файл достаточно мал, он сохраняется в памяти веб-сервера. В противном случае он помещается во временное место (туда, куда укажет :func:`tempfile.gettempdir`). Но как указать максимальный размер файла, после которого загрузка файла должна быть прервана? По умолчанию Flask не ограничивает размер файла, но вы можете задать лимит настройкой ключа конфигурации ``MAX_CONTENT_LENGTH``:: from flask import Flask, Request app = Flask(__name__) app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 Код выше ограничит максимальный размер файла 16 мегабайтами. Если передаваемый файл окажется больше, Flask сгенерирует исключение :exc:`~werkzeug.exceptions.RequestEntityTooLarge`. Эта функциональность была добавлена во Flask 0.6, но может быть реализована и в более ранних версиях при помощи наследовании от класса request. За более подробной информацией обратитесь к документации Werkzeug об обработке файлов. Индикаторы процесса загрузки ---------------------------- Многие разработчики придумывают считывать файл мелкими частями, сохранять процент загрузки в базу данных и давать возможность JavaScript считывать эти данные из клиента. Короче говоря, клиент спрашивает у сервера каждые 5 секунд, сколько уже было передано. Почувствовали иронию ситуации? Клиент спрашивает у сервера о том, что уже и так знает. Сейчас существуют способы получше, которые работают быстрее и более надёжны. В последнее время в вебе многое изменилось и теперь можно использовать HTML5, Java, Silverlight или Flash, чтобы сделать загрузку удобнее со стороны клиента. Посмотрите на следующие библиотеки, предназначенные именно для этого: - `Plupload `_ - HTML5, Java, Flash - `SWFUpload `_ - Flash - `JumpLoader `_ - Java Простейшее решение ------------------ Поскольку общая процедура загрузки файлов остаётся неизменной для всех приложений, занимающихся загрузкой файлов, для Flask есть расширение под названием `Flask-Uploads`_, которое реализует полностью самостоятельный механизм загрузки с белым и чёрным списком расширений и т.п. .. _Flask-Uploads: http://packages.python.org/Flask-Uploads/ `Оригинал этой страницы `_