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

Django异步表单上传进度条失效问题排查求助

问题描述

我正在为Django网站的异步表单添加上传进度条,此前功能正常,但近期进度条不再显示上传进度。怀疑是作用域错误,但不确定如何修复。相关代码如下:

HTML模板代码

{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
       <form id="post-form" method="POST" enctype="multipart/form-data">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Post
            <p style="text-align: right;">
                <a class="btn btn-outline-primary" href="{% url 'photobooth:photobooth' %}">Booth</a>
                {% if not request.GET.schedule %}<a class="btn btn-outline-success" href="?schedule=t{% if request.GET.camera %}&camera=t{% endif %}{% if request.GET.audio %}&audio=t{% endif %}"><i class="bi bi-calendar-fill"></i></a>{% endif %}
                {% if not request.GET.camera %}<a class="btn btn-outline-primary" href="?camera=t{% if request.GET.schedule %}&schedule=t{% endif %}{% if request.GET.audio %}&audio=t{% endif %}"><i class="bi bi-camera-fill"></i></a>{% endif %}
                {% if not request.GET.audio %}<a class="btn btn-outline-danger" href="?audio=t{% if request.GET.camera %}&camera=t{% endif %}{% if request.GET.schedule %}&schedule=t{% endif %}"><i class="bi bi-mic-fill"></i></a>{% endif %}
            </p>
        </legend>
                {{ form|crispy }}
            </fieldset>
            <div id="uploaded_image"></div>
            <hr>
            <p id="contentLength"></p>
        <div id="progressBars"></div>
            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Post</button>
            </div>
        <p style="text-color: green;" class="hide" id="posted">Your post has been submitted.</p>
        </form>
{% endblock content %}

JavaScript代码

var form = document.getElementById("post-form");
var posted = document.getElementById("posted");
var chooseFile = document.getElementById("id_image");
var imgPreview = document.getElementById("uploaded_image");
var counter = 0;
$(form).submit(function(e){
    e.preventDefault();
    form = document.getElementById("post-form");
    var formData = new FormData(form);
    form.reset();
    imgPreview.innerHTML = "";
    $("#progressBars").append('<progress id="progress' + counter + '" value="0" max="100" style="width: 100%">0%</progress>');
    var progress = document.getElementById("progress" + counter);
    counter++;
    $.ajax({
    xhr: function() {
        var xhr = new window.XMLHttpRequest();
        xhr.upload.counter = counter;
                xhr.counter = counter;
        xhr.upload.addEventListener("progress", function(evt) {
                if (evt.lengthComputable) {
                    var percentComplete = evt.loaded / evt.total;
                percentComplete = parseInt(percentComplete * 100);
                var progressBar = document.getElementById("progress" + evt.target.counter);
                progressBar.innerHTML = percentComplete + '%';
                progressBar.value = percentComplete;
            }
        });
        return xhr;
    },
        url: window.location.pathname,
        type: "POST",
        data: formData,
        cache: false,
        contentType: false,
        processData: false,
        timeout: 1000 * 60 * 30,
    success: function(data) {
        $(posted).toggleClass("hide");
        setTimeout(function() {
            $(posted).toggleClass("fade-hidden");
            $(progress).toggleClass("fade-hidden");
            setTimeout(function() {
                $(posted).toggleClass("hide");
                $(posted).toggleClass("fade-hidden");
                $(progress).toggleClass("hide");
                $(progress).toggleClass("fade-hidden");
            }, 2000);
        }, 2000);
    },
    });
});
chooseFile.addEventListener("change", function () {
  getImgData();
});
function getImgData() {
  const files = chooseFile.files[0];
  if (files) {
    const fileReader = new FileReader();
    fileReader.readAsDataURL(files);
    fileReader.addEventListener("load", function () {
      imgPreview.style.display = "block";
      imgPreview.innerHTML = '<img width="100%" src="' + this.result + '" />';
    });    
  }
}

我也曾尝试移除progressBar变量,改用progress变量,但仍未解决问题。这是否与浏览器更新有关?我哪里操作失误或遗漏了什么?


修复方案

核心问题:计数器值不匹配

你代码里的关键错误是计数器赋值时机错误:

  1. 创建进度条时用的是当前counter值(比如0)
  2. 紧接着执行counter++,此时counter变成1
  3. 然后给xhr.upload.counter赋值为1
  4. 进度事件触发时,用evt.target.counter(值为1)去找progress1元素,但实际存在的是progress0,自然找不到元素,进度条也就不会更新

修复步骤:

  1. 保存当前计数器值:在counter++之前,把当前的counter值存到一个变量里,避免后续自增影响
  2. 正确绑定进度条到XHR对象:用保存的变量给xhr.upload.counter赋值
  3. 可选优化:直接把进度条元素绑定到XHR对象,省去ID查找步骤,同时增加元素存在判断,避免报错

修改后的JavaScript关键部分:

$(form).submit(function(e){
    e.preventDefault();
    form = document.getElementById("post-form");
    var formData = new FormData(form);
    form.reset();
    imgPreview.innerHTML = "";
    
    // 保存当前counter值,避免自增后变化
    var currentCounter = counter;
    $("#progressBars").append('<progress id="progress' + currentCounter + '" value="0" max="100" style="width: 100%">0%</progress>');
    var progress = document.getElementById("progress" + currentCounter);
    counter++;
    
    $.ajax({
    xhr: function() {
        var xhr = new window.XMLHttpRequest();
        // 绑定当前的计数器值到upload对象
        xhr.upload.counter = currentCounter;
        // 直接绑定进度条元素,省去ID查找
        xhr.upload.progressBar = progress;
        
        xhr.upload.addEventListener("progress", function(evt) {
                if (evt.lengthComputable) {
                    var percentComplete = evt.loaded / evt.total;
                    percentComplete = parseInt(percentComplete * 100);
                    // 获取进度条元素,增加存在判断
                    var progressBar = evt.target.progressBar || document.getElementById("progress" + evt.target.counter);
                    if(progressBar){
                        progressBar.innerHTML = percentComplete + '%';
                        progressBar.value = percentComplete;
                    }
                }
        });
        return xhr;
    },
    // 其余代码保持不变
    });
});

额外检查点:

  • 确认服务器端未开启Gzip压缩:如果开启压缩,evt.total无法正确计算,会导致lengthComputable为false,进度条无法更新
  • 浏览器更新一般不会影响XHR的progress事件,可测试不同浏览器排除兼容性问题
  • 打开浏览器控制台(F12),查看是否有"找不到元素"的报错,快速定位问题

内容的提问来源于stack exchange,提问作者Charlotte Harper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:57:58