django——三种方式实现文件下载

博客主要介绍Django中的文件下载,将下载文件存于项目media目录。介绍了三种下载方式,即HttpResponse、StreamingHttpResponse和FileResponse,还提及文件名中文乱码问题的解决办法。此外,给出文件私有化的两种思路,并展示个人下载文档view视图代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在实际的项目中很多时候需要用到下载功能,如导excel、pdf或者文件下载,当然你可以使用web服务自己搭建可以用于下载的资源服务器,如nginx,这里我们主要介绍django中的文件下载。

这里我们将下载的文件存放在项目media目录下,当然在实际中并不会这样做。

方式一:使用HttpResponse

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)写入用户本地磁盘,先不将它们载入服务器内存。

方式二:使用StreamingHttpResponse

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

方式三:使用FileResponse

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

文件名中文乱码问题

其中用英文的文件名,浏览器显示正常,但是用了中文后,就是默认的文件名,如下载.xls,或者如果我用了utf-8编码,是乱码解决方法如下:

response['Content-Disposition'] = "attachment; filename*=utf-8''{}".format(escape_uri_path(name))

文件私有化的两种方法

如果你想实现只有登录过的用户才能查看和下载某些文件,大概有两种方法,这里仅提供思路。

  • 上传文件放在media文件夹,文件名使用很长的随机字符串命名(uuid), 让用户无法根据文件名猜出这是什么文件。视图和模板里验证用户是否已登录,登录或通过权限验证后才显示具体的url。- 简单易实现,安全性不高,但对于一般项目已足够。
  • 上传文件放在非media文件夹,用户即使知道了具体文件地址也无法访问,因为Django只会给media文件夹里每个文件创建独立url资源。视图和模板里验证用户是否已登录,登录或通过权限验证后通过自己编写的下载方法下载文件。- 安全性高,但实现相对复杂。

个人下载文档view视图代码

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、MySQL 和 Vue 构建有声图书网站的最佳实践 #### 项目初始化与环境配置 为了启动一个基于Django和Vue的有声图书网站,需先设置好开发环境。这包括但不限于安装Python解释器以及确保所有必要的Django依赖库已就绪[^3]。 对于数据库的选择,在此案例中选用了MySQL作为后端存储解决方案,并通过Navicat这类工具来进行管理和维护工作。此外,还需注意版本兼容性问题;例如,文中提到使用了特定版本组合——Python 3.7.7搭配MySQL 5.7。 #### 后端开发 (Django) ##### 创建并配置Django应用 一旦环境准备完毕,则可着手建立新的Django工程及其内部的应用程序组件。考虑到项目的特殊需求(即提供在线听书服务),应当定义相应的数据模型来表示书籍信息、章节详情等内容实体。这些都将被保存至预先设定好的MySQL实例之中。 ```python from django.db import models class Book(models.Model): title = models.CharField(max_length=200) author = models.CharField(max_length=100) class Chapter(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE) content_url = models.URLField() # 假设音频文件托管于外部CDN或云盘 ``` 上述代码片段展示了两个简单的ORM类映射关系,用于描述一本书籍与其所属各章节之间的关联形式。 ##### API接口设计 为了让前端能够顺利获取到所需的数据资源,必须精心规划RESTful风格的服务端点。借助`django-rest-framework`这样的第三方包可以帮助简化这一过程,从而使得开发者可以专注于业务逻辑本身而非底层通信细节。 #### 前端开发 (Vue.js) ##### 用户界面构建 在完成了基础架构之后,接下来就是打造直观易用的操作面板供访客浏览及收听电子读物之用了。这里推荐运用现代JavaScript框架之一—Vue.js 来完成这项任务。其声明式的语法糖特性极大地提高了编码效率的同时也降低了学习曲线[^4]。 利用HTML/CSS/JS编写静态页面元素,再配合Vue指令实现动态效果,比如加载进度条显示或是播放控制按钮等功能部件。与此同时,还可以考虑引入Element UI等UI库进一步美化整体外观样式。 ##### 客户端请求处理 最后一步便是让前后两端之间建立起有效的联系渠道。通常来说会采用AJAX异步调用来发起HTTP GET/POST 请求并与远端API进行交互。当然也可以探索WebSocket长连接机制以支持更加复杂的场景需求,不过对于本项目而言前者已经足够胜任了。 ```javascript axios.get('/api/books/') .then(response => { this.books = response.data; }) .catch(error => console.log(error)); ``` 这段脚本示范了怎样从服务器拉取最新发布的书籍列表并将结果显示给当前用户查看。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值