[英]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 + nginx
或apache + 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.