Remove File After Flask Serves It

Delete an uploaded file after downloading it from Flask

There are several ways to do this.

send_file and then immediately delete (Linux only)

Flask has an after_this_request decorator which could work for this use case:

@app.route('/files/<filename>/download')
def download_file(filename):
file_path = derive_filepath_from_filename(filename)
file_handle = open(file_path, 'r')
@after_this_request
def remove_file(response):
try:
os.remove(file_path)
file_handle.close()
except Exception as error:
app.logger.error("Error removing or closing downloaded file handle", error)
return response
return send_file(file_handle)

The issue is that this will only work on Linux (which lets the file be read even after deletion if there is still an open file pointer to it). It also won't always work (I've heard reports that sometimes send_file won't wind up making the kernel call before the file is already unlinked by Flask). It doesn't tie up the Python process to send the file though.

Stream file, then delete

Ideally though you'd have the file cleaned up after you know the OS has streamed it to the client. You can do this by streaming the file back through Python by creating a generator that streams the file and then closes it, like is suggested in this answer:

def download_file(filename):
file_path = derive_filepath_from_filename(filename)
file_handle = open(file_path, 'r')

# This *replaces* the `remove_file` + @after_this_request code above
def stream_and_remove_file():
yield from file_handle
file_handle.close()
os.remove(file_path)

return current_app.response_class(
stream_and_remove_file(),
headers={'Content-Disposition': 'attachment', 'filename': filename}
)

This approach is nice because it is cross-platform. It isn't a silver bullet however, because it ties up the Python web process until the entire file has been streamed to the client.

Clean up on a timer

Run another process on a timer (using cron, perhaps) or use an in-process scheduler like APScheduler and clean up files that have been on-disk in the temporary location beyond your timeout (e. g. half an hour, one week, thirty days, after they've been marked "downloaded" in RDMBS)

This is the most robust way, but requires additional complexity (cron, in-process scheduler, work queue, etc.)

Remove file after Flask serves it

after_request runs after the view returns but before the response is sent. Sending a file may use a streaming response; if you delete it before it's read fully you can run into errors.

This is mostly an issue on Windows, other platforms can mark a file deleted and keep it around until it not being accessed. However, it may still be useful to only delete the file once you're sure it's been sent, regardless of platform.

Read the file into memory and serve it, so that's it's not being read when you delete it later. In case the file is too big to read into memory, use a generator to serve it then delete it.

@app.route('/download_and_remove/<filename>')
def download_and_remove(filename):
path = os.path.join(current_app.instance_path, filename)

def generate():
with open(path) as f:
yield from f

os.remove(path)

r = current_app.response_class(generate(), mimetype='text/csv')
r.headers.set('Content-Disposition', 'attachment', filename='data.csv')
return r

Flask: delete file from server after send_file() is completed

Ok I solved it. I used the @app.after_request and used an if condition to check the endpoint,and then deleted the image

@app.after_request
def delete_image(response):
global image_name
if request.endpoint=="generate_image": //this is the endpoint at which the image gets generated
os.remove(image_name)
return response

Flask unable to delete file after send_file()

The issue here is that @after_this_request causes delete_file() to be passed in the "proposed" response object produced by test_func(). @after_this_request gives delete_file() the opportunity to modify the response before it is sent.

So, your os.remove('test.txt') is actually being called before send_file() has completed.

You can see this in the help for @after_this_request. It says "The function is passed the response object and has to return the same or a new one." So you can see from that the code will be run before the response is actually returned.

For a solution, see this question.

Flask - delete file after download

Resolved with this code.

@app.route('/path/<name>') 
def download(name):
file_path ="/path/"+name
file_handle = open(file_path, 'r')

@after_this_request
def remove_file(response):
os.remove("/path/"+name)
return response

return send_file(file_handle)

Send a file to the user, then delete file from server

Since send_file already returns the response from the endpoint, it is no longer possible to execute code afterwards.

However, it is possible to write the file to a stream before the file is deleted and then to send the stream in response.

from flask import send_file
import io, os, shutil

@app.route('/download/<path:filename>')
def download(filename):
path = os.path.join(
app.static_folder,
filename
)
cache = io.BytesIO()
with open(path, 'rb') as fp:
shutil.copyfileobj(fp, cache)
cache.flush()
cache.seek(0)
os.remove(path)
return send_file(cache, as_attachment=True, attachment_filename=filename)

In order to achieve better use of the memory for larger files, I think a temporary file is more suitable as a buffer.

from flask import send_file
import os, shutil, tempfile

@app.route('/download/<path:filename>')
def download(filename):
path = os.path.join(
app.static_folder,
filename
)
cache = tempfile.NamedTemporaryFile()
with open(path, 'rb') as fp:
shutil.copyfileobj(fp, cache)
cache.flush()
cache.seek(0)
os.remove(path)
return send_file(cache, as_attachment=True, attachment_filename=filename)

I hope your conditions are met.

Have fun implementing your project.

Remove file after Flask serves it

after_request runs after the view returns but before the response is sent. Sending a file may use a streaming response; if you delete it before it's read fully you can run into errors.

This is mostly an issue on Windows, other platforms can mark a file deleted and keep it around until it not being accessed. However, it may still be useful to only delete the file once you're sure it's been sent, regardless of platform.

Read the file into memory and serve it, so that's it's not being read when you delete it later. In case the file is too big to read into memory, use a generator to serve it then delete it.

@app.route('/download_and_remove/<filename>')
def download_and_remove(filename):
path = os.path.join(current_app.instance_path, filename)

def generate():
with open(path) as f:
yield from f

os.remove(path)

r = current_app.response_class(generate(), mimetype='text/csv')
r.headers.set('Content-Disposition', 'attachment', filename='data.csv')
return r

Flask: How to delete files from the server

I solved the issue with the following directory structure:

.
├── app.py
├── templates
│   └── delete_files.html
├── wellness.csv
└── wellness.jsonl

As you can see I have two files called wellness.csv and wellness.jsonl in the directory where I have placed my app.py file. The name wellness will be passed from the template and these two files will be deleted from the directory.

app.py:

import os
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def search():
return render_template('delete_files.html')

@app.route('/remove/<file_id>')
def remove(file_id):
filename_jsonl = f"{file_id}.jsonl"
filename_csv = f"{file_id}.csv"
try:
os.remove(filename_jsonl)
os.remove(filename_csv)
return "Files are deleted successfully"
except Exception as e:
return f"Error in deleting files: {e}"

delete_files.html:

<html>
<head>
<title>Delete files using button click in Flask</title>
</head>
<body>
<a href="/remove/wellness" id="remove" class="btn btn-success mr-2">Remove</a>
</body>
</html>

Output:

After clicking the delete button I see the message Files are deleted successfully.

after successfully deletion of the files

The folder structure after deletion of the files:

.
├── app.py
└── templates
└── delete_files.html

Update

If you want to redirect to root url after successful deletion you can use redirect method like below:

import os
from flask import Flask, render_template, redirect, url_for

app = Flask(__name__)

@app.route('/')
def search():
return render_template('delete_files.html')

@app.route('/remove/<file_id>')
def remove(file_id):
filename_jsonl = f"{file_id}.jsonl"
filename_csv = f"{file_id}.csv"
try:
os.remove(filename_jsonl)
os.remove(filename_csv)
return redirect(url_for('search'))
except Exception as e:
return f"Error in deleting files: {e}"


Related Topics



Leave a reply



Submit