Django 图片上传与下载

发布时间:2024年01月22日

写在前面

在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的奇妙世界,我们每个人都是一位魔法师,通过巧妙的咒语让用户轻松上传魔法图谱,然后在魔法图书馆里畅游,尽情下载这些奇幻之书!🧙?

文章来源:https://blog.csdn.net/m0_57055443/article/details/135694844
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。