[Django]-Django StreamingHttpResponse deleting file after user finishes downloading it

2๐Ÿ‘

I have tried the above suggestion by @mwag; however with FileWrapper.

In my case, wanted to zip up directories and get rid of the archive when download is done.

import os, time, zipfile
from django.http import StreamingHttpResponse
from wsgiref.util import FileWrapper

class FileDeleteWrapper(FileWrapper):
    def __init__(self, filepath, *args, **kwargs):
        self.filepath = filepath
        super(FileDeleteWrapper, self).__init__(*args, **kwargs)

    def __del__(self, *args, **kwargs):
        os.remove(self.filepath)
# View function
def zipFiles(request, assetId):
    asset = get_object_or_404(Asset, id=assetId)
    try:
        files = File.objects.filter(asset=asset)
        prefix = str(time.time()) +'_'

        zipPath = os.path.join(
            settings.ZIPPED_FILES_DIR,
            prefix + asset.label+'.zip'
        )

        z = zipfile.ZipFile(zipPath, 'w', zipfile.ZIP_DEFLATED)

        for f in files:
            path = os.path.join(
                mainSet.MEDIA_ROOT,
                str(f.file)
            )
            z.write(path, str(f))
        z.close()

        chunkSize = 16384

        response = StreamingHttpResponse(
            FileDeleteWrapper(
                filepath = zipPath,
                filelike=open(zipPath, 'rb'),
                blksize=chunkSize
            )
        )
        response['Content-Length'] = os.path.getsize(zipPath)
        response['Content-Disposition'] = "attachment; filename=%s" % asset.label+'.zip'
        return response
    except Exception as e:
            if mainSet.DEBUG:
                print(type(e))
            else:
                # log expception
                raise Http404

๐Ÿ‘คAbbas

1๐Ÿ‘

Try creating a class that will delete the file when it is gcโ€™d. For example, something like the below might work:

class open_then_delete(object):
    def __init__(self, filename, mode='rb'):
        self.filename = filename
        self.file_obj = open(filename, mode)

    def __del__(self):
        self.close()


    def close(self):
        if self.file_obj:
           self.file_obj.close()
           self.file_obj = None
        self.cleanup()

    def cleanup(self):
        if self.filename:
            try:
                sys.stderr.write('open_then_delete: del ' + self.filename)
                os.remove(self.filename)
            except:
                pass
            self.filename = None

    def __getattr__(self, attr):
        return getattr(self.file_obj, attr)

    def __iter__(self):
        return iter(self.file_obj)

# below is your code, modified to use use_then_delete
def down(request, file_name):
    if request.method == 'GET':
        if file_name:
            import os
            fh = get_object_or_404(FileHandler, filename=file_name)
            csv = open_then_delete(os.path.join(fh.path, fh.filename))

            response = StreamingHttpResponse(csv.open(), content_type='text/csv')
            response['Content-Disposition'] = 'attachment; filename="{}"'.format(fh.filename)
            return response
    return HttpResponseRedirect('/b2b/export/')
๐Ÿ‘คmwag

Leave a comment