How to clean up temporary file used with send_file?

独自空忆成欢 提交于 2019-12-01 02:40:36

If you are using Flask 0.9 or greater you can use the after_this_request decorator:

@app.route("/method",methods=['POST'])
def api_entry():
    tempcreator = ObjectThatCreatesTemporaryFiles():
    tempcreator.createTemporaryFiles()

    @after_this_request
    def cleanup(response):
        tempcreator.__exit__()
        return response

    return send_file(tempcreator.somePath)

EDIT

Since that doesn't work, you could try using cStringIO instead (this assumes that your files are small enough to fit in memory):

@app.route("/method", methods=["POST"])
def api_entry():
    file_data = dataObject.createFileData()
    # Simplest `createFileData` method:  
    # return cStringIO.StringIO("some\ndata")
    return send_file(file_data,
                        as_attachment=True,
                        mimetype="text/plain",
                        attachment_filename="somefile.txt")

Alternately, you could create the temporary files as you do now, but not depend on your application to delete them. Instead, set up a cron job (or a Scheduled Task if you are running on Windows) to run every hour or so and delete files in your temporary directory that were created more than half an hour before.

The method I've used is to use weak-references to delete the file once the response has been completed.

import shutil
import tempfile
import weakref

class FileRemover(object):
    def __init__(self):
        self.weak_references = dict()  # weak_ref -> filepath to remove

    def cleanup_once_done(self, response, filepath):
        wr = weakref.ref(response, self._do_cleanup)
        self.weak_references[wr] = filepath

    def _do_cleanup(self, wr):
        filepath = self.weak_references[wr]
        print('Deleting %s' % filepath)
        shutil.rmtree(filepath, ignore_errors=True)

file_remover = FileRemover()

And in the flask call I had:

@app.route('/method')
def get_some_data_as_a_file():
    tempdir = tempfile.mkdtemp()
    filepath = make_the_data(dir_to_put_file_in=tempdir)
    resp = send_file(filepath)
    file_remover.cleanup_once_done(resp, tempdir)
    return resp

This is quite general and as an approach has worked across three different python web frameworks that I've used.

I have two solutions.


The first solution is to delete the file in the __exit__ method, but not close it. That way, the file-object is still accessible, and you can pass it to send_file.

This will only work if you do not use X-Sendfile, because it uses the filename.


The second solution is to rely on the garbage collector. You can pass to send_file a file-object that will clean the file on deletion (__del__ method). That way, the file is only deleted when the file-object is deleted from python. You can use TemporaryFile for that, if you don't already.

rhyek

It's a bit late, but this is what I did using madjar's suggestions (in case anyone else comes across this). This is a little helper function that I use (it takes a PyExcelerate Workbook object as parameter) which you could adapt to your case. Just change the way you create/build your tempfile.TemporaryFile and you're set! Tested on Windows 8.1 and Ubuntu 12.04.

def xlsx_to_response(wb, filename):
    f = tempfile.TemporaryFile()
    wb._save(f)
    f.seek(0)
    response = send_file(f, as_attachment=True, attachment_filename=filename,
                         add_etags=False)

    f.seek(0, os.SEEK_END)
    size = f.tell()
    f.seek(0)
    response.headers.extend({
        'Content-Length': size,
        'Cache-Control': 'no-cache'
    })
    return response
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!