Django搭建网络相册:批量上传文件

5038 views, 2023/10/17 updated   Go to Comments

虽然 Django 自带的后台已经能够上传图片了,但是默认只能单张上传。如果你照片比较多,实在是有点不方便。

现在让我们来试着实现批量上传图片的功能。

后端代码

上一章讲过 GET 和 POST 请求的区别。图片上传会修改服务器资源,因此应该用 POST 请求。

又鉴于 home() 视图函数已经肩负用户登录功能了,如果图片上传也在里面处理,会显得太乱。

因此在 /photo/views.py 里新写一个 upload() 视图,专门用来处理文件上传。

修改内容如下:

# /photo/views.py

...
from django.shortcuts import render, redirect

...

def upload(request):
    if request.method == 'POST' and request.user.is_superuser:
        images = request.FILES.getlist('images')
        for i in images:
            photo = Photo(image=i)
            photo.save()
    return redirect('home')

和前面的用户名、密码这类普通的字符串数据不同,图片这类二进制文件数据在 request 中有专门的地方存放,即 request.FILES 了。因为图片可以是批量上传,所以是类似列表的有序集合,用 .getlist('images') 将此集合取出。

接下来就简单了。迭代这个图片列表,将它们全部都保存到模型中。调用 photo.save() 方法将其存储到数据库。

最后,upload() 视图并没有返回响应体,而是跳转到 home() 视图去了,继续执行 home() 视图中的代码了。参数 'home' 对应之前章节定义的 path(... name='home') 这个路径名。

接着例行惯例,需要给视图函数配置一个路径:

# /photo/urls.py

...
from photo.views import home, upload
...
urlpatterns = [
    ...
    path('upload/', upload, name='upload'),
]

接着去修改模板中的代码。

前端代码

上一章我们已经给上传文件预留了一个 “+” 号作为入口。

余下的工作就是将这个 “+” 号变成模态窗。

修改 header.html 模板:

<!-- /templates/header.html -->


<!-- 导航栏 -->
<nav class="navbar ...">
    <div class="container">
        ...
        <!-- 修改登录模态窗链接 -->
        {% if user.is_superuser %}
            <ul class="navbar-nav">
                <li class="nav-item">
                <h2>
                    <a 
                    class="nav-link active"
                    href="#"
                    data-bs-toggle="modal"
                    data-bs-target="#upload"
                    >
                        +
                    </a>
                </h2>
                </li>
            </ul>
        {% endif %}
    </div>
</nav>


<!-- 模态窗相关代码 -->
{% if user.is_superuser %}

...

<!-- 新增上传模态窗 -->
<div class="modal fade" id="upload">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-body">
                <h5 class="modal-title">上传图片</h5>
                <form action="{% url 'photo:upload' %}"
                method="post"
                enctype="multipart/form-data"
                >
                    {% csrf_token %}
                    <div class="col py-2">
                        <input
                            class="form-control" 
                            type="file"
                            id="images" 
                            name="images" 
                            multiple="multiple"
                            accept="image/*"
                        >
                    </div>
                    <button type="submit" class="btn btn-primary">提交</button>
                </form>
            </div>
        </div>
    </div>
</div>

{% else %}
...
{% endif %}

"+" 号通过超链接,作为触发模态窗的按钮。之所以还是考虑用模态窗的形式,是因为上传表单的信息量太小,没必要做页面的跳转。

让我们仔细看看上传文件表单的构造。

首先看 <form ...> 标签里的东东:

  • action="{% url 'photo:upload' %}" 指定此表单提交的路径。再次注意看它是如何和 url 路径的名称对应的。
  • method="post" 说明这是个 POST 请求的表单。
  • enctype="multipart/form-data" 指定表单的编码方式(将文件以二进制上传),必须有它才能正常上传文件。

再看 <input ... > 标签里:

  • type="file" 表示这是个文件上传控件。
  • name="images" 注意它要和后端代码的 .getlist('images') 的参数对应。
  • multiple="multiple" 表示支持多文件上传。
  • accept="image/*" 文件类型为图片。

欧了,测试。

测试

刷新页面,点击加号:

接下来就简单了,点击控件,随意选定多个图片,再点击提交按钮,图片就上传成功了。

总结

图片上传本身并不难,难的是对其进行安全的处理:

  • 如何裁剪图片尺寸?
  • 如何限制图片类型?
  • 如何判断图片里是否带有病毒?
  • 如何确保用户没有上传奇怪的小电影截图?

个人相册还好说,操作资源就自己,不会瞎折腾。但如果你要开发论坛或平台,这些都是要好好考虑的。

另一个现实的问题是,云服务器资源是昂贵的,作为草根网站,你的服务器带宽根本支撑不起庞大的图片流量。用户会在你的站点卡得吐血。教程末尾章节将会解决这个问题,在此之前,下一章先聊聊分页。

点赞 or 吐槽?评论区和我聊!




本文作者: 杜赛
发布时间: 2021年08月11日 - 11:48
最后更新: 2023年10月17日 - 11:49
转载请保留原文链接及作者