Работа с файлами встречается на многих сайтах, поэтому я решил написать эту статью с кратким, но информативным содержанием.
Примеры кода можно использовать, как основу, которую при необходимости можно без каких-либо сложностей расширять под вашу конкретную задачу
Не буду долго расписывать предисловие, приступим к разбору.
Скачивание файла
Скачивание файла реализуется очень просто, фактически в одну строку:
from fastapi import FastAPI
from fastapi.responses import FileResponse
app = FastAPI()
@app.get("/file/download")
def download_file():
return FileResponse(path='data.xlsx', filename='Статистика покупок.xlsx', media_type='multipart/form-data')
Мы импортируем FileResponse
, и возвращаем его с тремя параметрами(filename
и media_type
необязательные)
Первый параметр path
указывает на файл, который будет скачан пользователем, это может быть как строка, так и os.PathLike
.
В данном случае я указал относительный путь, мой файл data.xlsx
лежит рядом с исполняемым файлом.
Параметр filename
определяет имя файла на выходе, а то есть то, с каким названием пользователь его скачает, достаточно удобная функциональность позволяющая хранить файл с удобным для нас названием, при этом пользователю отдавать его с красивым наименованием.
И последний параметр, который я использовал, это media_type
, он указывает на MIME-тип, в данном случае multipart/form-data
, указывающий, что мы ожидаем набор данных из HTML-формы.
Файл может быть абсолютно любой, я в качестве примера использовал файл Excel.
2. Как отправить файл на сервер?
Отправка файла ничем не сложнее скачивания.
Нам нужно докачать модуль python-multipart
$ pip install python-multipart
У нас есть 2 способа работы с принятым файлом:
С помощью File
и UploadFile
from fastapi import FastAPI, UploadFile, File
app = FastAPI()
@app.post("/file/upload-bytes")
def upload_file_bytes(file_bytes: bytes = File()):
return {'file_bytes': str(file_bytes)}
@app.post("/file/upload-file")
def upload_file(file: UploadFile):
return file
В методе с File
мы получаем на выходе только байт-строку, которую я преобразовал к строковому типу, для вывода в документации ответа.
Этот метод используют только в том случае, когда нам кроме содержимого файла ничего не нужно, мы прокидываем байты в Pandas(если у нас Excel-формат), например, и тогда начинаем с ним непосредственно работать.
Метод UploadFile
несколько более интересный в плане параметров, он имеет их целый набор.
Хочешь взять имя отправленного файла? Есть метод file.filename
.
Нужен сам файл, а не его байты? Метод file.file
.
Нужны байты? Пожалуйста, file.file.read()
.
Также есть и другие, которые я решил не перечислять, так как я сделал краткий гайд, а не переписывание документации(ссылку страницу которой я оставил в конце статьи)
Ну а просто file
возвращает нам словарь различных данных о файле
Вот такая получилась кратенькая статья для быстрого старта, за более подробной информацией предлагаю обратиться к документации.
Комментарии (5)
baldr
13.01.2023 10:37+4Ну это же даже меньше чем в родной справке примеров!
Сеньор программист выше очень правильно заметил про async, и размер файла, и про mime-type. Хватит уже бояться StreamingResponse - очень просто и удобно реализуется, разгружает память, процессор, сеть и прокси.
Указывая правильный MIME-тип в ответе вы подсказываете браузеру и ОС чем можно сразу открыть файл.
WondeRu
13.01.2023 12:14+2Для высоконагруженных сервисов мы делали передачу через S3: когда скачиваем - просто передаем через API подписанный URL, который экспайрится через 5 минут. А когда закачка, то клиенту тоже даем подписанный URL для закачки https://docs.aws.amazon.com/AmazonS3/latest/userguide/PresignedUrlUploadObject.html. Это все интересно, когда файлы большие, а когда маленькие, то да - проще пихать через API.
Ximik87
13.01.2023 14:18+2В чем смысл статьи? пересказ документации? или у FastApi нет документации? или описан очень пограничный кейс, с которым редко сталкиваешься?
rSedoy
А про самое интересное и ничего не сказано: тема async не раскрыта (у нас же async фреймворк), про возможные проблемы, если я буду раздавать файл 1G, он будет весь в память загружен или частями, нет ничего про насколько это проигрывает или выигрывает, если сравнивать с раздачей файлов через тот же nginx.
rSedoy
ох, только сейчас заметил, у ответа media_type это про тип файла, в данном случае это application/vnd.openxmlformats-officedocument.spreadsheetml.sheet