Не скачиваются файлы с сервера flask

Обновлено: 03.07.2024

Я пытаюсь написать действительно просто webapp с PythonAnywhere и Flask, который позволяет пользователю загружать текстовый файл, генерирует файл csv, а затем позволяет пользователю загружать файл csv. Это не должно быть фантазией, это должно только работать. Я уже написал программу для генерации csv из txt-файла на диске.

прямо сейчас, моя функция открывает файл на диске с:

С INPUTFILE и OUTPUTFILE являются строками имени файла.

было бы лучше для меня обрабатывать файлы как объекты, возвращаемые колбой/html каким-то образом?

Я не знаю, как это делать. Как я должен структурировать эту программу? Сколько шаблонов HTML мне нужно? Я бы предпочел работать над файлами без сохранения их в любом месте, но если мне нужно сохранить их в каталоге PythonAnywhere, я мог бы. Как я могу это сделать?

PythonAnywhere dev здесь. Это хороший вопрос о колбе и веб-разработке в целом, а не конкретно для нашей системы, поэтому я постараюсь дать общий ответ без чего-либо конкретного для нас: -)

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

  • я предполагаю, что файлы, которые вы загружаете, не огромны и могут поместиться в разумный объем памяти-скажем, меньше мегабайта.
  • я предполагаю, что программа, которую вы уже написали для создания CSV из текстового файла, находится в Python, и что она имеет (или, возможно, более вероятно, может быть легко изменена) функцию, которая принимает строку, содержащую содержимое текстового файла, и возвращает содержимое, которое нужно для записи в CSV.

если оба эти случая, то лучший способ структурировать приложение колбы было бы обрабатывать все внутри колбы. Пример кода стоит тысячи слов, поэтому вот простой, который я собрал, который позволяет пользователю загружать текстовый файл, запускает его через функцию transform (где функция из вашей программы преобразования будет слот в -- mine просто заменяет = С , по всему файлу), и отправляет результаты обратно в браузер. есть живая версия этого приложения на PythonAnywhere здесь.

по поводу ваших вопросов:

  • Templates: я не использовал шаблон для этого примера, потому что я хотел, чтобы все это вписывалось в один фрагмент кода. Если бы я делал это правильно, я бы поместил материал, который генерируется form посмотреть в шаблон, но это все.
  • можете ли вы сделать это, написав в файлы - да вы может, и загруженный файл может быть сохранен с помощью save( имя файла ) метод file объект, который я использую stream собственность. Но если ваши файлы довольно малы (согласно моему предположению выше), то, вероятно, имеет смысл обрабатывать их в памяти, как это делает код выше.

Я надеюсь, что все поможет, и если у вас есть какие-либо вопросы, просто оставьте комментарий.

Если вы не добавляете тип контента, FF 48.0 сообщил об этом как html и открыл диалог сохранения один раз для HTML, а затем для CSV. Если вы не добавляете Cache-Control, ваш результат может кэшироваться, а если вы обслуживаете активный контент, это не то, что вы хотите. Если вы используете must-revalidate без возраста, он будет эффективно служить no-cache-see здесь и здесь для объяснений.

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

  1. Тег <form> помечается атрибутом enctype=multipart/form-data , а в форму помещается тег <input type=file> .
  2. Приложение получает доступ к файлу через словарь files в объекте запроса.
  3. Воспользуйтесь методом save() для того, чтобы сохранить временный файл в файловой системе для последующего использования.

Введение¶

Начнём с простейшего приложения, которое загружает файл в определённый каталог и отображает его пользователю. Вот начало нашего приложения:

Сначала нужно выполнить серию импортов. Большая часть понятна, werkzeug.secure_filename() рассматривается чуть позже. UPLOAD_FOLDER - это путь, куда будут загружаться файлы, а ALLOWED_EXTENSIONS - это набор допустимых расширений. Теперь вручную добавим в приложение правило для URL. Обычно мы так не делаем, но почему мы делаем это сейчас? Причина в том, что мы хотим заставить веб-сервер (или наш сервер приложения) обслуживать эти файлы и поэтому нам нужно генерировать правила для связывания URL с этими файлами.

Почему мы ограничили список допустимых расширений? Наверное вам совсем не хочется, чтобы пользователи могли загружать что угодно, если сервер напрямую отправляет данные клиенту. В таком случае вам нужно быть уверенными в том, что пользователи не загрузят файлы HTML, которые могут вызвать проблему XSS (см. Cross-Site Scripting ( xss ) - межсайтовый скриптинг). Также убедитесь в том, что запрещены файлы с расширением .php , если сервер их выполняет. Правда, кому нужен PHP на сервере? :)

Следующая функция проверяет, что расширение файла допустимо, загружает файл и перенаправляет пользователя на URL с загруженным файлом:

Что делает функция secure_filename() ? Мы исходим из принципа «никогда не доверяй тому, что ввёл пользователь». Это справедливо и для имени загружаемого файла. Все отправленные из формы данные могут быть поддельными и имя файла может представлять опасность. Сейчас главное запомнить: всегда используйте эту функцию для получения безопасного имени файла, если собираетесь поместить файл прямо в файловую систему.

Информация для профи

Если считать, что ../ - это нормально, то при соединении этого имени с UPLOAD_FOLDER , пользователь может получить возможность изменять на файловой системе сервера те файлы, который он не должен изменять. Нужно немного разбираться в устройстве вашего приложения, но поверьте мне, хакеры настойчивы :)

Посмотрим, как отработает функция:

Осталась последняя вещь: обслуживание загруженных файлов. Начиная с Flask 0.5 для этого можно использовать соответствующую функцию:

Другая возможность - зарегистрировать uploaded_file с помощью правила build_only и воспользоваться SharedDataMiddleware . Такой вариант будет работать и в более старых версиях Flask:

Теперь, если запустить приложение, всё должно работать как положено.

Улучшение загрузки¶

Добавлено в версии 0.6.

Как на самом деле Flask обрабатывает загрузку? Если файл достаточно мал, он сохраняется в памяти веб-сервера. В противном случае он помещается во временное место (туда, куда укажет tempfile.gettempdir() ). Но как указать максимальный размер файла, после которого загрузка файла должна быть прервана? По умолчанию Flask не ограничивает размер файла, но вы можете задать лимит настройкой ключа конфигурации MAX_CONTENT_LENGTH :

Код выше ограничит максимальный размер файла 16 мегабайтами. Если передаваемый файл окажется больше, Flask сгенерирует исключение RequestEntityTooLarge .

Эта функциональность была добавлена во Flask 0.6, но может быть реализована и в более ранних версиях при помощи наследовании от класса request. За более подробной информацией обратитесь к документации Werkzeug об обработке файлов.

Индикаторы процесса загрузки¶

Многие разработчики придумывают считывать файл мелкими частями, сохранять процент загрузки в базу данных и давать возможность JavaScript считывать эти данные из клиента. Короче говоря, клиент спрашивает у сервера каждые 5 секунд, сколько уже было передано. Почувствовали иронию ситуации? Клиент спрашивает у сервера о том, что уже и так знает.

Сейчас существуют способы получше, которые работают быстрее и более надёжны. В последнее время в вебе многое изменилось и теперь можно использовать HTML5, Java, Silverlight или Flash, чтобы сделать загрузку удобнее со стороны клиента. Посмотрите на следующие библиотеки, предназначенные именно для этого:

Простейшее решение¶

Поскольку общая процедура загрузки файлов остаётся неизменной для всех приложений, занимающихся загрузкой файлов, для Flask есть расширение под названием Flask-Uploads, которое реализует полностью самостоятельный механизм загрузки с белым и чёрным списком расширений и т.п.

Основная идея загрузки файлов во Flask на самом деле довольно проста. В основном это работает так:

  1. Тег формы помечается как enctype=multipart/form-data , и в эту форму помещается <input type=file> .
  2. Приложение обращается к файлу из словаря Request.files в объекте запроса.
  3. Используйте метод полученного объекта файла .save() , чтобы навсегда сохранить файл где-нибудь в файловой системе.

Содержание:

Ведение в загрузку файлов на Flask.

Создадим простое приложение, которое загружает файл в определенную папку и отображает его пользователю. Смотрим код инициализации веб-приложения:

Зачем ограничивать разрешенные к загрузке расширения файлов? Если сервер напрямую будет отправлять загруженные данные клиенту, то могут быть проблемы с XSS. Например загруженные HTML файлы могут содержать вредоносный JavaScript или если на сервере установлен php интерпретатор, то загруженные файлы php, с вредоносным кодом могут выполнится и т.д. Ограничивая разрешенные к загрузке расширения, мы оберегаем себя от разного рода неприятностей.

Далее напишем функции, которые проверяют, допустимо ли расширение, загружают файл и перенаправляют пользователя на URL-адрес загруженного файла:

Так что же на самом деле делает функция secure_filename() ? Проблема в том, что существует принцип, называемый "никогда не доверяйте вводу пользователя". Это также верно для имени загруженного файла. Все отправленные данные формы могут быть подделаны, а имена файлов могут быть опасными. Просто запомните: всегда используйте эту функцию для защиты имени файла перед сохранением его непосредственно в файловой системе.

Какие могут быть проблемы, если не использовать secure_filename() ? Представьте, что кто-то отправит следующую информацию в качестве имени файла в ваше приложение:

Предполагая, что вложенность ../ правильная, и Flask присоединит к нему папку UPLOAD_FOLDER , то пользователь сайта может иметь возможность изменить файл в файловой системе сервера, который он или она не должны изменять. Это действительно требует некоторых знаний о том, как выглядит приложение, но поверьте, хакеры терпеливы.

Посмотрим, как работает функция secure_filename() :

Ограничение размера загружаемых файлов.

Как Flask обрабатывает загрузку? Если файлы достаточно малы, то Flask будет хранить их в памяти веб-сервера, в противном случае во временном каталоге tempfile.gettempdir() . Но как указать максимальный размер файла, после которого загрузка будет прерываться? По умолчанию Flask с радостью принимает файлы с неограниченным объемом, но такое поведение можно ограничить, установив конфигурационный ключ MAX_CONTENT_LENGTH :

Приведенный выше код ограничивает максимально допустимую полезную нагрузку до 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 : параметр, запрещающий расширения файлов.

Чтобы сэкономить время на настройку, можно указать две настройки, которые будут применяться как настройки по умолчанию.

Так же можно установить MAX_CONTENT_LENGTH , чтобы ограничить размер загружаемых файлов.

Наборы загрузок UploadSet .

"Набор загрузок" - это единый набор файлов какой-категории. Его необходимо объявить в коде:

После этого можно использовать метод .store() , для сохранения загруженного файла в определенную директорию, после чего извлечь путь до файла и URL-адрес для доступа к нему. Например:

Если в конфигурации указано "расположение загрузок по умолчанию" UPLOADS_DEFAULT_DEST и например, ваше приложение имеет каталог экземпляра приложения, при этом загрузки должны сохраняться в папке upload каталога экземпляра приложения, то можно быстро перенастроить папку для загрузки, передав аргумент default_dest конструктору UploadSet . Например:

Конфигурация расширения Flask-Uploads.

Конфигурация набора загрузки хранится в приложении. Таким образом, можно использовать наборы загрузки сразу в нескольких приложениях. Используйте функцию configure_uploads() , чтобы загрузить конфигурацию для разных наборов загрузок. Функция configure_uploads() передает приложению и все наборы загрузок. Вызов configure_uploads более одного раза безопасен.

Если приложение имеет фабрику приложений, то это, именно то место где нужно настраивать расширение Flask-Uploads.

По умолчанию Flask не накладывает никаких ограничений на размер загружаемых данных. Чтобы защитить приложение, можно использовать patch_request_class() . Если вызывать patch_request_class() со вторым параметром None , то для ограничения максимального размера загружаемого файла будет использоваться параметр конфигурации MAX_CONTENT_LENGTH .

Класс patch_request_class() также второй параметр может быть числом, которое установить абсолютный предел, но он существует только по причинам обратной совместимости и не рекомендуется для использования. Кроме того, это не обязательно для Flask 0.6 или выше.

Форма загрузки файлов.

Чтобы действительно загрузить файлы, необходимо правильно настроить форму. Форма, которая загружает файлы, должна иметь свой метод, установленный в POST , и свой тип enctype , установленный в multipart/form-data . Если метод формы настроен на GET , то загрузка вообще не будет работать, а если не установить enctype , то будет передано только имя файла.

Само поле должно быть <input type=file> .

Демо-версия загрузки фотографий с помощью Flask-Uploads и Flask-WTF.

Шаблон index.html необходимо поместить в папку с именем templates .

Это файл самого приложения для загрузки фотографий с помощью расширений Flask-Uploads и Flask-WTF

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


Здесь может возникнуть вопрос: зачем нам создавать отдельный обработчик upload для загрузки файла? Почему бы его не реализовать сразу в profile? Дело в том, что если не разнести эти обработчики, то при обновлении страницы profile автоматически произойдет повторная загрузка этого же изображения, т.к. браузеры при refresh страницы дублируют всю информацию, в том числе и отправку данных формы. Чтобы этого не происходило, как раз и создается отдельный загрузчик, данные из формы направляются к нему, а затем, осуществляется автоматическое перенаправление в профайл. В этом случае обновление profile приведет просто к обновлению страницы без отправки данных формы.

Добавим этот функционал на наш сайт. Загруженную аватарку пользователя будем хранить непосредственно в БД. Это обычная практика в веб-программировании. Поэтому в таблице users создадим специальное поле avatar с типом BLOB:

И, затем, с помощью известной уже вам функции create_db создадим эту таблицу в БД. Но, предварительно удалим ее (иначе она не будет создана).

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

которая будет ограничивать максимальный размер загружаемого файла, в данном случае 1 Мб.

Далее, создадим шаблон отображения профайла (profile.html). Он будет следующий:

Мы здесь используем прокси-переменную current_user для обращения к авторизованному пользователю. И через нее вызываем методы класса UserLogin. Добавим этим методы:

Далее, чтобы профиль выглядел более-менее прилично, пропишем следующие стили оформления шаблона profile.html:

Отображение аватара

Для отображения аватара создадим отдельный обработчик userava:

Смотрите, мы здесь загружаем аватар пользователя с помощью метода getAvatar, который пропишем в классе UserLogin. Если данные не были получены, то возвращается пустая строка, а иначе, создается объект ответа с параметром 'Content-Type' равным 'image/png', т.к. мы для простоты будем полагать, что все загруженные аватары будут представлены в формате PNG. В результате, при отображении изображения через тег img, в профиле будем видеть соответствующий аватар текущего пользователя.

Теперь перейдем в класс UserLogin и там добавим метод получения аватара:

Мы здесь вначале проверяем: если в БД аватар не был загружен, то берется изображение по умолчанию:

А, иначе, берем аватар из БД. В конце метод возвращает прочитанные данные изображения.

Загрузка аватара

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

Здесь вначале проверяется, что пришли данные по POST-запросу и берется поле file из объекта request. Это поле ассоциировано с загруженным на сервер файлом, который был выбран через форму в профайле. Далее, идет проверка, что файл был успешно загружен и что его расширение PNG. Затем, происходит чтение данных из файла и обновляется аватар пользователя в БД.

Чтобы этот обработчик работал, нужно добавить два метода. Первый verifyExt в классе UserLogin:

И второй – updateUserAvatar в классе FDataBase:

Все, теперь наш функционал полностью готов и пользователи могут загружать свои аватарки в профайл.

Видео по теме
























© 2021 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта

Читайте также: