Django文件上传与下载

Django文件上传与下载

这篇文章我默认你对 Django 已经有所了解,即熟悉基本项目目录,url 设置,以及各代码所在文件夹。

只是不知如何具体操作,这里提供借鉴。Python3.7,Django2.1。

文件上传与下载,仅通过 web 端实现上传与下载,上传的文件展示并下载,并不是很难,下面就说说怎么做。

上传实现

上传前端代码:upload.html

<body>
	<form action="" method="POST" enctype="multipart/form-data">
           {% csrf_token %}
           <input type="file" name="file"/>
           <input type="submit"value="Upload"/>
	</form>
	{% if error_message %}
       	<strong>{{ error_message }}</strong>
       {% endif %}
       {% if success_message %}
       <strong>{{ success_message }}</strong>
	{% endif %}
</body>

上传表单,input 标签内的 name 值 file 传入后端即获取的文件名,下面的代码是上传错误与成功的反馈信息。

后端获取文件名,并处理 (app_name)/views:

1.文件上传

def upload(request):
    if request.method == 'POST':
   	    up_file = request.FILES.get('file')  # 获取文件
   	    if not up_file:  # 未选择文件
   	        error_message = 'no file. please choose a file.'
   	        return render(request, 'files/upload.html', locals())
   	    else:
   	        # 这里filter不替换为get,因为get查询不到会报错,而filter查询不到,会返回空列表,
   	        # 而我们上传的文件,数据库中很可能不存在(存在也不要上传了),这时代码会报错,而不是返回空列表
   	        # 所以应该使用filter查询。
   	        db_file = File.objects.filter(file_name=up_file)
   	        if not db_file:  # 判断数据库中是否已有正在上传的文件名
   	            File.objects.create(file_name='%s' % up_file)  # 如果没有,文件名存入数据库
   	            handle_uploaded_file(up_file, str(up_file))  # 处理文件
   	            success_message = 'upload success. please continue.'
   	            return render(request, 'files/upload.html', locals())
   	        else:   # 数据库中已存在,直接处理文件
   	            handle_uploaded_file(up_file, str(up_file))  # 处理文件
   	            success_message = 'upload success. please continue.'
   	            return render(request, 'files/upload.html', locals())
   	return render(request, 'files/upload.html')

2.处理上传的文件

def handle_uploaded_file(file, filename):
   	file_path = path.join(base_dir, 'polls', 'manage_files', 'download')
   	if not path.exists(file_path):  # 判断存储文件的路径是否存在
       	os.mkdir(file_path)
   	with open(file_path + '/' + filename, 'wb+') as destination:
       	for chunk in file.chunks():  # 分块写入文件
       	    destination.write(chunk)

3.数据库设置

# (app_name)/models
class File(models.Model):
    file_name = models.CharField(max_length=100)
	pub_date = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.file_name

上面两个函数处理上传的文件有相应注释,第三段是数据模型,仅用来储存文件名,没有储存文件路径,有兴趣和尝试。

展示数据库存储的文件名,视图函数 (app_name)/views:

1.展示上传的文件

def uploaded(request):
   	# 获取数据库存储的文件信息,以pub_date字段值为过滤器,条件为今天之前的所有数据
   	db_files_list = File.objects.filter(pub_date__lte=timezone.now())
   	return render(request, 'files/uploaded.html', locals())

2.前端代码:uploaded.html

<html lang="en">
<body>
{% if file_name_list %}
   	<ul>
   	    {% for file in file_name_list %}
   	    <li>{{ file }}</li>
   	    {% endfor %}
   	</ul>
   	{% else %}
   	<p>No file.</p>
   {% endif %}
</body>
</html>

下载实现

展示可供下载的文件,文件夹中存储的文件,和上一步的区别在于,文件夹里的文件被删除,数据库仍有记录,所以以文件夹为准。请看注释。

1.展示可供下载的文件

def show(request):
    """
       先从文件夹获取文件,反向从数据库获取id,拼接称字典,
       不直接从数据库获取文件名传入前端,原因是:如果存储文件的文件夹过大及想要删除一些文件时,
       文件夹中没有文件,数据库中有文件名,信息不匹配,导致前端会有一些文件无法下载。
       通过文件夹里的文件反向获取id,可以避免此问题。如此,文件的上传与下载可以统一化。
       上传的文件可以从页面支持下载,不需要修改源代码。
    """
    dir_file_path = os.path.join(base_dir, 'polls', 'manage_files', 'download')
    dir_file_list = os.listdir(dir_file_path)  # 获取文件夹里的文件,可以下载的文件
    id_list = []
    if not dir_file_list:  # 判断文件夹中是否存在文件
        return render(request, 'files/show.html', {'dir_file_list': dir_file_list})
    else:
        for file in dir_file_list:
            file_info = File.objects.get(file_name=file)  # 通过文件夹里的文件名获取文件在数据库里的文件信息
           	id_list.append(file_info.id)  # 获取文件在数据库里文件的id,并存入列表
       	files_dict = dict(zip(id_list, dir_file_list))  # id与name合成字典,一边传入前端,此id用来下载文件
       return render(request, 'files/show.html', {'dir_file_list': dir_file_list,
                                                   'files_dict': files_dict})

2.对应前端代码:show.html

<!DOCTYPE html>
<html lang="en">
<head>
   	<meta charset="UTF-8">
   	<title>File list</title>
</head>
<body>
   	{% if dir_file_list %}
   	<ul>{% for id, file in files_dict.items %}
       	<li>
       	    <a href="polls/download/{{ id }}/">
			{{ file }}
       	    </a>
      		</li>
       	{% endfor %}
   	</ul>
   	{% else %}
   	<p>No File.</p>
   	{% endif %}
   	<br>
</body>
</html>

3.文件下载实现:

# 文件下载
def download(request, file_id):
    file = File.objects.get(id=file_id)
    file_name = file.file_name
    files_path = os.path.join(base_dir, 'polls', 'manage_files', 'download', file_name)  # 获取文件
    files = open(files_path, 'rb')
    response = FileResponse(files)
    response['Content-Type'] = 'application/octet-stream'
    # 文件名为中文时无法识别,使用UTF-8和escape_uri_path处理
    response["Content-Disposition"] = "attachment; " \
                                      "filename*=UTF-8''{}".format(escape_uri_path(file_name))
    return response

4.(app_name)/urls.py 设置:

from django.urls import path, re_path

app_name = 'polls'

urlpatterns = [
	# ...
    # files
    path('upload/', views.upload, name='upload'),
    path('uploaded/', views.uploaded, name='uploaded'),
    path('show/', views.show, name='show'),
    re_path('download/(?P<file_id>\\d+)/', views.download, name='download'),
]

5.为简化代码,获取项目目录路径,设置在(app_name)/init.py 中:

import os
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

6.(app)/views 中需要导入一些包和方法。

import os
from os import path
from django.http import HttpResponseRedirect, FileResponse
from django.urls import reverse
from django.utils import timezone
from django.utils.encoding import escape_uri_path
from django.views import generic
from django.shortcuts import get_object_or_404, render
from .models import File
from . import base_dir

以上文件视情况导入。

2019/4/4 17:37

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值