簡體   English   中英

如何在 Django 中提供創建的臨時文件

[英]How to serve a created tempfile in django

我有一個遠程存儲項目,當用戶請求他的文件時,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))

我想如果我能設法用語句返回內部的響應,然后寫入最后一個塊,它會按我想要的方式工作,但是我沒有找到關於如何確定我們是否在最后一個循環中的解決方案(沒有被駭人聽聞)。

我應該怎么做才能為tempfile提供服務並在之后立即將其刪除?

添加一個通用答案(基於 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)

一旦文件處理程序關閉,任何由tempfile創建的tempfile都將被刪除。 在您的情況下,當您退出with語句時。 delete=False參數可以防止這種行為,並讓應用程序進行刪除。 可以通過注冊一個信號處理程序在發送后刪除文件,該處理程序將在發送響應后取消鏈接文件。

您的示例對文件不執行任何操作,因此您可能希望直接使用StreamingHttpResponseFileResponse StreamingHttpResponse內容。 但是正如您所說,您“將文件存儲在本地(用於某些處理)” ,我建議您考慮在不創建任何臨時文件的情況下進行處理,並且僅使用流。

一次性文件 問題的解決方案是不要在 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM