[django项目] 如何在网站上实现文档下载功能?

原创

[django项目] 如何在网站上实现文档下载功能?

文档下载功能

I. 功能需求分析

1>功能
  1. 文档下载展示页,展示整体的网页框架
  2. 文档列表,展示可下载的文档,包括标题、封面、简介等
  3. 文档下载,点击下载?即可下载文档

II. 模型设计

1>字段分析

针对文档的下载需要满足以下字段:

  1. 文件url
  2. 文件名
  3. 文件标题
  4. 简介
  5. 封面图片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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

定义模型 -> 生成迁移

III. 文档下载页面

1>接口设计

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

    文档下载页面

2>后端代码

2.1>文档页面视图

在doc/views.py里编写如下视图

from .models import Doc

class DocView(View):
“”"
文档下载页面视图
“”"

def get(self, request):
    return render(request, 'doc/docDownload.html')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
2.2>路由
# 在doc/urls.py中添加如下路由
from django.urls import path
from . import views

app_name = ‘doc’

urlpatterns = [
path(‘download/’, views.DocView.as_view(), name=‘index’),
]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
# 在根urls.py中添加如下路由
path('doc/', include('doc.urls'))

 
 
   
   
   
   
  • 1
  • 2

3>前端代码

3.1>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 %}

<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>

ul>
div>

<a href=javascript:void(0); class=btn-more>滚动加载更多a>

div>

{% endblock %}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
3.2>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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115

IIII. 文档列表

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"
            }
        ]
    }
}

 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2>后端代码

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
2.2>常量配置
# 在doc文件夹下创建constants.py文件,配置如下常量
PER_PAGE_DOC_COUNT = 5

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

 
 
   
   
   
   
  • 1
  • 2
  • 3

3>前端代码

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('服务器超时,请重试!')
        })
}

});

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

js写完记得引用到docDownload.html

{% block script %}
    <script src="{% static 'js/doc/doc.js' %}">script>
{% endblock %}
  • 1
  • 2
  • 3
  • 4

V. 文档下载

其实是上面通过前端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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
                                

文章最后发布于: 2019年08月25日 21:33:21

你可能感兴趣的:(django,django笔记)