Как удалить файл после `return FileResponse (file_path)`

#python #python-asyncio #fastapi #starlette

#python #python-asyncio #fastapi #старлетка

Вопрос:

Я использую FastAPI для получения изображения, его обработки, а затем возврата изображения в качестве FileResponse .

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

 @app.post("/send")
async def send(imagem_base64: str = Form(...)):

    # Convert to a Pillow image
    image = base64_to_image(imagem_base64)

    temp_file = tempfile.mkstemp(suffix = '.jpeg')
    image.save(temp_file, dpi=(600, 600), format='JPEG', subsampling=0, quality=85)

    return FileResponse(temp_file)

    # I need to remove my file after return it
    os.remove(temp_file)

  

Как я могу удалить файл после его возврата?

Ответ №1:

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

 import os
import tempfile

from fastapi import FastAPI
from fastapi.responses import FileResponse

from starlette.background import BackgroundTasks

app = FastAPI()


def remove_file(path: str) -> None:
    os.unlink(path)


@app.post("/send")
async def send(background_tasks: BackgroundTasks):
    fd, path = tempfile.mkstemp(suffix='.txt')
    with os.fdopen(fd, 'w') as f:
        f.write('TESTn')
    background_tasks.add_task(remove_file, path)
    return FileResponse(path)
  

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

 import os
import tempfile

from fastapi import FastAPI, Depends
from fastapi.responses import FileResponse


app = FastAPI()


def create_temp_file():
    fd, path = tempfile.mkstemp(suffix='.txt')
    with os.fdopen(fd, 'w') as f:
        f.write('TESTn')
    try:
        yield path
    finally:
        os.unlink(path)


@app.post("/send")
async def send(file_path=Depends(create_temp_file)):
    return FileResponse(file_path)
  

Примечание: mkstemp() возвращает кортеж с дескриптором файла и путем.

Ответ №2:

Вы можете передать задачу очистки в качестве параметра FileResponse :

 from starlette.background import BackgroundTask

# ...

def cleanup():
    os.remove(temp_file)

return FileResponse(
    temp_file,
    background=BackgroundTask(cleanup),
)
  

ОБНОВЛЕНИЕ 12-08-2022

Если кто-то генерирует имя файла динамически, то можно передать параметры фоновой задаче, например, следующим образом

 return FileResponse(
    temp_file,
    background=BackgroundTask(cleanup, file_path),
)
  

Затем cleanup функцию необходимо адаптировать для принятия параметра, который будет именем файла, и вызвать os.remove функцию с именем файла в качестве параметра вместо глобальной переменной

Ответ №3:

Рекомендуется отправлять FileResponse с прикрепленной фоновой задачей, которая удаляет файл или папку.

нажмите здесь для получения дополнительной информации

Фоновая задача будет запущена после получения ответа, поэтому она может безопасно удалить файл / папку.

 # ... other important imports
from starlette.background import BackgroundTasks

@app.post("/send")
async def send(imagem_base64: str = Form(...), bg_tasks: BackgroundTasks):

    # Convert to a Pillow image
    image = base64_to_image(imagem_base64)

    temp_file = tempfile.mkstemp(suffix = '.jpeg')
    image.save(temp_file, dpi=(600, 600), format='JPEG', subsampling=0, quality=85)


    bg_tasks.add_task(os.remove, temp_file)
 
   return FileResponse(
    temp_file,
    background=bg_tasks
   )