在Django中返回HttpResponse后删除临时文件

13 投票
7 回答
14071 浏览
提问于 2025-04-16 03:22

我正在使用以下的django/python代码将一个文件传输到浏览器:

wrapper = FileWrapper(file(path))
response = HttpResponse(wrapper, content_type='text/plain')
response['Content-Length'] = os.path.getsize(path)
return response

有没有办法在响应返回后删除这个文件呢?可以用回调函数之类的方式吗?我可以设置一个定时任务来删除所有临时文件,但如果能在同一个请求中既传输文件又删除文件,那样会更整洁。

7 个回答

2

通常,我们会使用定期的cron任务来处理这个问题。

Django已经有一个定期任务,用来清理那些失效的会话。你应该已经在运行这个任务了,对吧?

你可以查看这个链接了解更多信息:http://docs.djangoproject.com/en/dev/topics/http/sessions/#clearing-the-session-table

你可能还想在你的应用里添加一个类似的命令,用来清理旧文件。

可以参考这个链接来了解如何创建自定义管理命令:http://docs.djangoproject.com/en/dev/howto/custom-management-commands/

另外,你可能并不是直接通过Django来发送这个文件。有时候,在Apache使用的目录中创建文件,然后通过重定向到一个网址,让Apache来为你提供这个文件,会有更好的性能。这种方式有时会更快。不过,它在清理方面并没有更好的效果。

6

为了以后参考: 我遇到了一种情况,就是无法使用临时文件来下载。 但我还是需要在下载后删除这些文件,所以我就这样做了(我真的不想依赖定时任务或者其他复杂的东西,这个系统很小,我希望它保持简单)。

def plug_cleaning_into_stream(stream, filename):
    try:
        closer = getattr(stream, 'close')
        #define a new function that still uses the old one
        def new_closer():
            closer()
            os.remove(filename)
            #any cleaning you need added as well
        #substitute it to the old close() function
        setattr(stream, 'close', new_closer)
    except:
        raise

然后我就把用来处理响应的流直接接上去了。

def send_file(request, filename):
    with io.open(filename, 'rb') as ready_file:
        plug_cleaning_into_stream(ready_file, filename)
        response = HttpResponse(ready_file.read(), content_type='application/force-download')
        # here all the rest of the heards settings
        # ...
        return response

我知道这样做有点粗糙,但确实有效。我怀疑这种方法对于每秒有成千上万请求的服务器来说不太合适,但在我这里并不是这样(最多每分钟几十个请求)。

补充说明:我忘了说我处理的是非常非常大的文件,这些文件在下载时无法全部放进内存。所以我才使用了一个 BufferedReader(这就是 io.open() 背后的实现)。

23

你可以使用一个命名的临时文件:

from django.core.files.temp import NamedTemporaryFile
def send_file(request):
    newfile = NamedTemporaryFile(suffix='.txt')
    # save your data to newfile.name
    wrapper = FileWrapper(newfile)
    response = HttpResponse(wrapper, content_type=mime_type)
    response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(modelfile.name)
    response['Content-Length'] = os.path.getsize(modelfile.name)
    return response

这个临时文件在新的文件对象被移除后会自动删除。

撰写回答