当前位置: 首页 > news >正文

掌握Django文件处理:一步步构建上传功能

创建模型

首先先进入我们的testsite项目下,打开members/models.py文件,先添加我们保存文件的数据模型:

class Document(models.Model):name = models.CharField(max_length=255)file = models.FileField(upload_to='uploads/')  # 'uploads/' 是文件上传的相对路径uploaded_at = models.DateTimeField(auto_now_add=True)

这里的文件存储目录可以自定义我们需要存储文件的目录,也可以在我们的 env文件 做配置,这里直接读取相应的配置 env字段 就可以了。

进入testsite根目录执行更新迁移文件命令,这一步前面有说到,是为了更新迁移文件,

py manage.py makemigrations

可以看到我们的migrations目录增加了新的迁移文件

执行迁移

py manage.py migrate

我们新增加的文件模型表就在数据库生成好了。

使用模型

进入testsite/members目录,新增form.py表单文件,这里会定义文件上传的字段。

from django import forms
from .models import Documentclass UploadFileForm(forms.ModelForm):class Meta:model = Documentfields = ['name', 'file']  # 包含你想要用户填写的字段

接下来将上传的文件绑定到表单中,打开testsite/members/views.py视图文件:

from .forms import UploadFileForm #需要我们引入上一步建立好的表单文件def upload_file(request):if request.method == "POST":form = UploadFileForm(request.POST, request.FILES)if form.is_valid():form.save()  # 保存表单数据到数据库return HttpResponse("上传成功")else:form = UploadFileForm()return render(request, "polls/upload.html", {"form": form})

我们传递request.FILES 到表单的构造函数;这就是文件数据绑定到表单的方式。

请注意,request.FILES仅当请求方法为POST,实际发送了至少一个文件字段且<form>表单有发送请求的并且具有enctype="multipart/form-data"属性时。否则request.FILES将为空。

最后一步,添加上传页面,进入members/templates/members目录,新增upload.html文件,注意跟我们的视图里面指向的文件名保持一致。

<form method="post" enctype="multipart/form-data">{% csrf_token %}{{ form.as_p }}<button type="submit">提交</button>
</form>

打开地址http://127.0.0.1:8000/polls/upload/进入文件上传页面

点写完表单点击提交

这个时候我们的文件上传就完成了,可以打开我们的testsite目录,进入我们设置的uploads目录,就看到我们上传好的文件:
在这里插入图片描述
然后我们再打开数据库,进入模型对应的members_document表,也可以看到文件上传生成的数据。
在这里插入图片描述

批量上传

为了使用一个表单字段上传多个文件,创建该字段的子类并将其allow_multiple_selected类属性设置为True

进入表单文件forms.py

class MultipleFileInput(forms.ClearableFileInput):allow_multiple_selected = Trueclass MultipleFileField(forms.FileField):def __init__(self, *args, **kwargs):kwargs.setdefault("widget", MultipleFileInput())super().__init__(*args, **kwargs)def clean(self, data, initial=None):single_file_clean = super().cleanif isinstance(data, (list, tuple)):result = [single_file_clean(d, initial) for d in data]else:result = [single_file_clean(data, initial)]return resultclass FileFieldForm(forms.Form):file_field = MultipleFileField()

然后进入view.py视图文件重写子类form_valid()的方法 FormView来处理多个文件上传:

from .forms import FileFieldForm  # 确保导入正确的表单定义
from .models import Document  # 引入Document模型
def batch_upload_view(request):if request.method == 'POST':form = FileFieldForm(request.POST, request.FILES)if form.is_valid():# 获取表单中title的值title = form.cleaned_data['title']# 处理文件上传for file in request.FILES.getlist('file_field'):document = Document(file=file, name=title)  # 假设title作为文件的名称或描述document.save()return HttpResponse("上传成功")else:# 请求方法为GET时,初始化表单form = FileFieldForm()return render(request, "polls/uploads.html", {"form": form})

假如你不想使用模型关联上传,则使用如下方式:

from django.core.files.storage import default_storage
#使用默认存储引擎保存文件
file_name = default_storage.save(file.name, file)

然后重新渲染我们的批量上传页面,跟我们的forms django表单关联

<form method="post" enctype="multipart/form-data"> <!-- enctype 必须为 multipart/form-data 以支持文件上传 -->{% csrf_token %} <!-- Django表单必须的安全令牌 -->{{ form.title }} {{ form.file_field }} <!-- 渲染文件上传字段,MultipleFileField将会生成相应的input元素 --><button type="submit">上传文件</button>
</form>

上面的 csrf_token django表单必须的安全令牌,form.file_field 也是我们form.py定义的批量上传字段。

打开members/urls.py,添加我们的路由

path('uploads/', views.batch_upload_view, name='upload'),

我们的批量上传功能就已经写好了,我们打开http://127.0.0.1:8000/polls/uploads/页面

填好表单,批量选择文件,点击上传,这里我只是演示,没有做页面渲染样式与字段验证,感兴趣的小伙伴可以加上自己喜欢的样式。


然后进入我们的数据库可以看到我们与上传模型的记录被添加

我们打开uploads目录,也可以看到我们上传好的文件。


需要注意的是在保存上传的文件之前,数据需要存储在某个地方。

默认情况下,如果上传的文件小于 2.5 MBdjango 会将上传的全部内容保存在内存中。

这意味着保存文件仅涉及从内存读取和写入磁盘,因此速度非常快。

但是,如果上传的文件太大,django 会将上传的文件写入存储在系统临时目录中的临时文件。

例如/tmp/tmpzfp6I6.upload。如果上传的文件足够大,您可以看到该文件的大小随着 django 将数据传输到磁盘而增长

总结

文件上传算是一种很常见的需求,几乎构建很多项目系统,以及插件都需要用到。

通过上面例子可以看到django通过forms表单的形式灵活定义文件上传的页面,字段,以及数据库存储。

在实际项目中我们还要考虑到安全性,包括检查文件大小(可通过FileFieldmax_length属性设置)、防止路径遍历攻击。

还需注意服务器端的验证,比如检查文件类型、内容安全等。

例子里面有提到,djangoFileSystemStorageDefaultStorage类提供了基本的文件存储管理。

自定义存储后端可以提供更多灵活性,如自定义文件命名规则、存储路径等,以避免文件名冲突和优化组织结构。

大量文件上传或下载也会影响应用性能。最好采用云存储服务、内容分发网络(CDN)、以及对静态和媒体文件的有效缓存策略,可以显著提升用户体验。

对于我们本地的存储策略,需要考虑上传文件的生命周期管理,包括旧文件的定期清理策略,以避免无限制增长占用存储空间。

– 欢迎点赞、关注、转发、收藏【我码玄黄】,gonghao同名

相关文章:

  • 安全生产新篇章:可燃气体报警器检验周期的国家标准解读
  • 正则表达式 0.1v
  • Spring AI 第二讲 之 Chat Model API 第七节Mistral AI Chat
  • Docker:定义未来的软件部署
  • JVM之【字节码/Class文件/ClassFile 内容解析】
  • 【C语言之排序】-------六大排序
  • LabVIEW软件开发人员如何在软件开发中捕捉需求?
  • 前端 Web 与原生应用端 WebView 通信交互 - HarmonyOS Next
  • “Kubectl 如何工作案例:编写自定义 Kubectl 命令
  • 【机器学习】基于OpenCV和TensorFlow的MobileNetV2模型的物种识别与个体相似度分析
  • Spring Boot项目中,如何在yml配置文件中读取maven pom.xml文件中的properties标签下的属性值
  • 27、matlab傅里叶变换:fft()函数
  • Git配置SSH-Key
  • win+mac通用的SpringBoot+H2数据库集成过程。
  • SwiftUI中ContentUnavailableView的使用(iOS 17、tvOS 17推出的新组件)
  • __proto__ 和 prototype的关系
  • 【391天】每日项目总结系列128(2018.03.03)
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • JAVA 学习IO流
  • jQuery(一)
  • Promise面试题,控制异步流程
  • React系列之 Redux 架构模式
  • spark本地环境的搭建到运行第一个spark程序
  • SQLServer插入数据
  • 思考 CSS 架构
  • 通信类
  • 2017年360最后一道编程题
  • kubernetes资源对象--ingress
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • #NOIP 2014# day.1 T2 联合权值
  • #window11设置系统变量#
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (26)4.7 字符函数和字符串函数
  • (HAL库版)freeRTOS移植STMF103
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (创新)基于VMD-CNN-BiLSTM的电力负荷预测—代码+数据
  • (动态规划)5. 最长回文子串 java解决
  • (二)springcloud实战之config配置中心
  • (几何:六边形面积)编写程序,提示用户输入六边形的边长,然后显示它的面积。
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (一) storm的集群安装与配置
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • ******IT公司面试题汇总+优秀技术博客汇总
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .net6 webapi log4net完整配置使用流程
  • .Net面试题4
  • .NET运行机制
  • .NET中使用Protobuffer 实现序列化和反序列化
  • .考试倒计时43天!来提分啦!
  • @staticmethod和@classmethod的作用与区别
  • @transactional 方法执行完再commit_当@Transactional遇到@CacheEvict,你的代码是不是有bug!...