使用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资源。视图和模板里验证用户是否已登录,登录或通过权限验证后通过自己编写的下载方法下载文件。- 安全性高,但实现相对复杂。

原文地址:https://www.cnblogs.com/ellisonzhang/p/11422265.html

时间: 2024-10-03 14:45:09

使用StreamHttpResponse和FileResponse下载文件的注意事项及文件私有化的相关文章

JAVA下载URL所对应的资源文件

通过代码下载"http://www.baidu.com/img/bd_logo1.png"对应的图片文件 package guwen; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnec

gradlew wrapper使用下载到本地的gradle.zip文件装配--转

原文地址:http://www.myexception.cn/mobile/1860089.html gradlew wrapper使用下载到本地的gradle.zip文件安装.使用gradlew来build项目时,有时候老是需要下载gradle-2.2.1-all.zip安装来安装.但是因为gradle-2.2.1-all.zip老是被墙下不了.这里说一种从本地安装的方法. 1. 先下载gradle-2.2.1-all.zip包.2. 把下载好的zip包放到{project.dir}\grad

[sharepoint]rest api文档库文件上传,下载,拷贝,剪切,删除文件,创建文件夹,修改文件夹属性,删除文件夹,获取文档列表

写在前面 最近对文档库的知识点进行了整理,也就有了这篇文章,当时查找这些接口,并用在实践中,确实废了一些功夫,也为了让更多的人走更少的弯路. 系列文章 sharepoint环境安装过程中几点需要注意的地方 Rest API的简单应用 rest api方式实现对文档库的管理 通过WebClient模拟post上传文件到服务器 WebHttpRequest在sharepoint文档库中的使用 [sharepoint]Rest api相关知识(转) [sharepoint]根据用户名获取该用户的权限

C#实现FTP文件的上传、下载功能、新建目录以及文件的删除

本来这篇博文应该在上周就完成的,可无奈,最近工作比较忙,没有时间写,所以推迟到了今天.可悲的是,今天也没有太多的时间,所以决定给大家贴出源码,不做详细的分析说明,如果有不懂的,可以给我留言,我们共同讨论. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography; using Sys

JSP下载txt 和 Excel两种文件

JSP下载txt 和 Excel两种文件 jsp 下载txt文件和excel文件 jsp 下载txt文件和excel文件 最近做了个用jsp下载的页面 将代码贴出来 权作记录吧 1 下载txt文件 这个花了我不少时间 原因是用ie下载txt文件时是在页面中直接打开了文件.虽然查了一些资料,也看了别人的解决方案,可还是解决不了问题,最后发现是一个字母惹的祸:少写一个字母 嘿嘿 够马虎!!! 代码如下: OutputStream o=response.getOutputStream();   byt

文件的基本操作二与文件的上传和下载 (45)

访问远程文件 ??如果需要访问远程文件,必须在PHP的配置文件中激活“allow_url_fopen”选项,才能使用fopen( )函数打开运程文件.而且还要确定其他服务器中的文件是否访问权限,如果使用PHP协议对远程文件进行链接,只能以“只读”模式打开.如果需要访问的远程FTP服务器中,对所提供的用户开启了“可写”权限,则使用FTP协议链接远程的文件时,就可以使用“只写”或“只读”模式打开文件.但不可以使用“可读可写”的模式.??使用PHP访问远程文件就像访问本地文件一样,都是使用相同的读写函

文件夹没有安全选项-文件上传下载-路径访问被拒绝

在文件的下载和上传中,有时候会出现“路径...访问被拒绝”,这是由于权限问题引起,只要给文件所在的文件夹设置权限为everyone就可以解决了,但是有时候文件夹属性没有“安全”选项卡,解决方法如下: 第一种方法: 打开文件夹,选择“工具”----“文件夹选项”----“查看”,去掉“使用简单文件共享”选项. 如果第一种方法不行,使用第二方法: 选择 “开始”---“控制面板”----“管理工具”---“本地安全策略”---“本地策略”---“安全选项” 双击 “网络访问:本地账户的共享和安全模式

struts2学习(14)struts2文件上传和下载(4)多个文件上传和下载

四.多个文件上传: 五.struts2文件下载: 多个文件上传action com.cy.action.FilesUploadAction.java: package com.cy.action; import java.io.File; import org.apache.commons.io.FileUtils; import com.opensymphony.xwork2.ActionSupport; public class FilesUploadAction extends Actio

C#下载局域网共享文件夹中的文件

在公司的局域网内部,有个主机,共享了几个文件夹给下面的客户机使用. 想要利用这个文件夹上传最新的winform程序版本,每次运行exe的时候检测局域网的软件版本达到更新exe的目的. 这里有个例子,是下载局域网共享文件夹里面txt文件的一个案例:   问题: 现单击Button1就能实现访问固定IP为:10.19.96.215上的共享文件夹里的文件.共享文件夹在该机器中的路径为C:\gongxiang 里面有abc.txt文件.那么在private void button1_Click(obje