目录
方式一:使用HttpResponse
方式二:使用StreamingHttpResponse
方式三:使用FileResponse
方式四:使用迭代器优化文件加载
文件名中文乱码问题
文件私有化的两种方法
个人下载文档view视图代码
在实际的项目中很多时候需要用到下载功能,如导excel、pdf或者文件下载,当然你可以使用web服务自己搭建可以用于下载的资源服务器,如nginx,这里我们主要介绍django中的文件下载。
这里我们将下载的文件存放在项目media目录下,当然在实际中并不会这样做。
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)写入用户本地磁盘,先不将它们载入服务器内存。如果一定要使用HttpResponse,可以参考方式四。
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
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
import os
from django.http import HttpResponse, Http404
# 这是一个迭代器,减少内存占用
def read_file(url, chunk_size=512):
with open(url, "rb") as f:
while True:
c = f.read(chunk_size)
if c:
yield c
else:
break
def stream_http_download(request, file_path):
try:
response = HttpResponse(read_file(file_path))
response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
return response
except Exception:
raise Http404
其中用英文的文件名,浏览器显示正常,但是用了中文后,就是默认的文件名,如下载.xls
,或者如果我用了utf-8编码,是乱码。解决方法如下:
response['Content-Disposition'] = "attachment; filename*=utf-8''{}".format(escape_uri_path(name))
response['Content-Disposition'] = 'attachment;filename="{0}{1}{2}{3}"'.format(datetime.now().year,datetime.now().month,datetime.now().day,'三率.xls').encode('utf-8', 'ISO-8859-1')
如果你想实现只有登录过的用户才能查看和下载某些文件,大概有两种方法,这里仅提供思路。
from django.views import View
from django.conf import settings
from django.http import FileResponse,Http404
from django.utils.encoding import escape_uri_path
from .models import Doc
import requests
import logging
logger = logging.getLogger('django')
class Download(View):
"""
前端传来下载doc的id,后端传给它下载地址
"""
def get(self,request,doc_id):
doc = Doc.objects.only('file_url').filter(is_delete=False,id = doc_id).first()
if doc:
doc_url = doc.file_url
doc_url = settings.ITEM_DOMAIN_PORT + doc_url
try:
res = FileResponse(requests.get(doc_url,stream = True))
except Exception as e:
logger.info('文件获取异常:{}'.format(e))
raise Http404('文件获取异常')
file_end = doc_url.split('.')[-1]
if not file_end:
raise Http404('文档路径出错')
else:
file_end = file_end.lower()
if file_end == "pdf":
res["Content-type"] = "application/pdf"
elif file_end == "zip":
res["Content-type"] = "application/zip"
elif file_end == "doc":
res["Content-type"] = "application/msword"
elif file_end == "xls":
res["Content-type"] = "application/vnd.ms-excel"
elif file_end == "docx":
res["Content-type"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
elif file_end == "ppt":
res["Content-type"] = "application/vnd.ms-powerpoint"
elif file_end == "pptx":
res["Content-type"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
else:
raise Http404("文档格式不正确!")
doc_filename = escape_uri_path(doc_url.split('/')[-1])
# http1.1 中的规范
# 设置为inline,会直接打开
# attachment 浏览器会开始下载
res["Content-Disposition"] = "attachment; filename*=UTF-8''{}".format(doc_filename)
return res
else:
raise Http404("文档不存在!")
参考链接:
django——三种方式实现文件下载