【问题标题】:How to serve a created tempfile in django如何在 django 中提供创建的临时文件
【发布时间】:2015-12-13 09:24:35
【问题描述】:

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

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

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

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

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

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))

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

我应该如何处理tempfile 并在之后将其删除?

【问题讨论】:

  • 除非您可以以浏览器可以解释的某种形式发送文件(例如base64),否则无法保证在您删除文件之前会提供该文件。请记住,返回响应并不能保证浏览器届时将拥有该文件。
  • 不确定你想要什么,但这个包可能对你有用,至少对于测试django-ajax-upload-widget
  • @LorenzoPeña 那么我该如何实现呢?有什么链接可以参考吗?

标签: python django


【解决方案1】:

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

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

【讨论】:

  • 您能解释一下这如何消除对信号的需求吗? finally 将在返回时运行,并且 FileResponse 似乎没有读取构造函数中的文件。
  • 谢谢 - 添加了一些修改。这是一个依赖于平台的解决方案,这可能是您看到它失败的原因。
  • 感谢您的更新。出于某种原因,这对我来说并没有发生(我使用 Ubuntu)。通过使用终端 3 终端 cat > a.txttail -f a.txtrm -f a.txt。我看你是对的,文件被删除后tail会继续更新。
【解决方案2】:

一旦文件处理程序关闭,tempfile 创建的任何文件都将被删除。在您的情况下,当您退出 with 语句时。 delete=False 参数阻止了这种行为,并让应用程序删除。您可以registering a signal 处理程序发送文件后删除文件,一旦发送响应,该处理程序将取消链接文件。

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

【讨论】:

  • 我认为信号是要走的路,实际上在我的最终项目中,我有一些算法,比如秘密共享,它将上传的文件转换为 N 个片段,在下载阶段 django 服务器检索这些片段,并且必须负责组合他们,这是将要发生的真实过程,我想我也将不得不使用 TempDirectory。
【解决方案3】:

一次性文件 该问题的解决方案是不在 NamedTemporaryFile 中使用 with 并处理异常。目前,您的文件在您阅读之前已被删除。最后返回

f.seek(0)
return FileResponse(f, as_attachment=True, filename=f.name)

读取完成后临时文件将被关闭,因此被删除。

非一次性文件

对于那些偶然发现没有自动一次性文件句柄的人。

从其他答案来看,信号似乎是一个合理的解决方案,但是传递数据需要更改受保护的成员。我不确定它将来会得到多大的支持。我还发现 whp 的解决方案在当前版本的 Django 中不起作用。我能想到的最面向未来的版本是猴子修补文件输出,以便在关闭时删除文件。 Django 在发送文件结束时关闭文件句柄,我看不到这种变化。

def my_view(request):
  tmp = tempfile.NamedTemporaryFile(delete=False)

  try:
    # write file tmp (remember to close if re-opening)
    # after write close the file (if not closed)      

    stream_file = open(tmp.name, 'rb')

    # monkey patch the file
    original_close = stream_file.close

    def new_close():
        original_close()
        os.remove(tmp.name)

    stream_file.close = new_close

    # return the result
    return FileResponse(stream_file, as_attachment=True, filename='out.txt')
  except Exception:
      os.remove(output.name)
      raise

【讨论】:

    猜你喜欢
    • 2011-05-19
    • 1970-01-01
    • 2010-11-04
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 1970-01-01
    • 2012-06-14
    • 2011-03-26
    相关资源
    最近更新 更多