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

jQuery+PHP多文件上传重复提交问题求助

问题诊断与修复方案

核心问题分析

  1. 多文件重复上传:大概率是change事件被重复绑定(比如代码多次执行、动态渲染后重复绑定),导致每次选文件时多个回调同时启动上传流程,每个流程都会遍历所有文件,最终每个文件被上传N次(N为事件绑定次数)。
  2. 单文件仅传最后一个:原代码中FormData从现有表单初始化,会包含文件输入框的历史文件,后续set('file', file)的覆盖逻辑在单文件场景下可能被多次执行,最终只保留最后一次设置的文件。

修复代码

前端jQuery代码优化

$('input[type="file"]').off('change').on('change', function (e) {
  // 变量放在闭包内,避免全局污染
  let files = e.target.files;
  let currentFileIndex = 0;
  let isUploading = false;
  const progressBar = $('.js-upload-progress-bar');

  function uploadNextFile() {
    if (currentFileIndex < files.length) {
      if (!isUploading) {
        isUploading = true;
        const file = files[currentFileIndex];
        const fileSize = file.size / (1024 * 1024);
        const fileType = file.type;
        const isImage = fileType.startsWith('image/');
        const isVideo = fileType.startsWith('video/');

        if (fileSize <= 1024 && (isImage || isVideo)) {
          const previewSrc = isVideo 
            ? '../assets/images/icons/icons-video.png' 
            : URL.createObjectURL(file);
          $('.js-previewImg').attr('src', previewSrc);
          $('.js-uploadStatus').text(`Uploading ${currentFileIndex + 1}/${files.length}`);

          // 创建空FormData,手动添加文件和表单字段
          const formData = new FormData();
          // 若表单有其他字段,手动追加,例如:formData.append('csrf', $('[name="csrf"]').val());
          formData.append('file', file);

          $.ajax({
            url: 'upload.php',
            type: 'POST',
            data: formData,
            processData: false,
            contentType: false,
            xhr: () => {
              const xhr = new window.XMLHttpRequest();
              xhr.upload.addEventListener('progress', (e) => {
                if (e.lengthComputable) {
                  const progress = Math.round((e.loaded / e.total) * 100);
                  progressBar.val(progress);
                }
              });
              return xhr;
            },
            success: (response) => {
              console.log(response);
              currentFileIndex++;
              isUploading = false;
              uploadNextFile();
            },
            error: (xhr, status, error) => {
              console.log(`Error: ${error}`);
              currentFileIndex++;
              isUploading = false;
              uploadNextFile();
            }
          });
        } else {
          console.log('File validation failed');
          currentFileIndex++;
          isUploading = false;
          uploadNextFile();
        }
      }
    } else {
      $('.js-uploadStatus').text('Upload Successful');
      setTimeout(() => {  
        $('.uploadProgress').removeClass('show'); 
        $('input[type="file"]').val('');
        isUploading = false;
      }, 1000);
      progressBar.val(0);
      currentFileIndex = 0;
    }
  }

  uploadNextFile();
  $('.uploadProgress').addClass('show');
});

后端PHP接收脚本示例

<?php
$targetDir = 'uploads/';
// 确保上传目录存在
if (!is_dir($targetDir)) mkdir($targetDir, 0755, true);

if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
    $fileName = uniqid() . '-' . basename($_FILES['file']['name']);
    $targetFile = $targetDir . $fileName;
    if (move_uploaded_file($_FILES['file']['tmp_name'], $targetFile)) {
        echo json_encode(['status' => 'success', 'file' => $fileName]);
    } else {
        echo json_encode(['status' => 'error', 'msg' => 'Failed to save file']);
    }
} else {
    echo json_encode(['status' => 'error', 'msg' => 'Upload error: ' . $_FILES['file']['error']]);
}
?>

关键修复点

  • 用off('change')强制解绑后再绑定,避免事件重复触发上传流程。
  • 将控制变量放在事件回调闭包内,彻底避免全局变量冲突。
  • 初始化空FormData并手动添加文件,排除原表单历史数据的干扰。

内容的提问来源于stack exchange,提问作者Forever Edits

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:42:44