简体   繁体   English

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

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

I have a views snippet like below, which get a zip filename form a request, and I want to append some string sign after the end of zip file 我有一个如下的视图片段,该片段从请求中获取一个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

and the readFile function as below: 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

It works fine when using runserver mode, but failed on big zip file when I use uwsgi + nginx or apache + mod_wsgi . 使用runserver模式时,它可以正常工作,但是当我使用uwsgi + nginxapache + mod_wsgi时,在大的zip文件上失败。

It seems timeout because need too long time to read a big file. 似乎超时,因为需要太长时间才能读取大文件。

I don't understand why I use yield but the browser start to download after whole file read finished.(Because I see the browser wait until the log finished reading %s appeared) 我不明白为什么要使用yield但浏览器在读取完整个文件后才开始下载。(因为我看到浏览器一直等到日志finished reading %s出现之后才开始下载)

Shouldn't it start to download right after the first chunk read? 它不应该在读取第一个块后立即开始下载吗?

Is any better way to serve a file downloading function that I need to append a dynamic string after the file? 有什么更好的服务于文件下载功能的方法,我需要在文件后附加动态字符串?

Django doesn't allow streaming responses by default so it buffers the entire response. Django默认情况下不允许流式传输响应,因此它会缓冲整个响应。 If it didn't, middlewares couldn't function the way they do right now. 否则,中间件将无法立即发挥作用。

To get the behaviour you are looking for you need to use the StreamingHttpResponse instead. 为了获得您正在寻找的行为,您需要使用StreamingHttpResponse

Usage example from the docs : 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的用例。

It's better to use FileRespose, is a subclass of StreamingHttpResponse optimized for binary files. 最好使用FileRespose,它是StreamingHttpResponse的子类,为二进制文件进行了优化。 It uses wsgi.file_wrapper if provided by the wsgi server, otherwise it streams the file out in small chunks. 如果由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