Django 大文件下载实现过程解析
作者:再见紫罗兰 发布时间:2021-12-18 20:48:32
django提供文件下载时,若果文件较小,解决办法是先将要传送的内容全生成在内存中,然后再一次性传入Response对象中:
def simple_file_download(request):
# do something...
content = open("simplefile", "rb").read()
如果文件非常大时,最简单的办法就是使用静态文件服务器,比如Apache或者Nginx服务器来处理下载。不过有时候,我们需要对用户的权限做一下限定,或者不想向用户暴露文件的真实地址,或者这个大内容是临时生成的(比如临时将多个文件合并而成的),这时就不能使用静态文件服务器了。
django文档中提到,可以向HttpResponse传递一个迭代器,流式的向客户端传递数据。
要自己写迭代器的话,可以用yield:
def read_file(filename, buf_size=8192):
with open(filename, "rb") as f:
while True:
content = f.read(buf_size)
if content:
yield content
else:
break
def big_file_download(request):
filename = "filename"
response = HttpResponse(read_file(filename))
return response
或者使用生成器表达式,下面是django文档中提供csv大文件下载的例子:
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 {0}".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
python也提供一个文件包装器,将类文件对象包装成一个迭代器:
class FileWrapper:
"""Wrapper to convert file-like objects to iterables"""
def __init__(self, filelike, blksize=8192):
self.filelike = filelike
self.blksize = blksize
if hasattr(filelike,'close'):
self.close = filelike.close
def __getitem__(self,key):
data = self.filelike.read(self.blksize)
if data:
return data
raise IndexError
def __iter__(self):
return self
def next(self):
data = self.filelike.read(self.blksize)
if data:
return data
raise StopIteration
使用时:
from django.core.servers.basehttp import FileWrapper
from django.http import HttpResponse
import os
def file_download(request,filename):
wrapper = FileWrapper(open(filename, 'rb'))
response = HttpResponse(wrapper, content_type='application/octet-stream')
response['Content-Length'] = os.path.getsize(path)
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response
django也提供了StreamingHttpResponse类来代替HttpResponse对流数据进行处理。
压缩为zip文件下载:
import os, tempfile, zipfile
from django.http import HttpResponse
from django.core.servers.basehttp import FileWrapper
def send_zipfile(request):
"""
Create a ZIP file on disk and transmit it in chunks of 8KB,
without loading the whole file into memory. A similar approach can
be used for large dynamic PDF files.
"""
temp = tempfile.TemporaryFile()
archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)
for index in range(10):
filename = __file__ # Select your files here.
archive.write(filename, 'file%d.txt' % index)
archive.close()
wrapper = FileWrapper(temp)
response = HttpResponse(wrapper, content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=test.zip'
response['Content-Length'] = temp.tell()
temp.seek(0)
return response
不过不管怎么样,使用django来处理大文件下载都不是一个很好的注意,最好的办法是django做权限判断,然后让静态服务器处理下载。
这需要使用sendfile的机制:"传统的Web服务器在处理文件下载的时候,总是先读入文件内容到应用程序内存,然后再把内存当中的内容发送给客户端浏览器。这种方式在应付当今大负载网站会消耗更多的服务器资源。sendfile是现代操作系统支持的一种高性能网络IO方式,操作系统内核的sendfile调用可以将文件内容直接推送到网卡的buffer当中,从而避免了Web服务器读写文件的开销,实现了“零拷贝”模式。 "
Apache服务器里需要mod_xsendfile模块来实现,而Nginx是通过称为X-Accel-Redirect的特性来实现。
nginx配置文件:
# Will serve /var/www/files/myfile.tar.gz
# When passed URI /protected_files/myfile.tar.gz
location /protected_files {
internal;
alias /var/www/files;
}
或者
# Will serve /var/www/protected_files/myfile.tar.gz
# When passed URI /protected_files/myfile.tar.gz
location /protected_files {
internal;
root /var/www;
}
注意alias和root的区别。
django中:
response['X-Accel-Redirect']='/protected_files/%s'%filename
这样当向django view函数发起request时,django负责对用户权限进行判断或者做些其它事情,然后向nginx转发url为/protected_files/filename的请求,nginx服务器负责文件/var/www/protected_files/filename的下载:
@login_required
def document_view(request, document_id):
book = Book.objects.get(id=document_id)
response = HttpResponse()
name=book.myBook.name.split('/')[-1]
response['Content_Type']='application/octet-stream'
response["Content-Disposition"] = "attachment; filename={0}".format(
name.encode('utf-8'))
response['Content-Length'] = os.path.getsize(book.myBook.path)
response['X-Accel-Redirect'] = "/protected/{0}".format(book.myBook.name)
return response
来源:https://www.cnblogs.com/linxiyue/p/4187484.html


猜你喜欢
- 本文实例讲述了Python机器学习之scikit-learn库中KNN算法的封装与使用方法。分享给大家供大家参考,具体如下:1、工具准备,p
- 内容摘要:现在博客很流行,相信应该上网时间稍微长点的朋友都会在这或者在那的有一个自己的博客。对于一些有一定能力的朋友,可能更喜欢自己去下载一
- 元字符(Meta Characters)是正则表达式中具有特殊意义的专用字符,在Python中也不例外,是用来指明前导字符(位于元字符前的字
- 本文实例讲述了MySQL切分查询用法。分享给大家供大家参考,具体如下:对于大查询有时需要‘分而治之',将大查询切分为小查询: 每个查
- python中类的继承:子类继承父类,及子类拥有了父类的 属性 和 方法。python中类的初始化都是__init__()。所以父类和子类的
- 虚拟环境管理今天聊聊Python中虚拟环境的价值和常用命令。为什么会有虚拟环境?在实际项目开发中,我们通常会根据自己的需求去下载各种相应的框
- python urllib urlopen()对象方法/代理的补充说明urllib 是 python 自带的一个抓取网页信息一个接口,他最主
- 1. 前言对于列表类型的大量数据,前端展示往往采用 分页 和 无限滚动 的方式来展示,对于用户来说,鼠标滚轮和触控屏使滚动行为要比点击更快更
- 事务特性1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。2、一致性(Consiste
- 前言提示:以下是本篇文章正文内容🧡基本概念🌳树的定义树是n(n≥0)个结点的有限集合,n = 0时,称为空树,这是一种特殊情况在任意一棵非空
- 之前有个程序,里面有个时间部分是按照国内时区,也就是东八区,来写的,程序中定义了北京时间2点到八点进行检查;后面程序在国外机器上,例如说韩国
- 视觉设计是什么,人们怎么认为它的,自己又是怎么对待和理解它,它的核心价值是什么。视觉设计,冒似很艺术,跟艺术相关的职业,给大多数人的印象是做
- python2.7安装目录下没有Scripts文件夹。这种问题可能是你装的python安装包年代久远了,到官网下载最新的python2.7安
- RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用。RSS搭建了信息迅速传
- WeTest 导读小程序科普类的文章已经很多了,今天这里讲的是针对小程序的优化方法,可以有效提高小程序的响应速度和用户体验。当然,开发体验也
- 在讲这个问题之前让我们来先看一段代码: dim sql_injdata,SQL_inj,SQL_Get,SQL_Data,Sql_
- 本文为大家分享了Mysql WorkBench安装配置图文教程,供大家参考,具体内容如下workbench下载地址下载到workbench下
- Oracle to_char函数的功能是将数值型或者日期型转化为字符型,下面就为您详细介绍Oracle to_char函数的使用,希望对您能
- 1.CUDA驱动和CUDA Toolkit对应版本表一:CUDA驱动及CUDA Toolkit最高对应版本最新可查阅官方文档注:驱动是向下兼
- 卷积函数python提供了多种卷积方案,相比之下,定义在ndimage中的卷积函数,在功能上比numpy和signal中的卷积要稍微复杂一些