Django 實現下載文件功能


Django 實現下載文件功能

基於Django建立的網站,如果提供文件下載功能,最簡單的方式莫過於將靜態文件交給Nginx等處理,但有些時候,由於網站本身邏輯,需要通過Django提供下載功能,如頁面數據導出功能(下載動態生成的文件)、先檢查用戶權限再下載文件等。因此,有必要研究一下文件下載功能在Django中的實現。

最簡單的文件下載功能的實現

將文件流放入HttpResponse對象即可,如:

from django.http import HttpResponse

def file_download(request):
    # do something...
    with open('file_name.txt') as f:
        c = f.read()
    return HttpResponse(c)

這種方式簡單粗暴,適合小文件的下載,但如果這個文件非常大,這種方式會占用大量的內存,甚至導致服務器崩潰

更合理的文件下載功能

Django的HttpResponse對象允許將迭代器作為傳入參數,將上面代碼中的傳入參數c換成一個迭代器,便可以將上述下載功能優化為對大小文件均適合;而Django更進一步,推薦使用 StreamingHttpResponse對象取代HttpResponse對象,StreamingHttpResponse對象用於將文件流發送給瀏覽器,與HttpResponse對象非常相似,對於文件下載功能,使用StreamingHttpResponse對象更合理。
因此,更加合理的文件下載功能,應該先寫一個迭代器,用於處理文件,然后將這個迭代器作為參數傳遞給StreaminghttpResponse對象,如:

def down_chunk_file_manager(file_path, chuck_size=1024):
    """
    文件分片下發
    :param file_path:
    :param chuck_size:
    :return:
    """
    with open(file_path, "rb") as file:
        while True:
            chuck_stream = file.read(chuck_size)
            if chuck_stream:
                yield chuck_stream
            else:
                break
                
file_path = "...\test.txt"
response = StreamingHttpResponse(down_chunk_file_manager(file_path))
return response

文件下載功能再次優化

上述的代碼,已經完成了將服務器上的文件,通過文件流傳輸到瀏覽器,但文件流通常會以亂碼形式顯示到瀏覽器中,而非下載到硬盤上,因此,還要在做點優化,讓文件流寫入硬盤。優化很簡單,給StreamingHttpResponse對象的Content-TypeContent-Disposition字段賦下面的值即可,如:

response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename="test.pdf"'

完整代碼如下:

from django.http import StreamingHttpResponse
 
def download_big_file(request):
    # do something...
    def down_chunk_file_manager(file_path, chuck_size=1024):
        with open(file_path, "rb") as file:
            while True:
                chuck_stream = file.read(chuck_size)
                if chuck_stream:
                    yield chuck_stream
                else:
                    break
                
    file_path = "...\test.txt"
    response = StreamingHttpResponse(down_chunk_file_manager(file_path))
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename="{0}"'.format(file_path)
 
    return response

注意:

Content-Disposition里面的filename,原來不是RFC標准,僅支持ASCII編碼的文件名。如果文件名出現中文,就會導致下載的文件出現亂碼的情況。解決方法即為:可以定義一個filename*,然后把文件名編碼一下。

response["Content-Disposition"] = "attachment; filename*=UTF-8''{}".format(escape_uri_path(file_name))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM