应使用accept属性限制类型、onchange事件校验数量与格式,动态生成文件列表并预览元数据,用formdata按后端要求逐个append文件,配合promise.allsettled实现单文件级错误定位与提示。

用 <input type="file"> 多文件上传但限制类型和数量
企业资质证明(如营业执照、许可证、ISO证书)通常需上传多个 PDF 或图片文件,且必须控制格式和数量。原生 <input type="file"> 支持 multiple,但默认不限制类型和个数——用户可能误传 .exe 或选 20 个截图,后端校验前就得在前端拦住。
实操建议:
- 加
accept="application/pdf,image/*"缩小可选范围(注意:IE 不支持通配符,image/*在 Safari 16.4+ 才稳定) - 用
onchange拦截event.target.files,检查.length和每个.type:if (files.length > 5) { alert('最多上传 5 个文件'); return; } for (let f of files) { if (!f.type.match(/^image\/|application\/pdf$/)) { alert('仅支持 JPG/PNG/PDF'); return; } } - 别只信
accept—— 它只是提示,用户改后缀名或拖拽仍能绕过
前端预览文件名 + 类型 + 大小,避免“已选择 3 个文件”这种模糊提示
用户上传后常不确定是否成功、有没有漏传某项(比如把“食品经营许可证”传成“卫生许可证”)。光靠 <input> 自带的“未选择文件”提示毫无业务意义。
实操建议:
- 创建
<div id="file-list"></div>,在onchange后动态填充每条记录:<div class="file-item"> <span class="name"><code>营业执照.pdf</code></span> <span class="meta">1.2 MB, application/pdf</span> <button type="button" onclick="removeFile(0)">×</button> </div>
- 对
.name做简单校验(如包含“营业执”“许可”“ISO”等关键词),标黄提醒:“名称不含关键词,确认无误?” - 显示
f.size并换算为 KB/MB(Math.round(f.size / 1024)),超 10MB 的文件直接禁用提交按钮
用 FormData 组装多文件,注意字段名要匹配后端约定
后端接口通常要求所有文件走同一个字段(如 files[]),或按类型分字段(business_license、certificates[])。若前端拼错字段名,后端收不到任何文件,且无报错日志。
实操建议:
- 确认后端接收方式:
// 如果后端是 PHP/Laravel,通常期望: formData.append('files[]', file1); formData.append('files[]', file2); // 如果是 Java Spring Boot @RequestParam("license") MultipartFile,必须用: formData.append('license', file1); // 注意字段名完全一致 - 不要用
new FormData(formElement)自动收集——它会把所有<input>都塞进去,包括隐藏域、文本框,易污染 - 手动遍历
files并逐个append,同时加业务元数据:formData.append('files[]', file, file.name); // 第三个参数确保原始文件名不丢失 formData.append('upload_type', 'enterprise_qualification');
上传失败时,明确告诉用户哪个文件出问题了
网络中断、文件超限、后端拒绝(如 PDF 未签名)等错误,如果只弹“上传失败”,用户得重试全部文件,体验极差。尤其企业用户常同时处理多个资质,时间成本高。
实操建议:
- 用
Promise.allSettled()并行上传每个文件(而非打包成一个请求),便于定位单点失败:const uploadPromises = Array.from(files).map((file, i) => fetch('/api/upload', { method: 'POST', body: (() => { const fd = new FormData(); fd.append('file', file); return fd; })() }).then(r => r.json()) ); Promise.allSettled(uploadPromises).then(results => { results.forEach((r, i) => { if (r.status === 'rejected') { console.error(`第 ${i+1} 个文件上传失败:`, r.reason); // 在对应 .file-item 上加红色边框 + 提示文字 } }); }); - 后端返回的错误信息(如
{"code":400,"msg":"PDF 缺少数字签名"})应透传到对应文件项旁,不聚合显示 - 保留已成功上传的文件 ID(后端返回的
file_id),失败重试时跳过它们,避免重复计费或覆盖
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











