Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serve a created tempfile in django

Tags:

python

django

I have a remote storage project that when the user requests his file, the django server retrieves and stores the file locally (for some processing) as a temporary file and then serves it to the user with mod x-sendfile. I certainly want the tempfile to be deleted after it is served to the user.

The documentations state that NamedTemporaryFile delete argument if set to False leads to deletion of the file after that all the references are gone. But when the user is served the tempfile, it doesn't get deleted. If I set the delete=True in case of downloading I get the "The requested URL /ServeSegment/Test.jpg/ was not found on this server."

Here is a view to list the user files:

def file_profile(request):
    obj = MainFile.objects.filter(owner=request.user)
    context = {'title': 'welcome',
               'obj': obj
               }
    return render(request, 'ServeSegments.html', context=context)

This is the view which retrieves, stores temporarily and serve the requested file:

def ServeSegment(request, segmentID):    
    if request.method == 'GET':    
        url = 'http://192.168.43.7:8000/foo/'+str(segmentID)
        r = requests.get(url, stream=True)
        if r.status_code == 200:
            with tempfile.NamedTemporaryFile(dir=
        '/tmp/Files', mode='w+b') as f:
                for chunk in r.iter_content(1024):
                    f.write(chunk)        
            response = HttpResponse()
            response['Content-Disposition'] = 'attachment; segmentID={0}'.format(f.name)
            response['X-Sendfile'] = "{0}".format(f.name)
            return response
        else:
            return HttpResponse(str(segmentID))

I guess if I could manage to return the response inside with a statement and after that, the last chunk was written, it would work as I want, but I found no solution regarding how to determine if we are in the last loop (without being hackish).

What should I do the serve the tempfile and have it deleted right after?

like image 261
Amir Afianian Avatar asked Dec 13 '15 09:12

Amir Afianian


1 Answers

Adding a generalized answer (based on Cyrbil's) that avoids using signals by doing the cleanup in a finally block.

While the directory entry is deleted by os.remove on the way out, the underlying file remains open until FileResponse closes it. You can check this by inspecting response._closable_objects[0].fileno() in the finally block with pdb, and checking open files with lsof in another terminal while it's paused.

It looks like it's important that you're on a Unix system if you're going to use this solution (see os.remove docs)

https://docs.python.org/3/library/os.html#os.remove

import os
import tempfile
from django.http import FileResponse

def my_view(request):
    try:
        tmp = tempfile.NamedTemporaryFile(delete=False)
        with open(tmp.name, 'w') as fi:
            # write to your tempfile, mode may vary
        response = FileResponse(open(tmp.name, 'rb'))
        return response
    finally:
        os.remove(tmp.name)
like image 109
whp Avatar answered Sep 26 '22 20:09

whp