每天40分玩转Django:Django文件上传

news/2024/12/24 8:26:13/

Django文件上传

一、今日学习内容概述

学习模块重要程度主要内容
基础文件上传⭐⭐⭐⭐⭐文件字段、基本配置
自定义存储⭐⭐⭐⭐⭐存储后端、云存储集成
文件处理⭐⭐⭐⭐图片处理、文件验证
异步上传⭐⭐⭐⭐AJAX上传、进度显示

二、模型和表单设计

python"># models.py
from django.db import models
from django.core.validators import FileExtensionValidator
import uuid
import osdef get_file_path(instance, filename):"""生成唯一的文件路径"""ext = filename.split('.')[-1]filename = f'{uuid.uuid4()}.{ext}'return os.path.join('uploads', filename)class Document(models.Model):title = models.CharField('标题', max_length=200)file = models.FileField('文件',upload_to=get_file_path,validators=[FileExtensionValidator(['pdf', 'doc', 'docx'])])uploaded_at = models.DateTimeField('上传时间', auto_now_add=True)class Meta:verbose_name = '文档'verbose_name_plural = verbose_namedef __str__(self):return self.titleclass Image(models.Model):title = models.CharField('标题', max_length=200)image = models.ImageField('图片',upload_to='images/%Y/%m/%d/',validators=[FileExtensionValidator(['jpg', 'jpeg', 'png'])])thumbnail = models.ImageField('缩略图',upload_to='thumbnails/%Y/%m/%d/',null=True,blank=True)uploaded_at = models.DateTimeField('上传时间', auto_now_add=True)class Meta:verbose_name = '图片'verbose_name_plural = verbose_name

三、自定义存储后端

python"># storage.py
from django.core.files.storage import Storage
from django.conf import settings
import oss2
import osclass AliyunOSSStorage(Storage):"""阿里云OSS存储后端"""def __init__(self):self.access_key_id = settings.OSS_ACCESS_KEY_IDself.access_key_secret = settings.OSS_ACCESS_KEY_SECRETself.bucket_name = settings.OSS_BUCKET_NAMEself.endpoint = settings.OSS_ENDPOINT# 初始化OSS客户端auth = oss2.Auth(self.access_key_id, self.access_key_secret)self.bucket = oss2.Bucket(auth, self.endpoint, self.bucket_name)def _save(self, name, content):"""保存文件到OSS"""self.bucket.put_object(name, content)return namedef _open(self, name, mode='rb'):"""从OSS读取文件"""return self.bucket.get_object(name)def exists(self, name):"""检查文件是否存在"""try:self.bucket.get_object_meta(name)return Trueexcept:return Falsedef url(self, name):"""获取文件URL"""return f'https://{self.bucket_name}.{self.endpoint}/{name}'# settings.py 配置
DEFAULT_FILE_STORAGE = 'myapp.storage.AliyunOSSStorage'

四、文件处理视图

python"># views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.core.files.storage import default_storage
from django.views.decorators.csrf import csrf_exempt
from .forms import DocumentForm, ImageForm
from PIL import Image as PILImage
from io import BytesIO
import jsondef handle_uploaded_file(request):if request.method == 'POST':form = DocumentForm(request.POST, request.FILES)if form.is_valid():document = form.save()messages.success(request, '文件上传成功!')return redirect('document_list')else:form = DocumentForm()return render(request, 'upload/document_form.html', {'form': form})@csrf_exempt
def ajax_upload(request):if request.method == 'POST':try:file = request.FILES['file']# 保存文件filename = default_storage.save(file.name, file)url = default_storage.url(filename)return JsonResponse({'success': True,'url': url})except Exception as e:return JsonResponse({'success': False,'error': str(e)})return JsonResponse({'success': False})def handle_image_upload(request):if request.method == 'POST':form = ImageForm(request.POST, request.FILES)if form.is_valid():image = form.save(commit=False)# 创建缩略图if image.image:img = PILImage.open(image.image)thumb_size = (300, 300)img.thumbnail(thumb_size)# 保存缩略图thumb_io = BytesIO()img.save(thumb_io, format=img.format)thumb_filename = f'thumb_{image.image.name}'image.thumbnail.save(thumb_filename,thumb_io.getvalue(),save=False)image.save()messages.success(request, '图片上传成功!')return redirect('image_list')else:form = ImageForm()return render(request, 'upload/image_form.html', {'form': form})

五、文件上传流程图

在这里插入图片描述

六、模板实现

<!-- templates/upload/image_form.html -->
{% extends "base.html" %}{% block content %}
<div class="container mt-4"><h2>上传图片</h2><form method="post" enctype="multipart/form-data">{% csrf_token %}{% if form.errors %}<div class="alert alert-danger">{% for field in form %}{% for error in field.errors %}<p>{{ error }}</p>{% endfor %}{% endfor %}</div>{% endif %}<div class="form-group">{{ form.title.label_tag }}{{ form.title }}</div><div class="form-group">{{ form.image.label_tag }}{{ form.image }}<small class="form-text text-muted">支持的格式:JPG, JPEG, PNG</small></div><div id="preview" class="mt-3 mb-3"></div><button type="submit" class="btn btn-primary">上传</button></form>
</div><script>
document.querySelector('input[type="file"]').addEventListener('change', function(e) {const file = e.target.files[0];if (file) {const reader = new FileReader();reader.onload = function(e) {const preview = document.getElementById('preview');preview.innerHTML = `<img src="${e.target.result}" class="img-thumbnail" style="max-width: 300px;">`;}reader.readAsDataURL(file);}
});
</script>
{% endblock %}

七、文件验证器

python"># validators.py
from django.core.exceptions import ValidationError
import magic
import osdef validate_file_type(file):"""验证文件类型"""mime = magic.from_buffer(file.read(1024), mime=True)file.seek(0)  # 重置文件指针allowed_types = {'application/pdf': 'pdf','application/msword': 'doc','application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx'}if mime not in allowed_types:raise ValidationError('不支持的文件类型')def validate_file_size(file):"""验证文件大小"""max_size = 5 * 1024 * 1024  # 5MBif file.size > max_size:raise ValidationError('文件大小不能超过5MB')

八、异步上传实现

// static/js/upload.js
class FileUploader {constructor(options) {this.options = {url: '/upload/',maxSize: 5 * 1024 * 1024,allowedTypes: ['image/jpeg', 'image/png'],...options};this.init();}init() {this.fileInput = document.querySelector(this.options.fileInput);this.progressBar = document.querySelector(this.options.progressBar);this.fileInput.addEventListener('change', (e) => this.handleFiles(e));}handleFiles(e) {const files = e.target.files;Array.from(files).forEach(file => this.uploadFile(file));}uploadFile(file) {if (!this.validateFile(file)) return;const formData = new FormData();formData.append('file', file);const xhr = new XMLHttpRequest();xhr.open('POST', this.options.url, true);xhr.upload.addEventListener('progress', (e) => {if (e.lengthComputable) {const percent = (e.loaded / e.total) * 100;this.updateProgress(percent);}});xhr.onload = () => {if (xhr.status === 200) {this.options.onSuccess && this.options.onSuccess(xhr.response);} else {this.options.onError && this.options.onError(xhr.statusText);}};xhr.send(formData);}validateFile(file) {if (file.size > this.options.maxSize) {alert('文件太大');return false;}if (!this.options.allowedTypes.includes(file.type)) {alert('不支持的文件类型');return false;}return true;}updateProgress(percent) {this.progressBar.style.width = `${percent}%`;this.progressBar.textContent = `${Math.round(percent)}%`;}
}// 使用示例
const uploader = new FileUploader({fileInput: '#fileInput',progressBar: '#progressBar',onSuccess: (response) => {console.log('上传成功', response);},onError: (error) => {console.error('上传失败', error);}
});

通过本章学习,你应该能够:

  1. 实现基本的文件上传功能
  2. 自定义存储后端
  3. 处理文件验证和安全
  4. 实现异步文件上传

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!


http://www.ppmy.cn/news/1557691.html

相关文章

Android OpenGLES2.0开发(十):FBO离屏渲染

人生是一场单程的旅行&#xff0c;即使有些遗憾我们也没有从头再来的机会&#xff0c;与其纠结无法改变的过去不如微笑着珍惜未来。 Android OpenGLES开发&#xff1a;EGL环境搭建Android OpenGLES2.0开发&#xff08;一&#xff09;&#xff1a;艰难的开始Android OpenGLES2.0…

面向对象 类函数的区别 实例方法 类方法 静态方法 抽象方法

前言&#xff1a;面向对象类方法的说明&#xff1a; 实例方法 定义&#xff1a;实例方法是在类中定义的&#xff0c;用于操作类的实例&#xff08;对象&#xff09;的属性和行为的方法。它的第一个参数通常是self&#xff08;在 Python 中&#xff09;或this&#xff08;在 Jav…

#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

基于字节大模型的论文翻译(含免费源码)

基于字节大模型的论文翻译 源代码&#xff1a; &#x1f44f; star ✨ https://github.com/boots-coder/LLM-application 展示 项目简介 本项目是一个基于大语言模型&#xff08;Large Language Model, LLM&#xff09;的论文阅读与翻译辅助工具。它通过用户界面&#xff08…

【Django篇】--动手实践Django基础知识

一、url视图映射 在url.py中定义两个视图函数&#xff0c;并添加到urlpatterns中用于访问。 from django.contrib import admin from django.urls import path from django.shortcuts import HttpResponse# 默认的地址为&#xff1a;http://127.0.0.1:8000/# 如果我想要访问默…

Java 8使用Stream流去除一个list中包含另一个list已存在的某个字段的对象

项目场景&#xff1a; 在Java中&#xff0c;我们经常会遇到需要对List中的数据进行操作的情况。有时候&#xff0c;我们需要从一个List中删除另一个List已经包含的数据。这种情况下&#xff0c;我们可以使用Java Stream来简洁高效地完成操作。 代码示例 假设我们有两个对象列表…

CentOS7网络配置,解决不能联网、ping不通外网、主机的问题

1. 重置 关闭Centos系统 编辑->虚拟网络编辑器 还原默认设置 2. 记录基本信息 查看网关地址,并记录在小本本上 查看网段,记录下 3. 修改网卡配置 启动Centos系统 非root用户,切换root su root查看Mac地址 ifconfig 或 ip addr记录下来 修改配置文件 vim /et…

【IMU:视觉惯性SLAM系统】

视觉惯性SLAM系统简介 相机&#xff08;单目/双目/RGBD)与IMU结合起来就是视觉惯性&#xff0c;通常以单目/双目IMU为主。 IMU里面有个小芯片可以测量角速度与加速度&#xff0c;可分为6轴(6个自由度)和9轴&#xff08;9个自由度&#xff09;IMU&#xff0c;具体的关于IMU的介…