Python Web开发:开发资料下载功能

来自CloudWiki
跳转至: 导航搜索

创建“资料”模型

对资料建模,在数据库中创建对应资料类Doc

serviceApp/models.py:

from django.db import models
import django.utils.timezone as timezone

# Create your models here.


class Doc(models.Model):
    title = models.CharField(max_length=250, verbose_name='资料名称')
    file = models.FileField(upload_to='Service/',
                            blank=True,
                            verbose_name='文件资料')
    publishDate = models.DateTimeField(max_length=20,
                                       default=timezone.now,
                                       verbose_name='发布时间')

    def __str__(self):
        return self.title

    class Meta:
        ordering = ['-publishDate']
        verbose_name = "资料"
        verbose_name_plural = verbose_name

  • 通过参数upload_to的设置使得上传的文件统一放置在media/Service文件夹下面

python manage.py makemigrations

Migrations for 'contactApp':
  contactApp\migrations\0003_auto_20210531_2203.py
    - Alter field birth on resume
Migrations for 'serviceApp':
  serviceApp\migrations\0001_initial.py
    - Create model Doc

python manage.py migrate

Operations to perform:
  Apply all migrations: aboutApp, admin, auth, contactApp, contenttypes, newsApp, productsApp, serviceApp, sessions
Running migrations:
  Applying contactApp.0003_auto_20210531_2203... OK
  Applying serviceApp.0001_initial... OK

注册模型

serviceApp/admin.py:

from django.contrib import admin

# Register your models here.
from .models import Doc

admin.site.register(Doc)

保存后启动项目,登录后台系统,找到”资料“模型,添加几条暑假:

注意:上传的文件资料路径中不能含有中文。

Python21053101.png

”资料下载列表“页面开发

serviceApp/templates/ 下新建模板文件docList.html:

{% extends "base.html" %}
{% load staticfiles %}

{% block title %}
资料下载
{% endblock %}

{% block content %}
<link href="{% static 'css/news.css' %}" rel="stylesheet">
<!-- 广告横幅 -->
<div class="container-fluid">
    <div class="row">
        <img class="img-responsive model-img" src="{% static 'img/service.jpg' %}">
    </div>
</div>
<!-- 主体内容 -->
<div class="container">
    <div class="row row-3">
        <!-- 侧边导航栏 -->
        <div class="col-md-3">
            <div class="model-title">
                服务支持
            </div>
            <div class="model-list">
                <ul class="list-group">
                    <li class="list-group-item" id='download'>
                        <a href="{% url 'serviceApp:download' %}">资料下载</a>
                    </li>
                    <li class="list-group-item" id='platform'>
                        <a href="{% url 'serviceApp:platform' %}">人脸识别开放平台</a>
                    </li>
                </ul>
            </div>
        </div>
        <!-- 说明文字和图片 -->
        <div class="col-md-9">
            <div class="model-details-title">
                资料列表
            </div>
            <div class="model-details">
                {% for doc in docList %}
                <div class="news-model">
                    <img src="{% static 'img/newsicon.gif' %}">
                    <a href="{% url 'serviceApp:getDoc' doc.id %}"><b>{{doc.title}}</b></a>
                    <span>【{{doc.publishDate|date:"Y-m-d"}}】</span>
                </div>
                {% endfor %}

                {% if pageData %}
                <div class="paging">
                    <ul id="pages" class="pagination">
                        {% if pageData.first %}
                        <li><a href="?page=1">1</a></li>
                        {% endif %}
                        {% if pageData.left %}
                        {% if pageData.left_has_more %}
                        <li><span>...</span></li>
                        {% endif %}
                        {% for i in pageData.left %}
                        <li><a href="?page={{i}}">{{i}}</a></li>
                        {% endfor %}
                        {% endif %}
                        <li class="active"><a href="?page={{pageData.page}}">
                                {{pageData.page}}</a></li>
                        {% if pageData.right %}
                        {% for i in pageData.right %}
                        <li><a href="?page={{i}}">{{i}}</a></li>
                        {% endfor %}
                        {% if pageData.right_has_more %}
                        <li><span>...</span></li>
                        {% endif %}
                        {% endif %}
                        {% if pageData.last %}
                        <li><a href="?page={{pageData.total_pages}}">
                                {{pageData.total_pages}}</a></li>
                        {% endif %}
                    </ul>
                </div>
                {% endif %}
            </div>
        </div>
    </div>
</div>
{% endblock %}

注:资料访问路由 由{% url 'serviceApp:getDoc' doc.id%}设置,

添加路由

serviceApp /urls.py 添加路由:

from django.urls import path
from . import views

app_name = 'serviceApp'

urlpatterns = [
    path('download/', views.download, name='download'),  # 资料下载
    path('platform/', views.platform, name='platform'),  # 人脸识别开放平台
    path('getDoc/<int:id>/',views.getDoc,name='getDoc'),
]

修改视图函数

修改download函数

传入docList变量,具体和newsApp下的news()函数基本一致。

from django.shortcuts import render
from django.shortcuts import HttpResponse
from .models import Doc
from django.core.paginator import Paginator



def download(request):
    submenu = 'download'
    docList = Doc.objects.all().order_by('-publishDate')
    p = Paginator(docList, 5)
    if p.num_pages <= 1:
        pageData = ''
    else:
        page = int(request.GET.get('page', 1))
        newList = p.page(page)
        left = []
        right = []
        left_has_more = False
        right_has_more = False
        first = False
        last = False
        total_pages = p.num_pages
        page_range = p.page_range
        if page == 1:
            right = page_range[page:page + 2]
            print(total_pages)
            if right[-1] < total_pages - 1:
                right_has_more = True
            if right[-1] < total_pages:
                last = True
        elif page == total_pages:
            left = page_range[(page - 3) if (page - 3) > 0 else 0:page - 1]
            if left[0] > 2:
                left_has_more = True
            if left[0] > 1:
                first = True
        else:
            left = page_range[(page - 3) if (page - 3) > 0 else 0:page - 1]
            right = page_range[page:page + 2]
            if left[0] > 2:
                left_has_more = True
            if left[0] > 1:
                first = True
            if right[-1] < total_pages - 1:
                right_has_more = True
            if right[-1] < total_pages:
                last = True
        pageData = {
            'left': left,
            'right': right,
            'left_has_more': left_has_more,
            'right_has_more': right_has_more,
            'first': first,
            'last': last,
            'total_pages': total_pages,
            'page': page,
        }
    return render(
        request, 'docList.html', {
            'active_menu': 'service',
            'sub_menu': submenu,
            'docList': docList,
            'pageData': pageData,
        })

修改getDoc()函数

文件下载,普通的HttpResponse可以用,

但是Django提供了专门的StreamingHttpResponse对象来代替HttpResponse

具体的,在views.py 中添加一个文件分批读取的函数read_file(),

该函数通过构建一个迭代器,分批处理文件。

# Create your views here.
def read_file(file_name, size):  #分批读取文件
    with open(file_name, mode='rb') as fp:
        while True:
            c = fp.read(size)
            if c:
                yield c
            else:
                break

其中file_name为文件路径,size为分批读取文件的大小。

修改getDoc()函数:

from django.shortcuts import get_object_or_404
from django.http import StreamingHttpResponse
import os

def getDoc(request, id):
    doc = get_object_or_404(Doc, id=id)
    update_to, filename = str(doc.file).split('/')
    filepath = '%s/media/%s/%s' % (os.getcwd(), update_to, filename)
    response = StreamingHttpResponse(read_file(filepath, 512))
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename="{}"'.format(
        filename)
    return response

  • 获取文件:get_object_or_404
  • 读取文件:read_file()
  • 设置文件类型:为了防止文件以乱码形式显示到浏览器上,而非下载到硬盘上:
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename="{}"'.format(
        filename)

保存所有修改后启动项目,打开”资料下载“页面 进行下载:

Python21053102.png

Python21053103.png