You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django中点击按钮实现表单提交前的文件预上传?

Django实现文件预上传(先保存后处理)的方案

核心思路是把文件先通过AJAX提交到服务器临时存储(数据库+文件系统),生成唯一标识关联该文件;点击「运行」按钮时,再通过这个标识找到文件进行后续处理。以下是具体实现步骤:

1. 调整模型(新增标识与状态字段)

在你的UploadFile模型中添加唯一UUID和状态字段,用来追踪文件状态和关联文件:

# models.py
import uuid
from django.db import models

class UploadFile(models.Model):
    file = models.FileField(upload_to='temp_uploads/')  # 文件存到临时目录
    uuid = models.UUIDField(default=uuid.uuid4, unique=True)  # 唯一标识文件
    status = models.CharField(max_length=20, default='uploaded')  # 状态:uploaded/processed/error
    uploaded_at = models.DateTimeField(auto_now_add=True)

2. 后端视图实现两个接口

分别处理预上传文件和运行文件处理的请求:

# views.py
from django.http import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie
from .models import UploadFile
import os

# 预上传文件接口
@ensure_csrf_cookie
def pre_upload_file(request):
    if request.method == 'POST' and request.FILES.get('file_upload'):
        upload_file = request.FILES['file_upload']
        # 验证文件类型
        allowed_extensions = ['.zip', '.rar', '.7z', '.gz']
        ext = os.path.splitext(upload_file.name)[1].lower()
        if ext not in allowed_extensions:
            return JsonResponse({'status': 'error', 'message': '不支持的文件类型'})
        
        # 保存文件到数据库和文件系统
        upload_request = UploadFile()
        upload_request.file = upload_file
        upload_request.save()
        
        return JsonResponse({
            'status': 'success',
            'file_uuid': str(upload_request.uuid),
            'message': '文件预上传成功'
        })
    return JsonResponse({'status': 'error', 'message': '无效请求'})

# 运行文件处理接口
def run_processing(request):
    if request.method == 'POST':
        file_uuid = request.POST.get('file_uuid')
        try:
            # 找到未处理的文件
            upload_request = UploadFile.objects.get(uuid=file_uuid, status='uploaded')
        except UploadFile.DoesNotExist:
            return JsonResponse({'status': 'error', 'message': '文件不存在或已处理'})
        
        # 这里写入你的文件处理逻辑
        # 示例:process_zip(upload_request.file.path)
        
        # 更新文件状态为已处理
        upload_request.status = 'processed'
        upload_request.save()
        
        return JsonResponse({'status': 'success', 'message': '文件处理完成'})
    return JsonResponse({'status': 'error', 'message': '无效请求'})

3. 配置URL路由

在项目的urls.py中添加两个接口的路由:

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('pre-upload/', views.pre_upload_file, name='pre_upload'),
    path('run-processing/', views.run_processing, name='run_processing'),
    # 其他已有路由...
]

4. 前端模板修改(AJAX处理交互)

把原来的表单提交改成AJAX异步上传,新增「运行」按钮并关联逻辑:

<!-- 模板文件 -->
{% csrf_token %}
<input type="file" required name="file_upload" id="choose_upload_file" accept=".zip,.rar,.7z,.gz"></br>           
<button class="btn btn-secondary btn-block" id="pre_upload_btn">预上传文件</button>
<button class="btn btn-primary btn-block" id="run_btn" disabled>运行处理</button>
<div id="status_msg" style="margin-top:10px;"></div>

<script>
// 获取CSRF Token(Django跨域验证需要)
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
const csrftoken = getCookie('csrftoken');

let fileUuid = '';

// 预上传按钮点击事件
document.getElementById('pre_upload_btn').addEventListener('click', function() {
    const fileInput = document.getElementById('choose_upload_file');
    const file = fileInput.files[0];
    if (!file) {
        document.getElementById('status_msg').textContent = '请先选择文件';
        return;
    }

    const formData = new FormData();
    formData.append('file_upload', file);

    // 禁用按钮防止重复提交
    this.disabled = true;
    document.getElementById('status_msg').textContent = '上传中...';

    fetch('{% url "pre_upload" %}', {
        method: 'POST',
        headers: {'X-CSRFToken': csrftoken},
        body: formData
    })
    .then(response => response.json())
    .then(data => {
        this.disabled = false;
        if (data.status === 'success') {
            fileUuid = data.file_uuid;
            document.getElementById('status_msg').textContent = data.message;
            document.getElementById('run_btn').disabled = false; // 启用运行按钮
        } else {
            document.getElementById('status_msg').textContent = data.message;
        }
    })
    .catch(error => {
        this.disabled = false;
        document.getElementById('status_msg').textContent = '上传失败:' + error;
    });
});

// 运行处理按钮点击事件
document.getElementById('run_btn').addEventListener('click', function() {
    if (!fileUuid) {
        document.getElementById('status_msg').textContent = '请先完成预上传';
        return;
    }

    const formData = new FormData();
    formData.append('file_uuid', fileUuid);

    this.disabled = true;
    document.getElementById('status_msg').textContent = '处理中...';

    fetch('{% url "run_processing" %}', {
        method: 'POST',
        headers: {'X-CSRFToken': csrftoken},
        body: formData
    })
    .then(response => response.json())
    .then(data => {
        this.disabled = false;
        document.getElementById('status_msg').textContent = data.message;
        if (data.status === 'success') {
            this.disabled = true; // 处理完成后禁用按钮
        }
    })
    .catch(error => {
        this.disabled = false;
        document.getElementById('status_msg').textContent = '处理失败:' + error;
    });
});
</script>

注意事项

  • 临时文件清理:可以通过Django信号(如post_save)或定时任务(如Celery)清理已处理/过期的文件,避免服务器空间浪费。
  • 存储配置:确保MEDIA_ROOT和MEDIA_URL配置正确,temp_uploads/目录会自动创建在MEDIA_ROOT下。
  • 权限控制:可以给视图添加@login_required装饰器,限制只有登录用户能上传和处理文件。

内容的提问来源于stack exchange,提问作者Prasanth Nelli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 04:30:41