基于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
相关产品推荐
相关产品推荐

