使用StreamHttpResponse和FileResponse下载文件的注意事项及文件私有化
为什么需要编写下载视图方法?
你或许知道,我们上传的文件默认放在media文件夹中的,且Django会为每个上传的静态文件分配一个静态url。在模板中,你可以使用{{ mymodel.file.url }}获取每个文件的链接(url),浏览器也是可以直接打开这个url的,如下所示。
<td><a href="/media/files/b1957d79f3.JPG/">/media/files/b1957d79f3.JPG</a></td>
然而当你碰到如下2种情况时,你需要编写自己的视图下载方法。
你希望用户以附件形式获得文件,而不是浏览器直接打开。
你希望允许用户下载一些保密文件,而不希望在html模板中暴露它们。
具体思路
我们先新建一个file_download的app,添加如下urls。该URL了包含了一个文件的相对路径file_path作为参数, 其对应视图是file_download方法。我们现在就开始尝试用不同方法来处理文件下载。
from django.urls import path, re_path
from . import views
# namespace
app_name = 'file_download'
urlpatterns = [
re_path(r'^download/(?P<file_path>.*)/$', views.file_download, name='file_download'),
]
模板templates/file_upload/file_list.html改为如下所示, 而不再采用option 1。
{% for file in files %}
<tr>
<!-- Option 1 <td><a href="{{ file.file.url }}/">{{ file.file.url }}</a></td> -->
<td><a href="/file/download{{ file.file.url }}/">{{ file.file.url }}</a></td>
<td>{{ file.file.size | filesizeformat }}</td>
<td>{{ file.upload_method }}</td>
</tr>
{% endfor %}
方法一: 使用HttpResonse
下面方法从url获取file_path, 打开文件,读取文件,然后通过HttpResponse方法输出。
import os
from django.http import HttpResponse
def file_download(request, file_path):
# do something...
with open(file_path) as f:
c = f.read()
return HttpResponse(c)
然而该方法有个问题,如果文件是个二进制文件,HttpResponse输出的将会是乱码。对于一些二进制文件(图片,pdf),我们更希望其直接作为附件下载。当文件下载到本机后,用户就可以用自己喜欢的程序(如Adobe)打开阅读文件了。这时我们可以对上述方法做出如下改进, 给response设置content_type和Content_Disposition。
import os
from django.http import HttpResponse, Http404
def media_file_download(request, file_path):
with open(file_path, 'rb') as f:
try:
response = HttpResponse(f)
response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
return response
except Exception:
raise Http404
HttpResponse有个很大的弊端,其工作原理是先读取文件,载入内存,然后再输出。如果下载文件很大,该方法会占用很多内存。对于下载大文件,Django更推荐StreamingHttpResponse和FileResponse方法,这两个方法将下载文件分批(Chunks)写入用户本地磁盘,先不将它们载入服务器内存。
方法二: 使用SteamingHttpResonse
import os
from django.http import HttpResponse, Http404, StreamingHttpResponse
def stream_http_download(request, file_path):
try:
response = StreamingHttpResponse(open(file_path, 'rb'))
response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
return response
except Exception:
raise Http404
方法三: 使用FileResonse
FileResponse方法是SteamingHttpResponse的子类,是小编我推荐的文件下载方法。如果我们给file_response_download加上@login_required装饰器,那么我们就可以实现用户需要先登录才能下载某些文件的功能了。
import os
from django.http import HttpResponse, Http404, FileResponse
def file_response_download1(request, file_path):
try:
response = FileResponse(open(file_path, 'rb'))
response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
return response
except Exception:
raise Http404
然而即使加上了@login_required的装饰器,用户只要获取了文件的链接地址, 他们依然可以通过浏览器直接访问那些文件。我们等会再谈保护文件的链接地址和文件私有化,因为此时我们还有个更大的问题需要担忧。我们定义的下载方法可以下载所有文件,不仅包括.py文件,还包括不在media文件夹里的文件(比如非用户上传的文件)。比如当我们直接访问127.0.0.1:8000/file/download/file_project/settings.py/时,你会发现我们连file_project目录下的settings.py都下载了。如果哪个程序员这么蠢,你可以将他直接fire了。所以我们在编写下载方法时,我们一定要限定那些文件可以下,哪些不能下或者限定用户只能下载media文件夹里的东西。
def file_response_download(request, file_path):
ext = os.path.basename(file_path).split('.')[-1].lower()
# cannot be used to download py, db and sqlite3 files.
if ext not in ['py', 'db', 'sqlite3']:
response = FileResponse(open(file_path, 'rb'))
response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
return response
else:
raise Http404
文件私有化的两种方法
如果你想实现只有登录过的用户才能查看和下载某些文件,大概有两种方法,这里仅提供思路。
上传文件放在media文件夹,文件名使用很长的随机字符串命名(uuid), 让用户无法根据文件名猜出这是什么文件。视图和模板里验证用户是否已登录,登录或通过权限验证后才显示具体的url。- 简单易实现,安全性不高,但对于一般项目已足够。
上传文件放在非media文件夹,用户即使知道了具体文件地址也无法访问,因为Django只会给media文件夹里每个文件创建独立url资源。视图和模板里验证用户是否已登录,登录或通过权限验证后通过自己编写的下载方法下载文件。- 安全性高,但实现相对复杂。
使用StreamHttpResponse和FileResponse下载文件的注意事项及文件私有化的更多相关文章
- limits.conf文件修改注意事项,限制文件描述符数和进程数
参考文章: https://blog.csdn.net/fanren224/article/details/79971359 https://www.cnblogs.com/micmouse521/p ...
- gradlew wrapper使用下载到本地的gradle.zip文件装配--转
原文地址:http://www.myexception.cn/mobile/1860089.html gradlew wrapper使用下载到本地的gradle.zip文件安装.使用gradlew来b ...
- ftp 根据特定正则匹配文件名 下载到本地 并且上传文件到ftp java *** 最爱那水货
/** * 建立FTP链接,FTP服务器地址.端口.登陆用户信息都在配置里配置即可. * @throws IOException */ public boolean connectFtp(String ...
- [No00006B]方便的网络下载工具wget 可下载网站目录下的所有文件(可下载整个网站)
wget是linux下命令行的下载工具,功能很强大,它能完成某些下载软件所不能做的,比如如果你想下载一个网页目录下的所有文件,如何做呢?网络用户有时候会遇到需要下载一批文件的情况,有时甚至需要把整个网 ...
- xshell下载文件到本地/上传文件到服务器
xshell很好用,然后有时候想在windows和linux上传或下载某个文件,其实有个很简单的方法就是rz,sz首先你的Ubuntu需要安装rz.sz(如果没有安装请执行以下命令,安装完的请跳过.其 ...
- 利用php CI force_download($filename, $data) 下载.csv 文件解决文件名乱码,文件内容乱码
利用php CI force_download($filename, $data) 下载.csv 文件解决文件名乱码,文件内容乱码 2014-07-31 12:53 1047人阅读 评论(0) 收藏 ...
- JSP下载txt 和 Excel两种文件
JSP下载txt 和 Excel两种文件 jsp 下载txt文件和excel文件 jsp 下载txt文件和excel文件 最近做了个用jsp下载的页面 将代码贴出来 权作记录吧 1 下载txt文件 ...
- [sharepoint]rest api文档库文件上传,下载,拷贝,剪切,删除文件,创建文件夹,修改文件夹属性,删除文件夹,获取文档列表
写在前面 最近对文档库的知识点进行了整理,也就有了这篇文章,当时查找这些接口,并用在实践中,确实废了一些功夫,也为了让更多的人走更少的弯路. 系列文章 sharepoint环境安装过程中几点需要注意的 ...
- php 通过header下载中文文件名 压缩包损坏或文件不存在的问题
开发中大家都是使用的utf8编码,昨天遇到一个奇坑,本是一件很小的问题,解决也浪费了个吧小时.废话不多说,植入正题: 文件下载方式:通过header二进制流文件下载需求: 文件上传保留文件名不变数据字 ...
随机推荐
- Mac系统常用软件
1.往移动硬盘中拷贝东西.创建文件夹: Mounty 2.解压缩软件(rar): the unarchiver 3.类似Xshell软件: FinalShell(国产) 输入以下命令: curl -o ...
- PO设计模式-实现移动端自动化测试
开发环境:python 3.6.5 + selenium 2.48.0 + pytest框架 + Android 5.1 工具:pycharm + Appium + Genymotion 测试机型:S ...
- 奇异值分解SVD
在介绍奇异值分解(SVD)之前我们先来回顾一下关于矩阵的一些基础知识. 矩阵基础知识 方阵 给定一个$ n×m $的矩阵$ A $,若n和m相等也就是矩阵的行和列相等那矩阵$ A $就是一个方阵. 单 ...
- axios统一封装
本文代码参考了网上别人的资料,经过修改而来 /** * Created by zxf on 2017/9/6. * 封装统一的ajax请求,统一拦截请求,对不同的请求状态封装 * 通常说, ajax ...
- DBCP + C3P0 连接池
DBCP 配置参数如下: 参数 默认值 可选值 格式 说明 username 用户名 password 密码 url 数据库连接url driverClassNam ...
- 测试用例设计经典面试题之电梯、杯子、笔、桌子、洗衣机、椅子、ATM等
测试用例设计经典面试题之电梯.杯子.笔.桌子.洗衣机.椅子.ATM等 1.测试项目:电梯 需求测试:查看电梯使用说明书.安全说明书等 界面测试:查看电梯外观 功能测试:测试电梯能否实现正常的上升和下降 ...
- Proto3:C++代码生成指南
本章节实际上是介绍Protocol Buffer编译器从给定的protocol定义中生成的C++代码.所有proto2和proto3生成的代码不同之处都会高亮标出 --- 需要注意的是这些不同之处只是 ...
- H2O theme for Jekyll
正如我在微博上所说的,使用Jekyll半年以来一直没有令我满意的主题模板,所以开始计划自己写一套好看又好用的主题模板.设计之初就明确了极简主义,风格采用扁平化了,通过卡片式设计来进行区块分明的布局,参 ...
- 码海拾遗:基于MySQL Connector/C++的MySQL操作(连接池)
1.MySQL安装及简单设置 (1)安装:在OSX系统下,可以使用万能的“brew install”命令来进行安装:brew isntall mysql(默认安装最新版的MySQL) (2)启动:br ...
- 查漏补缺:QObject类介绍
QObject是Qt对象模型的中心.这个模型的核心特征就是一种用于无缝对象间通信的被叫做信号和槽的非常强大的机制,可以使用connect()把信号和槽连接起来,也可以通过disconnect()来破坏 ...