FastAPI/Starlette, Веб-интерфейс: Диалоговое окно Сохранения файла, Диалоговое окно открытия файла

#python #fastapi #starlette

Вопрос:

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

Ответственный кодекс:

src/html.py :

 @app.post('/download')
def form_post(request: Request, num: int = Form(...), multiply_by_2: bool = Form(False), action: str = Form(...)):
    if action == 'convert':
        result = spell_number(num, multiply_by_2)
        return templates.TemplateResponse('download.html', context={'request': request, 'result': result, 'num': num})
    elif action == 'download':
        # Requires aiofiles
        result = spell_number(num, multiply_by_2)
        filepath = save_to_text(result, num)
        return FileResponse(filepath, media_type='application/octet-stream', filename='{}.txt'.format(num))
 

templates/download.html :

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sample Form</title>
</head>
<body>
<form method="post">
    <input type="number" name="num" value="{{ num }}"/>
    <input type="checkbox" id="multiply_by_2" name="multiply_by_2" value="True">
    <label for="multiply_by_2">Multiply by 2amp;nbsp;</label>
    <input type="submit" name="action" value="convert">
    <input type="submit" name="action" value="download">
</form>
<p>Result: {{ result }}</p>
</body>
</html>
 

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

Как это работает?

UPD, чтобы было понятнее.

Я играю с чем-то вроде этого:

 from tkinter import Tk
from tkinter.filedialog import askopenfilename
...
@app.post("/open")
def form_post(
    request: Request,
    action: str = Form("open"),
):
    if action == "open":
        root = Tk()
        root.withdraw()
        # ensure the file dialog pops to the top window
        root.wm_attributes('-topmost', 1)
        fname = askopenfilename(parent=root)
        print(f"Chosen file: {fname}")
        return templates.TemplateResponse("open.html", context={"request": request})
    elif action == "save":
        # Requires aiofiles
        return FileResponse(
            "templates/legacy/download.html",
            media_type="text/html",
            filename="download.html",
        )
 

На данный момент кнопка save использует диалоговое окно Сохранения файла системы, в то время как кнопка open использует диалоговое окно открытия tkinter. Это подойдет, потому что все это просто приложение с веб — интерфейсом. Тем не менее, это выглядит и кажется немного нелепым.

Есть ли способ заставить браузер обслуживать открытый файл Dialg?

Ответ №1:

Диалоговое окно файла-это то, что Chrome показывает для такого ответа ( application/octet-stream ). Платформа на стороне сервера не отвечает за создание диалогового окна сохранения файла; она просто предоставляет ответ, и браузер делает то, что он обычно делает для такого рода ответа.

Вы можете использовать Content-Disposition заголовок в своем ответе, чтобы указать браузеру, что он должен отображать диалоговое окно загрузки вместо содержимого напрямую (если он изначально поддерживает тип mime). Content-Disposition Заголовок также позволяет указать имя файла по умолчанию для диалогового окна сохранения (но только имя файла, а не путь).

За диалог открытия файла будет отвечать HTML, используя <input type="file" name="name_matching_fastapi_parameter"> . Затем вы можете сообщить FastAPI, что ожидаете, что параметр представляет собой файл с UploadFile типом.

Из ссылки FastAPI:

 from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes = File(...)):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
    return {"filename": file.filename}
 

Комментарии:

1. Большое вам спасибо, @MatsLindh! Поэтому application/octet-stream вызывает Диалоговое окно Сохранения файла . Мне очень жаль, но я не могу найти тип MIME, который вызвал бы диалоговое окно открытия файла . Существует ли такой тип?

2. Я вижу, что тип MIME отвечает только за тип файла (как и должно быть). Я все еще не вижу, как заказать диалоговое окно открытия файла .

3. Это было бы то, что вы делаете в своем HTML; когда пользователь нажимает кнопку «Обзор» на input type="file" элементе, откроется диалоговое окно выбора файла. Вы не можете сделать это из своего кода FastAPI; это должно быть сделано в самом HTML.

4. Я вставил <input type="file" name="action" value="name"> его в шаблон, и у меня все в порядке с открытым диалогом . Тем не менее, я не могу собрать имя файла. Удивительно, но информации о том, как это сделать, особенно относящейся к FastAPI, очень мало.

5. Если вы посмотрите на страницу для загрузки файла в качестве ссылки в ответе , вы обнаружите, что она содержит filename атрибут: имя файла: str с исходным именем файла, который был загружен (например myimage.jpg).