使用django服务下载大型zip文件并附加一些数据

时间:2015-07-21 10:04:11

标签: python django nginx mod-wsgi

我有一个如下所示的视图片段,它从请求中获取一个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时,大邮件文件失败。

似乎超时因为需要太长时间才能读取大文件。

我不明白为什么我使用yield但浏览器在整个文件读取完成后开始下载。(因为我看到浏览器等到日志finished reading %s出现)

不应该在第一个块读取后立即开始下载吗?

有没有更好的方法来提供文件下载功能,我需要在文件后附加动态字符串?

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

这是StreamingHttpResponse的用例,而不是HttpResponse。

答案 2 :(得分:0)

最好使用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