html 文件输入框的 `files` 属性是只读的 filelist 对象,无法直接修改;需通过维护独立的文件数组(如 `selectedfiles`)来实现添加、删除与上传控制。
在前端多文件上传场景中,常见的需求是:用户选择多个文件后,页面展示预览列表,并支持逐个删除某项——但直接操作 的 files 属性(如 splice() 或 delete)无效,因为 input.files 是只读的 FileList 对象,任何对其的修改都不会反映到 DOM 或后续上传逻辑中。
✅ 正确做法是:绕过直接修改 input.files,转而用 JavaScript 维护一个可变的 File[] 数组(例如 selectedFiles = []),所有 UI 渲染、删除、上传均基于该数组操作。 仅用于初始文件选取,之后不再依赖其 files 属性。
暂无文件
let selectedFiles = [];
document.getElementById('fileInput').addEventListener('change', function (e) {
if (e.target.files.length === 0) return;
// 将新选文件合并进数组(避免重复引用问题)
selectedFiles = [...selectedFiles, ...Array.from(e.target.files)];
displayFiles();
document.getElementById('uploadBtn').disabled = false;
});
function displayFiles() {
const listEl = document.getElementById('fileList');
if (selectedFiles.length === 0) {
listEl.innerHTML = '暂无文件
';
return;
}
listEl.innerHTML = selectedFiles.map((file, idx) => `
? ${file.name} (${(file.size / 1024).toFixed(1)} KB)
`).join('');
}
function removeFile(index) {
selectedFi
les.splice(index, 1);
displayFiles();
// 若删空,则禁用上传按钮
if (selectedFiles.length === 0) {
document.getElementById('uploadBtn').disabled = true;
}
}
document.getElementById('uploadBtn').addEventListener('click', async function () {
if (selectedFiles.length === 0) return;
const formData = new FormData();
selectedFiles.forEach(file => {
formData.append('files[]', file, file.name); // 第三个参数确保原始文件名
});
try {
const res = await fetch('/upload', {
method: 'POST',
body: formData
});
alert(`上传成功!共 ${selectedFiles.length} 个文件。`);
selectedFiles = []; // 重置
displayFiles();
} catch (err) {
console.error('上传失败:', err);
alert('上传出错,请检查网络或服务器状态。');
}
});通过这种「数据驱动 UI」的方式,你不仅能可靠地增删文件,还能轻松集成校验、排序、分组等高级功能,是现代文件上传交互的最佳实践。