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

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

Django文件上传

一、今日学习内容概述

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

二、模型和表单设计

# models.py
from django.db import models
from django.core.validators import FileExtensionValidator
import uuid
import os

def 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_name
        
    def __str__(self):
        return self.title

class 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

三、自定义存储后端

# storage.py
from django.core.files.storage import Storage
from django.conf import settings
import oss2
import os

class AliyunOSSStorage(Storage):
    """阿里云OSS存储后端"""
    
    def __init__(self):
        self.access_key_id = settings.OSS_ACCESS_KEY_ID
        self.access_key_secret = settings.OSS_ACCESS_KEY_SECRET
        self.bucket_name = settings.OSS_BUCKET_NAME
        self.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 name
    
    def _open(self, name, mode='rb'):
        """从OSS读取文件"""
        return self.bucket.get_object(name)
    
    def exists(self, name):
        """检查文件是否存在"""
        try:
            self.bucket.get_object_meta(name)
            return True
        except:
            return False
            
    def url(self, name):
        """获取文件URL"""
        return f'https://{self.bucket_name}.{self.endpoint}/{name}'

# settings.py 配置
DEFAULT_FILE_STORAGE = 'myapp.storage.AliyunOSSStorage'

四、文件处理视图

# 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 json

def 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 %}

七、文件验证器

# validators.py
from django.core.exceptions import ValidationError
import magic
import os

def 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  # 5MB
    if 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.kler.cn/a/449382.html

相关文章:

  • springboot481基于springboot社区老人健康信息管理系统(论文+源码)_kaic
  • Java复习|图形用户界面AWT、Swing----银行客户管理系统【校课版】【1】
  • 一体式IO模块:打印机加工产线国产化降本增效的新利器
  • 【杂谈】-为什么Python是AI的首选语言
  • StarRocks 生产部署一套集群,存储空间如何规划?
  • 《计算机组成及汇编语言原理》阅读笔记:p48-p81
  • 9. 日常算法
  • SAP SD客户主数据及其配置
  • vue前端实现同步发送请求【已封装】
  • 【唐叔学算法】第17天:排序算法之插入排序
  • GPU环境配置
  • 华为OD --- TLV解码
  • Go怎么做性能优化工具篇之基准测试
  • 芝法酱学习笔记(2.2)——sql性能优化2
  • 0.96寸OLED显示屏详解
  • Day1 苍穹外卖前端 Vue基础、Vue基本使用方式、Vue-router、Vuex、TypeScript
  • Python实现将series系列数据格式批量转换为Excel
  • OCR(五)linux 环境 基于c++的 paddle ocr 编译【CPU版本 】
  • 高原地区无人机巡检作业技术详解
  • 螺栓连接|结构强度与刚度评定
  • C++练习题之计算天数
  • SpringBoot3-第二篇(Web开发)
  • 使用FreeNAS软件部署ISCSI的SAN架构存储(IP-SAN)练习题
  • 物联网水文观测设备
  • 蓝桥杯物联网开发板硬件组成
  • 汽车IVI中控开发入门及进阶(41):视频播放器MPlayer