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

基于DataTransfer实现双模式文件上传 删除图片新增文件的问题修复

问题根因拆解
  • 第一,原删除函数的操作对象错误:从fileInput.files拆出来的fileListArr是独立的临时数组,对它做splice删除根本不会反向修改原始的fileInput.files和全局维护的dT存储,原本要移除的文件实际上还留在存储里,根本没有被删掉。
  • 第二,原代码手动触发change事件会触发重复添加逻辑:change事件的回调里固定写了dT.items.add($(this)[0].files[0]),只要触发change,不管是用户选文件触发的,还是删除操作手动触发的,都会把当前input的第一个文件再往dT里追加一次,直接导致点删除的时候,不光没删掉目标文件,反而多塞了一个重复的第一个文件,出现越删文件越多的异常。
  • 第三,多入口的文件存储逻辑不统一:粘贴逻辑、本地选择逻辑、删除逻辑各写各的操作,没有统一的公共状态和渲染入口,三个场景下的存储内容、渲染内容不同步,很容易出现状态错乱。
修复方案

1. 抽离公共的预览渲染方法

把原来写在change事件里的预览生成逻辑单独拆成可复用的函数,避免重复写代码、也避免触发change时的重复添加副作用:

let imageIndex = 0; // 全局维护当前预览的索引
function renderImagePreview(){
  $("#imagePreview").html("");
  imageIndex = 0;
  Array.from(dT.items).forEach(item => {
    const blob = item.getAsFile();
    const uri = URL.createObjectURL(blob);
    const imgHtml = `<img width='150px' height='100px' src="${uri}">`;
    $("#imagePreview").append(`
      <td style="text-align:center; vertical-align:middle">
        ${imgHtml}
        <p class="text-center" align="center">
          <a onclick="return deleteImage(${imageIndex});" href="#"> Delete </a>
        </p>
      </td>
    `);
    imageIndex++;
  });
}

2. 重写文件删除逻辑

所有存储操作都围绕全局dT实例来做,不要在临时拆分的数组上操作:

function deleteImage(index){
  // 生成过滤掉目标索引的文件列表
  const remainFiles = Array.from(dT.items)
    .filter((_, curIdx) => curIdx !== index)
    .map(item => item.getAsFile());
  
  // 生成新的DataTransfer实例,把剩余文件全部装进去
  const newDT = new DataTransfer();
  remainFiles.forEach(file => newDT.items.add(file));
  
  // 同步更新input的files属性,保持表单提交时的内容正确
  fileInput.files = newDT.files;
  // 替换全局的dT引用,保证后续的添加、删除操作都基于最新状态
  window.dT = newDT;
  
  // 直接调用公共渲染函数更新预览,不要手动触发change事件走原来的重复添加逻辑
  renderImagePreview();
}

3. 修正本地文件选择的change事件逻辑

原来的change事件只取input的第一个文件加入dT、会重复追加的问题要改掉,统一走全量同步的逻辑:

$("#ssi-upload").change(function(){
  // 把当前input里的所有新选择的文件追加到全局dT里,不要只取第一个
  Array.from(this.files).forEach(file => {
    dT.items.add(file);
  });
  // 同步把input的files更新为dT的全量内容,避免下次选文件时重复追加
  fileInput.files = dT.files;
  // 调用公共渲染函数更新预览
  renderImagePreview();
});

4. 修正粘贴文件的逻辑

去掉原来临时创建的dTLocal,直接把粘贴的文件追加到全局dT里,避免状态分叉。原代码里声明的未实际使用的FileReader属于冗余代码,可以直接删除:

document.onpaste = function(event){
  const items = (event.clipboardData || event.originalEvent.clipboardData).items;
  for (index in items) {
    const item = items[index];
    if (item.kind === 'file') {
      const blob = item.getAsFile();
      // 直接把粘贴的文件追加到全局dT中
      dT.items.add(blob);
    }
  }
  // 同步更新input的files属性
  fileInput.files = dT.files;
  // 直接调用公共渲染函数,不手动触发change事件
  renderImagePreview();
}

之所以之前会出现“点删除反而新增文件”的问题,本质是两个逻辑耦合导致的:删除操作没有真正修改原始文件存储,反而通过手动触发change事件,把change逻辑里固定追加第一个文件的代码执行了一次,最终导致文件总数只增不减,和预期的删除效果完全相反。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 01:27:03