如何在 Django 中提供创建的临时文件

Mii*_*ind 5 python django

我有一个远程存储项目,当用户请求他的文件时,django 服务器在本地检索和存储文件(用于某些处理)作为临时文件,然后使用 mod x-sendfile 将其提供给用户。我当然希望在向用户提供临时文件后将其删除。

文档指出,NamedTemporaryFile如果设置为 delete 参数,则False在所有引用都消失后会导致删除文件。但是当为用户提供服务时tempfile,它不会被删除。如果我在下载时设置 delete=True,我会收到“/ServeSegment/Test.jpg/在此服务器上找不到请求的 URL ”。

这是列出用户文件的视图:

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

这是检索、临时存储和提供请求文件的视图:

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))
Run Code Online (Sandbox Code Playgroud)

我想如果我能设法用语句返回内部的响应,然后写入最后一个块,它会按我想要的方式工作,但是我没有找到关于如何确定我们是否在最后一个循环中的解决方案(没有被骇人听闻)。

我应该怎么做服务tempfile并在之后立即将其删除?

whp*_*whp 6

添加一个通用答案(基于 Cyrbil 的),通过在 finally 块中进行清理来避免使用信号。

虽然目录条目在出路时被 os.remove 删除,但底层文件保持打开状态,直到 FileResponse 关闭它。您可以通过response._closable_objects[0].fileno()使用 pdb 在 finally 块中检查并lsof在暂停时在另一个终端中检查打开的文件来检查这一点。

如果您要使用此解决方案,那么您在 Unix 系统上似乎很重要(请参阅 os.remove 文档)

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)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢-添加了一些编辑。这是一个依赖于平台的解决方案,这可能就是您看到它失败的原因。 (2认同)

Cyr*_*bil 5

tempfile文件处理程序关闭后,创建的任何文件都将被删除。就您而言,当您退出with声明时。该delete=False参数可以阻止这种行为,并让应用程序自行删除。您可以通过注册信号处理程序在发送文件后删除文件,该信号处理程序将在发送响应后取消链接文件。

您的示例对文件不执行任何操作,因此您可能希望直接使用StreamingHttpResponse或 流式传输内容FileResponse。但正如您所说,您“在本地存储文件(用于某些处理)”,我建议考虑在不创建任何临时文件的情况下进行处理,并且仅使用流。