写在前面
在Web开发中,文件上传和下载是常见的功能之一。
Django 是一位魔法师🪄,为我们提供了 FileField
和 ImageField
等神奇得字段类型,以及相应的视图和模板标签,使得处理文件变得十分便捷。本文以图片上传作为示例,向各位小伙伴展示如何使用Django框架构建一个简单的图片上传与下载功能的项目。🌝
完整代码见:https://github.com/MaitreChen/django-upload-download-demo
接下来,就由笔者手把手带你如何和在Django的舞台上演绎一场简单而精彩的图片上传与下载的戏码!🎉
准备工作
创建django项目,并创建应用程序:
django-admin startproject upload_download_demo
python manage.py startapp demo
setting.py添加应用程序:
INSTALLED_APPS = [
xxx
'demo',
]
在django项目目录创建media文件夹,并在setting.py中进行配置路径:
import os
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
需要说明的是,在Django项目使用文件上传功能时,MEDIA
路径是用来存储用户上传的媒体文件(例如图片、文档等)的位置,这是Django处理媒体文件的标准配置方式。
步骤一:定义模型类
模型(Model)是Django中的一个组件,用于定义数据的结构和规则,以便存储在数据库中。模型描述了数据的种类、字段类型、约束条件等,它充当着数据库表的蓝图,确保数据的一致性和完整性。
from django.db import models
from os.path import basename
class Record(models.Model):
name = models.CharField(max_length=100)
image = models.ImageField(upload_to='images/') # 指定图片文件存储在media/images文件夹内
def get_image_name(self):
return basename(self.image.name)
需要说明的是,这里定义get_image_name()函数是为了便于获取图片文件名,以在前端模板显示,非常好用~~
然后做一下数据库迁移:
python manage.py makemigrations
python manage.py migrate
- 第一条命令会生成数据库迁移文件,包含了关于模型变更的描述,所以模型如果添加或删除字段的时候就要做迁移;
- 第二条命令会应用变更,将模型映射到数据库表。
步骤二:定义表单
表单的定义是为了在Web应用中收集用户输入的数据,然后将其传递给后端处理,以创建、更新或查询数据库中的相应记录。
举个栗子🌰,表单就像是网页中的小秘书,它的任务是接收用户的各种奇思妙想,然后把这些信息悄悄地传递给后端大Boss。这样,后端Boss就能够以一种魔法般的方式创造、更新或者找到数据库中的相应“秘密档案”了!
from django import forms
from .models import Record
class RecordForm(forms.ModelForm):
class Meta:
model = Record
fields = ['image']
- RecordForm继承自forms.ModelForm,表示这是一个基于模型的表单;
- 在RecordForm内部,有一个内嵌的Meta类,用于配置一些表单的元数据
- fields = ['image'] 指定了在表单中包含哪些字段,由于我们在上传时只需要选择图片,所以我们添加一个image字段即可,如果要输入其他字段,比如titile,那这里也应该相应添加。
步骤三:构建视图函数
让我们编写一个能够在前端舞台上展示图像的视图,笔者为小伙伴们做了逐行解释(好贴心!)
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.conf import settings
from urllib.parse import quote
import os
from .forms import RecordForm # 导入图片上传表单
from .models import Record # 导入图片上传模型类
# 图片上传
def upload_image(request):
# 检查表单是否提交
if request.method == 'POST':
# 使用提交的数据创建一个表单实例
form = RecordForm(request.POST, request.FILES)
# 检查表单数据是否有效
if form.is_valid():
# 将表单数据保存到数据库
form.save()
# 成功提交后重定向到相同页面
return redirect('upload_image')
else:
# 如果是 GET 请求,创建一个空表单
form = RecordForm()
# 从数据库中检索所有记录
records = Record.objects.all()
# 使用表单和记录渲染模板,这样我们可以在前台获取records的属性值并显示
return render(request, 'upload_image.html', {'form': form, 'records': records})
# 图片下载
def download_image(request, filename):
# 构建文件路径
file_path = os.path.join(settings.MEDIA_ROOT, 'images', filename)
# 获取文件扩展名
_, file_extension = os.path.splitext(filename.lower())
# 根据文件扩展名确定内容类型,由于我们下载的是图片数据,所以添加常见的格式
content_type_mapping = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
}
# 如果找不到扩展名,默认使用 'application/octet-stream'
content_type = content_type_mapping.get(file_extension, 'application/octet-stream')
# 读取文件内容并创建响应对象
with open(file_path, 'rb') as file:
response = HttpResponse(file.read(), content_type=content_type)
# 设置 Content-Disposition 修改文件名,否则下载时浏览器会给你默认名字,比如"下载.jpg"
response['Content-Disposition'] = f'attachment; filename={quote(filename)}'
return response
步骤四:构建模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload and Download</title>
</head>
<body>
<div id="content">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload Image</button>
</form>
<table>
<thead>
<tr>
<th>Number</th>
<th>Image Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ record.get_image_name }}</td>
<td>
<a href="{% url 'download_image' filename=record.get_image_name %}" download>
Download
</a>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
第一部分,form标签中的内容表示图片上传表单。
- method="post" 表示使用 POST 方法提交表单;
- enctype="multipart/form-data" 表示支持文件上传;
- {% csrf_token %} 添加了一个 CSRF 令牌,用于防止跨站请求伪造攻击;
- {{ form.as_p }} 将表单的字段以段落形式呈现,使其更易于阅读;
- button是提交按钮,用于触发上传操作;
第二部分,这里笔者使用了table标签,以表格形式呈现在前台。
- 使用table表格,列出了每个图片的序号、图片名称和一个用于下载的链接;
- 使用 {% for record in records %} 迭代遍历所有记录;
- {% forloop.counter %} 提供了当前迭代的计数,不是必要的,只是为了前台为每一条记录编个号而已~
- 下载链接使用 {% url 'download_image' filename=record.get_image_name %} 来构建下载图片的URL,download 属性用于提示浏览器下载文件而不是直接打开!
- 另外,笔者只是为了带大家实现基本的功能,就不设置花里胡哨的style了~~
步骤五:配置URL
在应用程序app中添加文件上传与下载的url:
#demo/urls.py
from django.urls import path
from .views import upload_image, download_image
urlpatterns = [
path('upload/', upload_image, name='upload_image'),
path('download/<str:filename>/', download_image, name='download_image'),
]
我们解释一下download部分:
- 'download/<str:filename>/': 代表实际的URL模式。它包含了一个变量 <str:filename>,表示在这个位置匹配一个字符串,并将其作为名为 filename 的参数传递给视图函数。例如,如果URL为 download/my_image.jpg/,那么 my_image.jpg 将被传递给视图函数。
- name='download_image': 为这个URL模式起个小名,以便在Django中的其他地方引用。通过这个名字,我们可以在模板或其他地方使用 {% url 'download_image' filename=record.get_image_name %} 来生成对应的URL。
不要忘了在django项目添加app的url:
# upload_download_demo/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path('', include('demo.urls')),
]
终极测试
启动开发服务器的命令:
python manage.py runserver
浏览器访问:http://127.0.0.1:8000/upload
在Django的奇妙世界,我们每个人都是一位魔法师,通过巧妙的咒语让用户轻松上传魔法图谱,然后在魔法图书馆里畅游,尽情下载这些奇幻之书!🧙✨