0

When serving a static file:

@post('/download')
def downloadpage():
    return static_file('tempDS6529QSGYUA41.csv', root='temp', download=True)

how is it possible to launch an action just after the file has been successfully 100% downloaded by the client?

Example: I would like to delete the temporary file after it has been successfully downloaded by the client with os.remove('tempDS6529QSGYUA41.csv').

Note: if not possible with Bottle, I'm ok for a solution with Flask (I'm hesitating about moving to Flask which has a very similar API anyway).

Basj
  • 41,386
  • 99
  • 383
  • 673

1 Answers1

1

If you are using Linux or another Unix-like OS, you can exploit the fact, that, when when you delete a file that is kept open (static_file does that), the file vanishes from the directory but the real deletion is delayed by the OS until the file is no longer open. That means that you don't need to wait for the file to be 100% downloaded.

@post('/download')
def downloadpage():
    f = static_file('tempDS6529QSGYUA41.csv', root='temp', download=True)
    os.remove('tempDS6529QSGYUA41.csv')
    return f

or (performing the action even if an error happens while opening the file):

@post('/download')
def downloadpage():
    try:
        return static_file('tempDS6529QSGYUA41.csv', root='temp', download=True)
    finally:
        os.remove('tempDS6529QSGYUA41.csv')

If you want to perform the action only and exactly when it was 100 % downloaded, something like this should work (ignoring some corner cases):

@post('/download')
def downloadpage():

    # Ignore partial download request that would confuse our code
    if 'HTTP_RANGE' in request.environ:
        del request.environ['HTTP_RANGE']

    def wrapper_iterator(f):
        for chunk in WSGIFileWrapper(f):
            yield f

        os.remove('tempDS6529QSGYUA41.csv')

    f = static_file('tempDS6529QSGYUA41.csv', root='temp', download=True)
    return wrapper_iterator(f)
cg909
  • 2,247
  • 19
  • 23
  • Thanks @cg909. I tried method #1 and #2 on Windows but they both fail. Would there be a more general method than #3, for which there are no corner cases? – Basj Jun 26 '18 at 23:08