Django中如何实现文件下载功能

本文最后更新于2018年5月12日,已超过 1 年没有更新,如果文章内容失效,还请反馈给我,谢谢!

=Start=

缘由:

学习、整理一下最近看到的知识点,方便以后参考。

正文:

参考解答:
方法一(直接HttpResponse返回文件内容):
# 简单粗暴,适合小文件的下载
def simple_file_download(request):
    with open('manage.py') as f:
        c = f.read()
    return HttpResponse(c)

&

# 借助 FileWrapper 将文件对象包装成一个迭代器
def filewrapper_download(request):
    the_file_name = "manage.py"

    wrapper = FileWrapper(file(the_file_name))
    response = HttpResponse(wrapper)
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename="{0}"'.format(the_file_name)
    return response
方法二(使用StreamingHttpResponse返回文件内容):
def big_file_download(request):
    def file_iterator(file_name, chunk_size=512):
        with open(file_name) as f:
            while True:
                c = f.read(chunk_size)
                if c:
                    yield c
                else:
                    break

    the_file_name = "manage.py"
    response = StreamingHttpResponse(file_iterator(the_file_name))
    return response
方法三(使用FileResponse返回文件内容):
def fileresponse_download(request):
    the_file_name = "manage.py"

    response = FileResponse(file(the_file_name))
    return response
方法四(添加上「Content-Type」和「Content-Disposition」header字段):
def fileresponse_download(request):
    the_file_name = "manage.py"

    response = FileResponse(file(the_file_name))
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename="{0}"'.format(the_file_name)
    return response
方法五(先让Django做权限判断,然后让静态服务器处理下载):

不管怎么样,使用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_files/{0}".format(book.myBook.name)
    return response

*「方法五」暂未实际测试*,先记录至此供参考。

 

参考链接:

=END=

声明: 除非注明,ixyzero.com文章均为原创,转载请以链接形式标明本文地址,谢谢!
https://ixyzero.com/blog/archives/3925.html

《Django中如何实现文件下载功能》上的一个想法

  1. Django框架防止目录穿越——从路由传参说起
    `
    本文从目录遍历漏洞入手,分析了django框架处理url传递的逻辑,以及如何正确控制风险点避免出现问题代码,遵循的原则就是不要相信用户的输入,严格控制每一个参数。
    `
    https://kylingit.com/blog/django%E6%A1%86%E6%9E%B6%E9%98%B2%E6%AD%A2%E7%9B%AE%E5%BD%95%E7%A9%BF%E8%B6%8A%E4%BB%8E%E8%B7%AF%E7%94%B1%E4%BC%A0%E5%8F%82%E8%AF%B4%E8%B5%B7/
    https://docs.djangoproject.com/en/2.0/topics/http/urls/
    http://www.lijiejie.com/python-django-directory-traversal/
    https://www.leavesongs.com/PENETRATION/arbitrary-files-read-via-static-requests.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注