簡體   English   中英

使用django提供下載大zip文件並附加一些數據的服務

[英]use django to serve downloading big zip file with some data appended

我有一個如下的視圖片段,該片段從請求中獲取一個zip文件名,並且我想在zip文件末尾附加一些字符串sign

@require_GET
def download(request):
    ... skip
    response = HttpResponse(readFile(abs_path, sign),  content_type='application/zip')
    response['Content-Length'] = os.path.getsize(abs_path) + len(sign)
    response['Content-Disposition'] = 'attachment; filename=%s' % filename
    return response

readFile函數如下:

def readFile(fn, sign, buf_size=1024<<5):
    f = open(fn, "rb")
    logger.debug("started reading %s" % fn)
    while True:
        c = f.read(buf_size)
        if c:
            yield c
        else:
            break
    logger.debug("finished reading %s" % fn)
    f.close()
    yield sign

使用runserver模式時,它可以正常工作,但是當我使用uwsgi + nginxapache + mod_wsgi時,在大的zip文件上失敗。

似乎超時,因為需要太長時間才能讀取大文件。

我不明白為什么要使用yield但瀏覽器在讀取完整個文件后才開始下載。(因為我看到瀏覽器一直等到日志finished reading %s出現之后才開始下載)

它不應該在讀取第一個塊后立即開始下載嗎?

有什么更好的服務於文件下載功能的方法,我需要在文件后附加動態字符串?

Django默認情況下不允許流式傳輸響應,因此它會緩沖整個響應。 否則,中間件將無法立即發揮作用。

為了獲得您正在尋找的行為,您需要使用StreamingHttpResponse

docs中的用法示例:

import csv

from django.utils.six.moves import range
from django.http import StreamingHttpResponse

class Echo(object):
    """An object that implements just the write method of the file-like
    interface.
    """
    def write(self, value):
        """Write the value by returning it, instead of storing in a buffer."""
        return value

def some_streaming_csv_view(request):
    """A view that streams a large CSV file."""
    # Generate a sequence of rows. The range is based on the maximum number of
    # rows that can be handled by a single sheet in most spreadsheet
    # applications.
    rows = (["Row {}".format(idx), str(idx)] for idx in range(65536))
    pseudo_buffer = Echo()
    writer = csv.writer(pseudo_buffer)
    response = StreamingHttpResponse((writer.writerow(row) for row in rows),
                                     content_type="text/csv")
    response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
    return response

這是StreamingHttpResponse而不是HttpResponse的用例。

最好使用FileRespose,它是StreamingHttpResponse的子類,為二進制文件進行了優化。 如果由wsgi服務器提供,它將使用wsgi.file_wrapper,否則它將以小塊流式傳輸文件。

import os
from django.http import FileResponse
from django.core.servers.basehttp import FileWrapper


def download_file(request):
    _file = '/folder/my_file.zip'
    filename = os.path.basename(_file)
    response = FileResponse(FileWrapper(file(filename, 'rb')), content_type='application/x-zip-compressed')
    response['Content-Disposition'] = "attachment; filename=%s" % _file
    return response

暫無
暫無

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

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