django项目--文档下载

文档下载功能

一、文档下载功能需求分析

1.功能

  • 文档下载展示页
  • 文档列表
  • 文档下载

二、模型设计

1.字段分析

  • 文件url
  • 文件名
  • 文件标题
  • 简介
  • 封面图片url

2.模型定义

# 在doc/models.py中定义如下模型
from django.db import models

from utils.models import BaseModel


class Doc(BaseModel):
    """
    文件模型
    """
    file_url = models.URLField('文件url', help_text='文件url')
    file_name = models.CharField('文件名', max_length=48,  help_text='文件名')
    title = models.CharField('文件标题', max_length=150, help_text='文件标题')
    desc = models.TextField('文件描述', help_text='文件描述')
    image_url = models.URLField('封面图片url', help_text='封面图片url')
    author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)

    class Meta:
        db_table = 'tb_docs'        # 数据库表名
        verbose_name = '文件表'        # admin 站点中显示的名称
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

三、文档下载页面

1.接口设计

  1. 接口说明:
类目 说明
请求方法 GET
url定义 /doc/download/
参数格式 无参数
  1. 返回结果:

    文档下载页面

3.后端代码

  1. 视图

    # 在doc/views.py里编写如下视图
    from .models import Doc
    
    
    class DocView(View):
        """
        文档下载页面视图
        """
        
        def get(self, request):
            return render(request, 'doc/docDownload.html')
    
  2. 路由

    # 在doc/urls.py中添加如下路由
    from django.urls import path
    from . import views
    
    app_name = 'doc'
    
    urlpatterns = [
        path('download/', views.DocView.as_view(), name='index'),
    ]
    
    # 在根urls.py中添加如下路由
    path('doc/', include('doc.urls'))
    

4.前端代码

  1. html
<!-- template/doc/docDownload.html -->
{% extends 'base/base.html' %}
{% load static %}
{% block title %}文档下载{% endblock %}
{% block link %}
      <link rel="stylesheet" href="{% static 'css/doc/docDownload.css' %}">
    <script>
        iMenuIndex = 2
    </script>
{% endblock %}

{% block main_contain %}
        <!-- main-contain start  -->
        <div class="main-contain ">
            <div class="banner">
                <img src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1802845035,3786939119&fm=26&gp=0.jpg"
                     alt="">
            </div>
            <div class="pay-doc-contain">
                <ul class="pay-list">
                    <!-- doc info -->
                </ul>
            </div>
        <!-- btn-more start -->
        <a href="javascript:void(0);" class="btn-more">滚动加载更多</a>
        <!-- btn-more end -->
        </div>
        <!-- main-contain  end -->
{% endblock %}

{% block script %}
    <script src="{% static 'js/doc/doc.js' %}"></script>
{% endblock %}
  1. css

    /* 在static/css/doc/docDownload.css中,把如下代码覆盖之前的样式 */
    
    /* ================= main start ================= */
    #main {
        margin-top: 25px;
        min-height: 700px;
        flex: 1;
    }
    
    /* ========= main-contain start ============ */
    #main .main-contain {
        width: 800px;
        float: left;
        margin-bottom: 30px;
    }
    
    /* ========= banner start =========== */
    .main-contain .banner {
        width: 100%;
    }
    
    .main-contain .banner img {
        max-width: 100%;
    }
    
    .main-contain .pay-doc-contain {
        background: #fff;
    }
    
    .main-contain .pay-doc-contain .pay-list {
        display: flex;
        justify-content: space-between;
        flex-flow: wrap;
        padding: 0 20px 20px;
    }
    
    .main-contain .pay-doc-contain .pay-item {
        width: 800px;
        height: 200px;
        border-top: 1px solid #ddd;
        margin-top: 20px;
        display: flex;
    }
    
    .main-contain .pay-doc-contain .pay-item:hover {
        box-shadow: 2px 2px 2px #ccc;
    }
    
    .pay-doc-contain .pay-item .pay-img {
        width: 120px;
        margin-right: 30px;
    }
    
    .pay-doc-contain .pay-item .pay-contain {
        width: 250px;
        position: relative
    }
    
    .pay-item .pay-contain .pay-title {
        font-size: 20px;
        line-height: 40px;
        white-space: nowrap;
        overflow: hidden;
    }
    
    .pay-item .pay-contain .pay-desc {
        line-height: 20px;
        color: #878787;
        text-overflow: ellipsis;
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
        font-size: 14px;
        overflow: hidden;
    }
    
    .pay-item .pay-price {
        display: block;
        font-size: 20px;
        text-align: right;
        padding-right: 20px;
        color: coral;
    }
    
    .d-contain {
        width: 100%;
        margin: 20px 0 20px ;
        font-size: 18px;
        line-height: normal;
    }
    
    .d-contain .doc-desc {
        text-indent: 2em;
        margin: 15px;
        /*padding: 10px;*/
    }
    
    .d-contain .doc-title {
        font-size: 20px;
        font-weight: bold;
        color: chocolate;
    }
    .btn-more {
        display: block;
        border: none;
        width: 400px;
        line-height: 40px;
        text-align: center;
        background: skyblue;
        color: #efefef;
        margin: 20px auto;
    }
    /* ========= banner end =========== */
    /* ========= main-contain end ============ */
    /* ================= main end ================= */
    

四、文档列表

1. 接口设计

  1. 接口说明:
类目 说明
请求方法 GET
url定义 /doc/docs/
参数格式 查询参数
  1. 参数说明:
参数名 类型 是否必须 描述
page 整数 页码
  1. 返回结果:
{
    "errno": "0", 
    "errmsg": "OK",  
    "data": {
        "total_page": 2,
        "docs": [
            {
                "desc": "youkou老师说:每天一个单词,充实每一天!",
                "file_name": "django项目班_英语单词2.doc",
                "file_url": "/media/django项目班_英语单词2.doc",
                "image_url": "/media/django项目班_英语单词.jpg",
                "title": "django项目班_英语单词2"
            },
            {
                "desc": "本书由奋战在Python开发一线近20年的Luciano Ramalho执笔,从语言设计层面剖析编程细节,兼顾Python 3和Python 2,教你写出风格地道的Python代码。",
                "file_name": "流畅的Python.pdf",
                "file_url": "/media/流畅的Python.pdf",
                "image_url": "/media/fluent_python_1.jpg",
                "title": "流畅的Python"
            }
        ]
    }
}

2.后端代码

  1. 视图

    # 在doc/views.py中添加DocListView视图如下
    class DocListView(View):
        def get(self, request):
            # 1.拿到所有文档
            docs = Doc.objects.values('file_url', 'file_name', 'title', 'desc', 'image_url').filter(is_delete=False)
            # 2.分页
            paginator = Paginator(docs, constants.PER_PAGE_DOC_COUNT)
    
            try:
                page = paginator.get_page(int(request.GET.get('page')))
            except Exception as e:
                page = paginator.get_page(1)
    
            data = {
                'total_page': paginator.num_pages,
                'docs': list(page)
            }
            return json_response(data=data)
    
  2. 常量配置

    # 在doc文件夹下创建constants.py文件,配置如下常量
    PER_PAGE_DOC_COUNT = 5
    
  3. 导入数据

    # 将给定的media文件夹内容复制到项目media文件内
    # 然后导入tb_docs.sql中的数据
    mysql -uroot -pqwe123 -D tzproject < tb_docs.sql
    

3. 前端代码

  1. js代码

    // 创建static/js/doc/doc.js文件,代码如下
    $(() => {
        let iPage = 1;       // 当前页面页数
        let iTotalPage = 1;      // 总页数
        let bIsLoadData =false;      // 是否正在加载
        fn_load_docs();      //  加载文件列表
        // 页面滚动加载
        $(window).scroll(function () {
           // 浏览器窗口高度
            let showHeigtht = $(window).height();
           // 整个网页高度
            let pageHeight = $(document).height();
            //页面可以滚动的距离
            let canScrollHeight = pageHeight - showHeigtht;
            // 页面滚动了多少, 整个是随着页面滚动实时变化的
            let nowScroll = $(document).scrollTop();
            if ((canScrollHeight - nowScroll) < 100){
                if(!bIsLoadData){
                    bIsLoadData = true;
                    //判断页数,去更新新闻,小于总数才加载
                    if(iPage < iTotalPage){
                        iPage += 1;
                        fn_load_docs();
    
                    }else {
                        message.showInfo('已全部加载,没有更多内容!');
                        $('a.btn-more').html('已全部加载,没有更多内容!')
                    }
    
                }
            }
        });
    
        // 获取docs信息
        function fn_load_docs() {
            $
                .ajax({
                    url: '/doc/docs/',
                    type: 'GET',
                    data: {page: iPage},
                    dataType: 'json'
                })
                .done((res) => {
                    if (res.errno === '0') {
                        iTotalPage = res.data.total_page;
                        res.data.docs.forEach((doc) => {
                            let content = `<li class="pay-item">
                            <div class="pay-img doc"></div>
                               <img src="${ doc.image_url }" alt="" class="pay-img doc">
                            <div class="d-contain">
                                <p class="doc-title">${ doc.title }</p>
                                <p class="doc-desc">${ doc.desc }</p>
    
                                <!-- /www/?xxx -->
                                <a href="${ doc.file_url }" class="pay-price" download="${ doc.file_name }">下载</a>
                            </div>
                        </li>`;
                            $('.pay-list').append(content);
                            bIsLoadData = false;
                            $('a.btn-more').html('滚动加载更多');
                        })
                    } else {
                        message.showError(res.errmsg)
                    }
                })
                .fail(() => {
                    message.showError('服务器超时,请重试!')
                })
        }
    });
    

五、文档下载

其实是上面通过前端a标签我们已经实现了本项目中的文件下载。但是实际项目中经常出现的文件下载功能中的文件是动态生成的,这种情况怎么处理呢?

# 文件下载视图
class DocDownload(View):
    """
    """
    def get(self, request, doc_id):

        file_fb = makefile()        # 生成文件流
        try:
            res = FileResponse(file_fb)
        except Exception as e:
            logger.info("获取文档内容出现异常:\n{}".format(e))
            raise Http404("文档下载异常!")

        ex_name = 'xls'     # 文件后缀,表明文件类型
        # https://stackoverflow.com/questions/23714383/what-are-all-the-possible-values-for-http-content-type-header
        # http://www.iana.org/assignments/media-types/media-types.xhtml#image
        if not ex_name:
            raise Http404("文档url异常!")
        else:
            ex_name = ex_name.lower()

        if ex_name == "pdf":
            res["Content-type"] = "application/pdf"
        elif ex_name == "zip":
            res["Content-type"] = "application/zip"
        elif ex_name == "doc":
            res["Content-type"] = "application/msword"
        elif ex_name == "xls":
            res["Content-type"] = "application/vnd.ms-excel"
        elif ex_name == "docx":
            res["Content-type"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
        elif ex_name == "ppt":
            res["Content-type"] = "application/vnd.ms-powerpoint"
        elif ex_name == "pptx":
            res["Content-type"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation"

        else:
            raise Http404("文档格式不正确!")

        doc_filename = escape_uri_path('某表格.xls')
        
        res["Content-Disposition"] = "attachment; filename*=UTF-8''{}".format(doc_filename)
        return res

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容