Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delete the file after a `return FileResponse(file_path)`

I'm using FastAPI to receive an image, process it and then return the image as a FileResponse.

But the returned file is a temporary one that need to be deleted after the endpoint return it.

@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)

How can I delete the file after return it ?

like image 852
Kleyson Rios Avatar asked Nov 06 '20 14:11

Kleyson Rios


People also ask

How do I delete a file or folder in a fileresponse?

It is recommended to send FileResponse with a background task attached that deletes the file or folder. The background task will run after the response has been served, so it can safely delete the file/folder.

How to delete a file at a specific path in Java?

The delete () method of java.nio.file .Files help us to delete a file located at the path passed as a parameter. This method may not be atomic with respect to other file system operations.

Can I delete a file in a background task?

You can delete a file in a background task, as it will run after the response is sent.

How long does background_remove (file_path) block for?

Testing on my machine, the line background_remove (file_path) blocks for 3,063 µs, while just doing os.remove (path) blocks for 65 µs. Good catch, It depends on the file size, besides, the number of files that you're trying to remove.


Video Answer


2 Answers

You can delete a file in a background task, as it will run after the response is sent.

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('TEST\n')
    background_tasks.add_task(remove_file, path)
    return FileResponse(path)

Another approach is to use dependency with yield. The finally block code will be executed after the response is sent and even after all background tasks have been completed.

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('TEST\n')
    try:
        yield path
    finally:
        os.unlink(path)


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

Note: mkstemp() returns a tuple with a file descriptor and a path.

like image 56
alex_noname Avatar answered Oct 07 '22 11:10

alex_noname


You can pass the cleanup task as a parameter of FileResponse:

from starlette.background import BackgroundTask

# ...

def cleanup():
    os.remove(temp_file)

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

UPDATE 12-08-2022

If someone is generating the filename dynamically, then one may pass the parameters to the background task, e.g., as follows

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

The cleanup function then needs to be adapted to accept a parameter, which will be the filename, and call the os.remove function with the filename as parameter instead of the global variable

like image 7
madox2 Avatar answered Oct 07 '22 11:10

madox2