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

如何从JavaScript数组中删除已上传文件,解决Django中删除后仍被上传问题

多文件上传移除功能异常的修复方案

问题根因

  • 移除逻辑存在漏洞:遍历filesToUpload数组执行splice操作后,数组长度发生变化,未调整遍历索引会导致后续元素被跳过;仅通过文件名匹配的逻辑遇到重名文件时会出现误删。
  • 自定义文件数组未实际参与提交:表单默认提交时会直接读取input type="file"控件内置的只读FileList对象,你自行维护的filesToUpload数组没有被用到,删除数组元素自然不影响提交结果。
  • 后端直接读取原生文件流:Django代码中直接取request.FILES.getlist('files1'),拿到的是前端input控件提交的全量文件,不会过滤掉你前端页面删除的条目。

修复方案

第一步:修复前端JS逻辑

替换原有JS代码为如下版本,同时调整提交逻辑使用FormData携带筛选后的文件提交:

$(document).ready(function (){
    $.fn.fileUploader = function (filesToUpload) {
        this.closest(".files").change(function (evt) {
            // 保存当前数组长度,作为新增文件的唯一标识前缀,避免重名冲突
            const currentLength = filesToUpload.length;
            for (var i = 0; i < evt.target.files.length; i++) {
                filesToUpload.push(evt.target.files[i]);
            };
            var output = [];
            for (var i = 0, f; f = evt.target.files[i]; i++) {
                // 用数组真实索引作为data-fileid,不用本次遍历的i
                const realIndex = currentLength + i;
                var removeLink = `<a class="removeFile" href="#" data-fileid="${realIndex}">Remove</a>`;
                output.push("<li><strong>", escape(f.name), "</strong> - ",
                    f.size, " bytes. &nbsp; &nbsp; ", removeLink, "</li> ");
            }
            $(this).children(".fileList")
                .append(output.join(""));
            // 清空input的值,避免重复提交相同文件
            $(this).find('input[type="file"]').val('');
        });
    };
    var filesToUpload = [];
    // 修复移除逻辑
    $(document).on("click",".removeFile", function(e){
        e.preventDefault();
        // 直接取绑定的真实数组索引,不用匹配文件名
        const fileIndex = parseInt($(this).data("fileid"));
        if(fileIndex >=0 && fileIndex < filesToUpload.length) {
            filesToUpload.splice(fileIndex, 1);
            // 移除后更新所有后续移除按钮的data-fileid,适配数组索引变化
            $(this).parent().nextAll('li').each(function() {
                const curIndex = parseInt($(this).find('.removeFile').data("fileid"));
                $(this).find('.removeFile').data("fileid", curIndex -1);
            })
        }
        $(this).parent().remove();
    });
    $("#files1").fileUploader(filesToUpload);
    $("#files2").fileUploader(filesToUpload);
    // 重写提交逻辑,用FormData提交筛选后的文件
    $("#uploadBtn").click(function (e) {
        e.preventDefault();
        const formData = new FormData($('form')[0]); // 替换为你的表单选择器
        // 先删除默认的files1文件列表
        formData.delete('files1');
        // 把筛选后的文件逐个加入FormData
        filesToUpload.forEach(file => {
            formData.append('files1', file);
        });
        // 提交请求,注意加Django CSRF头
        $.ajax({
            url: window.location.href, // 替换为你的提交地址
            type: 'POST',
            data: formData,
            processData: false,
            contentType: false,
            headers: {
                'X-CSRFToken': $('input[name="csrfmiddlewaretoken"]').val()
            },
            success: function(res) {
                // 提交成功后的跳转逻辑,可自行修改
                window.location.href = "/procedures/main/";
            },
            error: function(err) {
                // 错误处理逻辑,可自行修改
                console.log(err);
            }
        })
    });
});

第二步:调整Django后端逻辑(可选,适配AJAX提交)

如果采用上述AJAX提交方式,后端可以保留原有逻辑,也可以增加AJAX请求适配:

def post(self, request, *args, **kwargs):
    if "cancel" in request.POST:
        return HttpResponseRedirect(reverse('Procedures:procedure_main_menu'))
    else:
        self.object = None
        user = request.user
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        file_form = NewProcedureFilesForm(request.POST, request.FILES)
        files = request.FILES.getlist('files1')
        if form.is_valid() and file_form.is_valid():
            procedure_instance = form.save(commit=False)
            procedure_instance.user = user
            procedure_instance.save()
            for f in files:
                procedure_file_instance = NewProcedureFiles(attachments=f, new_procedure=procedure_instance)
                procedure_file_instance.save()
            # 适配AJAX请求返回
            if request.headers.get('x-requested-with') == 'XMLHttpRequest':
                return JsonResponse({'status':'success'}, status=200)
            return self.form_valid(form)
        else:
            if request.headers.get('x-requested-with') == 'XMLHttpRequest':
                return JsonResponse({'status':'error', 'errors': form.errors}, status=400)
            form = self.get_form(form_class)
            file_form = NewProcedureFilesForm()
            return self.form_invalid(form)

内容的提问来源于stack exchange,提问作者Steve Smith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 23:45:01