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)
保存后启动项目,登录后台系统,找到”资料“模型,添加几条暑假:
注意:上传的文件资料路径中不能含有中文。
”资料下载列表“页面开发
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)
保存所有修改后启动项目,打开”资料下载“页面 进行下载: