Django 大文件下载实现过程解析


Posted in Python onAugust 01, 2019

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python实现简单socket程序在两台电脑之间传输消息的方法
Mar 13 Python
在Linux中通过Python脚本访问mdb数据库的方法
May 06 Python
解决pandas使用read_csv()读取文件遇到的问题
Jun 15 Python
python-str,list,set间的转换实例
Jun 27 Python
Python通用循环的构造方法实例分析
Dec 19 Python
python3.4+pycharm 环境安装及使用方法
Jun 13 Python
200行python代码实现2048游戏
Jul 17 Python
将tensorflow模型打包成PB文件及PB文件读取方式
Jan 23 Python
使用Python实现牛顿法求极值
Feb 10 Python
Python基于smtplib协议实现发送邮件
Jun 03 Python
python定义类的简单用法
Jul 24 Python
Python&Matlab实现樱花的绘制
Apr 07 Python
python爬虫刷访问量 2019 7月
Aug 01 #Python
用Cython加速Python到“起飞”(推荐)
Aug 01 #Python
Python爬取视频(其实是一篇福利)过程解析
Aug 01 #Python
flask框架jinja2模板与模板继承实例分析
Aug 01 #Python
Win10环境python3.7安装dlib模块趟过的坑
Aug 01 #Python
python爬虫解决验证码的思路及示例
Aug 01 #Python
Django多数据库的实现过程详解
Aug 01 #Python
You might like
php把数据表导出为Excel表的最简单、最快的方法(不用插件)
2014/05/10 PHP
joomla实现注册用户添加新字段的方法
2016/05/05 PHP
php仿微信红包分配算法的实现方法
2016/05/13 PHP
javascript的函数、创建对象、封装、属性和方法、继承
2011/03/10 Javascript
jQuery 阴影插件代码分享
2012/01/09 Javascript
判定是否原生方法的JS代码
2013/11/12 Javascript
jQuery分组选择器用法实例
2014/12/23 Javascript
javascript如何写热点图
2015/12/08 Javascript
jQuery实现滚动鼠标放大缩小图片的方法(附demo源码下载)
2016/03/05 Javascript
js滑动提示效果代码分享
2016/03/10 Javascript
javascript将中国数字格式转换成欧式数字格式的简单实例
2016/08/02 Javascript
详解angularjs获取元素以及angular.element()用法
2017/07/25 Javascript
VueJs单页应用实现微信网页授权及微信分享功能示例
2017/07/26 Javascript
JavaScript常用截取字符串的三种方式用法区别实例解析
2018/05/15 Javascript
Vue+webpack+Element 兼容问题总结(小结)
2018/08/16 Javascript
基于JS实现数字动态变化显示效果附源码
2019/07/18 Javascript
python查找第k小元素代码分享
2013/12/18 Python
Python 时间操作例子和时间格式化参数小结
2014/04/24 Python
windows下python连接oracle数据库
2017/06/07 Python
Python 数据处理库 pandas进阶教程
2018/04/21 Python
Python中循环后使用list.append()数据被覆盖问题的解决
2018/07/01 Python
JSON文件及Python对JSON文件的读写操作
2018/10/07 Python
详解python--模拟轮盘抽奖游戏
2019/04/12 Python
django使用haystack调用Elasticsearch实现索引搜索
2019/07/24 Python
CSS3属性选择符介绍
2008/10/17 HTML / CSS
用css3实现当鼠标移进去时当前亮其他变灰效果
2014/04/08 HTML / CSS
英国优质鞋类专家:Robinson’s Shoes
2017/12/08 全球购物
美国在线自行车商店:Jenson USA
2018/05/22 全球购物
Feelunique中文官网:欧洲最大化妆品零售电商
2020/07/10 全球购物
农业局学习党的群众路线教育实践活动心得体会
2014/03/07 职场文书
中职生自荐信范文
2014/06/15 职场文书
科技工作者先进事迹
2014/08/16 职场文书
iPhone13 Pro外观确定,升级4800万镜头,4月20日发新品
2021/04/15 数码科技
解决golang post文件时Content-Type出现的问题
2021/05/02 Golang
解决Pytorch修改预训练模型时遇到key不匹配的情况
2021/06/05 Python
Go 内联优化让程序员爱不释手
2022/06/21 Golang