Загрузка файлов на ваш Web-сайт
Если вам нужен интерфейс для загрузки файлов на ваш Web-сайт с удаленного компьютера, то вы можете создать его из двух частей: формы и сценария РНР. В этой главе вы узнаете, как выполнить следующие действия:
Что тaкoe зaгpyзкa файла на Web-сервер?
Процесс загрузки файла на Web-сервер через HTML-форму является загадкой для многих людей. Уделим немного времени для объяснения этого процесса.
Чтобы начать процесс загрузки, вам необходимо следующее:
Сам по себе процесс загрузки представляет собой следующую последовательность действий:
Пользователь РНР (пользователи, которые запустили РНР, с такими именами, как «nobody», «www» или «joe») должны иметь право записи файла во временную папку, так же как и в результирующую папку.
В следующем знакомстве мы начнем создание простой HTML-формы.
Создание формы
Начнем создание формы с одним полем ввода. Вы можете создать форму для загрузики такого количества файлов, которое впоследствии планируете получать, используя последовательность действий при работе с одним файлом.
Параметр ENCTYPE используется потому, что мы будем отправлять больше, чем просто текст. Предположим, что вы будете загружать файл с изображением, поэтому назовем поле ввода имени файла в форме img1.
Параметр TYPE=»file» в поле ввода формы будет отображаться, как попе ввода с кнопкой Обзор. Кнопка Обзор запускает файловый менеджер, с помощью которого вы можете выбрать файл для загрузки.
http://localhost/upload_form.html
и нажмите клавишу Enter.
Браузер загрузит страницу Загрузка файла (Рис. 6. 1).
Рис. 6.1. Страница Загрузка файла
На Рис. 6.2 вы видите форму загрузки файла с полем ввода и кнопками Обзор и Загрузить на сервер. В следующем упражнении вы создадите сценарий для обработки этой формы.
Создание сценария загрузки.
Перед началом создания сценария запомните список переменных, которые создаются автоматически после успешной загрузки файла. Основная часть имени переменных — img1 — является именем поля ввода созданной в предыдущем знакомстве формы.
Целью создаваемого сценария является следующее: взять загруженный файл, скопировать его в папку для документов Web-сервера и вернуть подтверждающее сообщение пользователю, содержащее все переменные из указанного выше списка.
Если значение $imgl_name не пустое, нужно выполнить функцию копирования. Применим символ @ перед именем функции, чтобы подавить предупреждения, и используем функцию die( ), чтобы вызвать завершение сценария и отобразить сообщение о том, что функция сору( ) закончилась неудачей:
Рис. 6.2. Файл сценария do_upload.php
Загрузкa файла с помощью формы и сценария
Теперь, когда вы создали форму и сценарий для загрузки файла, проверим, как все это работает.
Запустите свой Web-браузер, в поле ввода Адрес введите
http://localhost/upload_form.
и нажмите клавишу Enter. Браузер загрузит страницу Загрузка файла (Рис. 6.1). Нажмите кнопку Обзор. На экране появится диалог Выбор файла (Рис. 6.3).
Рис. 6.3. Диалог Выбор файла
Рис. 6.4. Сообщение об успешном завершении загрузки файла
Файл pic6_3.gif был выбран для примера, имя вашего файла будет отличаться от использованного здесь имени.
Рис. 6.5. Диалог Открыть
Окно браузера отобразит загруженный на ваш Web-сервер файл (Рис. 6.6).
Рис. 6.6. Файл, который был загружен с использованием созданного сценария
Для поиска файла вы также можете использовать кнопку Обзор диалога Oткрыть (Рис. 6.6).
Простой счетчик обращений
Сценарий, который вам предстоит создать в этом упражнении, подсчитывает количество обращений к web-странице, на которой он находится.
Полный листинг, созданного вами файла, должен выглядеть так как приведенный листинг на рис. 6.7
Рис 6.7. Листинг счетчика обращений к Web-страничке.
http://localhost/nomer.php
и нажмите клавишу Enter. В рабочее окно браузера будет загружена страничка с сообщением о количестве обращений к ней. Нажмите несколько раз на кнопку Обновить, имитируя новые обращения к страничке. Счетчик на страничке будет исправно подсчитывать все новые и новые обращения к ней (рис 6.8).
Рис 6.8 Страничка подсчитывающая своих пользователей
Загрузка файлов на сервер во Flask Python.
Основная идея загрузки файлов во Flask на самом деле довольно проста. В основном это работает так:
- Тег формы помечается как
enctype=multipart/form-data
, и в эту форму помещается<input type=file>
. - Приложение обращается к файлу из словаря
Request.files
в объекте запроса. - Используйте метод полученного объекта файла
.save()
, чтобы навсегда сохранить файл где-нибудь в файловой системе.
- Ведение в загрузку файлов на Flask.
- Ограничение размера загружаемых файлов.
- Расширение для загрузки файлов Flask-Uploads:
- Параметры конфигурации расширения Flask-Uploads.
- Наборы загрузок
UploadSet
. - Конфигурация расширения Flask-Uploads.
- Форма загрузки файлов.
- Пример загрузки фотографий с помощью Flask-Uploads и Flask-WTF.
Ведение в загрузку файлов на Flask.
Создадим простое приложение, которое загружает файл в определенную папку и отображает его пользователю. Смотрим код инициализации веб-приложения:
import os from flask import Flask, flash, request, redirect, url_for # объясняется ниже from werkzeug.utils import secure_filename # папка для сохранения загруженных файлов UPLOAD_FOLDER = '/path/to/the/uploads' # расширения файлов, которые разрешено загружать ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} # создаем экземпляр приложения app = Flask(__name__) # конфигурируем app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
Зачем ограничивать разрешенные к загрузке расширения файлов? Если сервер напрямую будет отправлять загруженные данные клиенту, то могут быть проблемы с XSS. Например загруженные HTML файлы могут содержать вредоносный JavaScript или если на сервере установлен php интерпретатор, то загруженные файлы php, с вредоносным кодом могут выполнится и т. д. Ограничивая разрешенные к загрузке расширения, мы оберегаем себя от разного рода неприятностей.
Далее напишем функции, которые проверяют, допустимо ли расширение, загружают файл и перенаправляют пользователя на URL-адрес загруженного файла:
def allowed_file(filename): """ Функция проверки расширения файла """ return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.route('/', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': # проверим, передается ли в запросе файл if 'file' not in request.files: # После перенаправления на страницу загрузки # покажем сообщение пользователю flash('Не могу прочитать файл') return redirect(request.url) file = request.files['file'] # Если файл не выбран, то браузер может # отправить пустой файл без имени. if file.filename == '': flash('Нет выбранного файла') return redirect(request. url) if file and allowed_file(file.filename): # безопасно извлекаем оригинальное имя файла filename = secure_filename(file.filename) # сохраняем файл file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) # если все прошло успешно, то перенаправляем # на функцию-представление `download_file` # для скачивания файла return redirect(url_for('download_file', name=filename)) return ''' <!doctype html> <title>Загрузить новый файл</title> <h2>Загрузить новый файл</h2> <form method=post enctype=multipart/form-data> <input type=file name=file> <input type=submit value=Upload> </form> </html> '''
Так что же на самом деле делает функция secure_filename()
? Проблема в том, что существует принцип, называемый «никогда не доверяйте вводу пользователя«. Это также верно для имени загруженного файла. Все отправленные данные формы могут быть подделаны, а имена файлов могут быть опасными. Просто запомните: всегда используйте эту функцию для защиты имени файла перед сохранением его непосредственно в файловой системе.
Дополнительная информация:
Какие могут быть проблемы, если не использовать secure_filename()
? Представьте, что кто-то отправит следующую информацию в качестве имени файла в ваше приложение:
filename = "../../../../home/username/.bashrc"
Предполагая, что вложенность ../
правильная, и Flask присоединит к нему папку UPLOAD_FOLDER
, то пользователь сайта может иметь возможность изменить файл в файловой системе сервера, который он или она не должны изменять. Это действительно требует некоторых знаний о том, как выглядит приложение, но поверьте, хакеры терпеливы…
Посмотрим, как работает функция secure_filename()
:
>>> from werkzeug.utils import secure_filename >>> secure_filename('. ./../../../home/username/.bashrc') # 'home_username_.bashrc'
Теперь определим функцию-представление download_file
для обслуживания файлов в папке загрузки по его имени. Функция url_for('download_file', name=name)
генерирует URL-адреса для скачивания.
from flask import send_from_directory @app.route('/uploads/<name>') def download_file(name): return send_from_directory(app.config["UPLOAD_FOLDER"], name)
Если использовать промежуточное ПО или HTTP-сервер для обслуживания файлов, например Nginx, то можно зарегистрировать конечную точку download_file
как build_only
, и тогда url_for()
будет работать без функции-представления.
app.add_url_rule( "/uploads/<name>", endpoint="download_file", build_only=True )
Ограничение размера загружаемых файлов.
Как Flask обрабатывает загрузку? Если файлы достаточно малы, то Flask будет хранить их в памяти веб-сервера, в противном случае во временном каталоге tempfile. gettempdir()
. Но как указать максимальный размер файла, после которого загрузка будет прерываться? По умолчанию Flask с радостью принимает файлы с неограниченным объемом, но такое поведение можно ограничить, установив конфигурационный ключ MAX_CONTENT_LENGTH
:
from flask import Flask, Request app = Flask(__name__) app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000
Приведенный выше код ограничивает максимально допустимую полезную нагрузку до 16 мегабайт. Если передается файл большего размера, Flask вызовет исключение RequestEntityTooLarge
.
Проблема сброса подключения: при использовании локального сервера разработки можно получить ошибку сброса соединения вместо ответа 413. При запуске приложения на боевом сервером WSGI, ответ о статусе будет правильный.
Расширение для загрузки файлов Flask-Uploads.
Расширение Flask-Uploads позволяет веб-приложению гибко и эффективно обрабатывать загрузку файлов, а так же обслуживать загруженные файлы. Flask-Uploads умеет создавать различные наборы загрузок — один для вложений документов, другой для фотографий и т. д. Веб-приложение может быть настроено так, чтобы сохранять эти наборы в разных местах и генерировать для них разные URL-адреса.
Параметры конфигурации расширения Flask-Uploads.
Можно гибко настроить поведение расширения Flask-Uploads прямо из конфигурации создаваемого веб-приложения.
Приведенные ниже настройки применяются для одного набора загрузок, замените FILES
на имя набора (например, UPLOADED_PHOTOS_DEST
):
UPLOADED_FILES_DEST
: параметр указывает на каталог, в котором будут сохранены загруженные файлы.UPLOADED_FILES_URL
: если есть сервер, настроенный для обслуживания файлов в этом наборе, то это URL-адрес, с которого загруженные файла набора будут общедоступны. В конце добавьте косую черту/
.UPLOADED_FILES_ALLOW
: параметр, разрешающий указанные расширения файлов.UPLOADED_FILES_DENY
: параметр, запрещающий расширения файлов.
Чтобы сэкономить время на настройку, можно указать две настройки, которые будут применяться как настройки по умолчанию.
UPLOADS_DEFAULT_DEST
: параметр указывает место назначения набора загрузки, если оно не объявлено иным образом. Например, если установить значение/var/uploads
, то набор с именемphotos
будет хранить свои загрузки в/var/uploads/photos
.UPLOADS_DEFAULT_URL
: это базовый URL-адрес настроенного сервера, для обслуживания файлов изUPLOADS_DEFAULT_DEST
. Продолжая приведенный выше пример, если директория/var/uploads
доступна по адресу http://example.ru/uploads, то URL-адреса для набор с именемphotos
будут начинаться с http://example.ru/uploads/photos. Включите завершающую косую черту.
Так же можно установить MAX_CONTENT_LENGTH
, чтобы ограничить размер загружаемых файлов.
Если нет настроенного сервера для обслуживания файлов, то и не нужно устанавливать какие-либо параметры *_URL
. В этом случае, загруженные файлы будут обслуживаться фреймворком Flask. НО если у вас большой трафик загрузки, то для обслуживания файлов лучше использовать более быстрый и производительный сервер, такой как Nginx или Lighttpd.
Наборы загрузок
UploadSet
.«Набор загрузок» — это единый набор файлов какой-категории. Его необходимо объявить в коде:
photos = UploadSet('photos', IMAGES)
После этого можно использовать метод .store()
, для сохранения загруженного файла в определенную директорию, после чего извлечь путь до файла и URL-адрес для доступа к нему. Например:
@app.route('/upload', methods=['GET', 'POST']) def upload(): if request.method == 'POST' and 'photo' in request.files: filename = photos.save(request.files['photo']) rec = Photo(filename=filename, user=g.user. id) rec.store() flash("Фотография сохранена.") return redirect(url_for('show', id=rec.id)) return render_template('upload.html') @app.route('/photo/<id>') def show(id): photo = Photo.load(id) if photo is None: abort(404) url = photos.url(photo.filename) return render_template('show.html', url=url, photo=photo)
Если в конфигурации указано «расположение загрузок по умолчанию» UPLOADS_DEFAULT_DEST
и например, ваше приложение имеет каталог экземпляра приложения, при этом загрузки должны сохраняться в папке upload
каталога экземпляра приложения, то можно быстро перенастроить папку для загрузки, передав аргумент default_dest
конструктору UploadSet
. Например:
media = UploadSet('media', default_dest=lambda app: app.instance_path)
Конфигурация расширения Flask-Uploads.
Конфигурация набора загрузки хранится в приложении. Таким образом, можно использовать наборы загрузки сразу в нескольких приложениях. Используйте функцию configure_uploads()
, чтобы загрузить конфигурацию для разных наборов загрузок. Функция configure_uploads()
передает приложению и все наборы загрузок. Вызов configure_uploads
более одного раза безопасен.
from flask_uploads import UploadSet, configure_uploads, IMAGES photos = UploadSet('photos', IMAGES) configure_uploads(app, photos)
Если приложение имеет фабрику приложений, то это, именно то место где нужно настраивать расширение Flask-Uploads.
По умолчанию Flask не накладывает никаких ограничений на размер загружаемых данных. Чтобы защитить приложение, можно использовать patch_request_class()
. Если вызывать patch_request_class()
со вторым параметром None
, то для ограничения максимального размера загружаемого файла будет использоваться параметр конфигурации MAX_CONTENT_LENGTH
.
from flask_uploads import patch_request_class patch_request_class(app, None)
Класс patch_request_class()
также второй параметр может быть числом, которое установить абсолютный предел, но он существует только по причинам обратной совместимости и не рекомендуется для использования. Кроме того, это не обязательно для Flask 0.6 или выше.
Форма загрузки файлов.
Чтобы действительно загрузить файлы, необходимо правильно настроить форму. Форма, которая загружает файлы, должна иметь свой метод, установленный в POST
, и свой тип enctype
, установленный в multipart/form-data
. Если метод формы настроен на GET
, то загрузка вообще не будет работать, а если не установить enctype
, то будет передано только имя файла.
Само поле должно быть <input type=file>
.
<form method=POST enctype=multipart/form-data action="{{ url_for('upload') }}"> ... <input type=file name=photo> ... </form>
Демо-версия загрузки фотографий с помощью Flask-Uploads и Flask-WTF.
Шаблон index.html
необходимо поместить в папку с именем templates
.
{# index.html #} {# Создайте папку с именем templates, поместите в нее этот файл #} <!DOCTYPE html> <title>Upload File</title> <h2>Photo Upload</h2> <form method="POST" enctype="multipart/form-data"> {{ form. hidden_tag() }} {{ form.photo }} {% for error in form.photo.errors %} <span>{{ error }}</span> {% endfor %} {{ form.submit }} </form> {% if file_url %} <br> <img src="{{ file_url }}"> {% endif %}
Это файл самого приложения для загрузки фотографий с помощью расширений Flask-Uploads и Flask-WTF
# app-upload.py import os from flask import Flask, render_template from flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_class from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileRequired, FileAllowed from wtforms import SubmitField basedir = os.path.abspath(os.path.dirname(__file__)) app = Flask(__name__) app.config['SECRET_KEY'] = 'I have a dream' # нужно будет создать папку с именем 'uploads' app.config['UPLOADED_PHOTOS_DEST'] = os.path.join(basedir, 'uploads') photos = UploadSet('photos', IMAGES) configure_uploads(app, photos) # максимальный размер файла, по умолчанию 16MB patch_request_class(app) class UploadForm(FlaskForm): photo = FileField(validators=[FileAllowed(photos, 'Image only!'), FileRequired('File was empty!')]) submit = SubmitField('Upload') @app. route('/', methods=['GET', 'POST']) def upload_file(): form = UploadForm() if form.validate_on_submit(): filename = photos.save(form.photo.data) file_url = photos.url(filename) else: file_url = None return render_template('index.html', form=form, file_url=file_url) if __name__ == '__main__': app.run()
Как загрузить файл только в формате HTML
спросил
Изменено 9 лет, 8 месяцев назад
Просмотрено 2к раз