Отвечает на подтверждение загрузки и затем обрабатывает файл в Sanic

#python #api #asynchronous #sanic

#python #API #асинхронный #sanic

Вопрос:

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

 @app.route("/upload", methods=['POST'])
async def upload(request):
        try:
            file = request.files.&et("file")
        except Exception as e:
            return json({"Received": False})

    await process(file)
    return response.text("File has been uploaded successfully")


async def process(file):
    """ Processin& File and &enerate data"""
    ...
    result = await foo(data)
    
  

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

«Файл был успешно загружен»

и разорвите соединение, но все равно продолжайте обрабатывать (файл).

Ответ №1:

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

Вариант # 1 — Потоковый ответ

Этот вариант представляет собой потоковый ответ, при котором вы немедленно отправляете обратно первую часть, а затем продолжаете обработку ответов по мере необходимости. Очевидно, что это несколько зависит от того, о каком объеме обработки мы говорим. Если это будет несколько долго (выберите произвольное время, 60 секунд), то это решение может не сработать.

 import asyncio
from functools import partial

from sanic import Sanic
from sanic.response import json, stream

app = Sanic(__name__)


async def process(file, response):
    print(file, flush=True)
    await response.protocol.push_data(b"0x0")
    await response.write("File has been uploaded successfullyn")
    await asyncio.sleep(2)
    await response.write("33% processedn")
    await asyncio.sleep(2)
    await response.write("66% processedn")
    await asyncio.sleep(2)
    await response.write("100% processedn")


@app.post("/")
async def upload(request):
    try:
        file = request.files.&et("file")
    except Exception:
        return json({"Received": False})

    return stream(partial(process, file), chunked=False)

  

Вариант # 2 — Фоновая задача

Я полагаю, что это больше то, что вы ищете. Немедленный ответ (при закрытом соединении). Мы берем processin& и переводим его в фоновый режим с помощью add_task .

 import asyncio
from functools import partial

from sanic import Sanic
from sanic.response import json, text

app = Sanic(__name__)


async def process(file,):
    await asyncio.sleep(3)
    print(f"{file=}", flush=True)


@app.post("/")
async def upload(request):
    try:
        file = request.files.&et("file")
    except Exception:
        return json({"Received": False})

    request.app.add_task(process(file))

    return text("File has been uploaded successfully")
  

В качестве другой альтернативы, вот пример, который я собрал вместе, где обработчик помещает задачу в очередь, и есть отдельный «рабочий», который очищает очередь и выполняет задачи.

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

1. Большое спасибо!! Вариант № 2 был именно тем, что я искал!!

2. Идеальный вариант # 2 спас меня