文件上传是表单里非常常见的功能,比如头像上传、身份证图片提交、附件上传、相册选择、简历投递等。HTML 原生的 input type="file" 已经提供了基础能力,再配合 accept、multiple 和少量 JavaScript,就能完成大多数前端交互。
不过文件上传也容易踩坑:前端限制不等于安全校验,图片预览不等于已经上传成功,多文件选择也不等于后端一定能接收。本文从基础控件讲起,介绍文件类型限制、多文件上传、前端预览和安全注意事项。
基础控件
最基础的文件上传控件写法很简单,只需要把输入框类型设置为 file。如果表单要直接提交文件,通常还需要设置 method="post" 和 enctype="multipart/form-data"。
<form action="/upload" method="post" enctype="multipart/form-data">
<label for="avatar">上传头像</label>
<input id="avatar" name="avatar" type="file">
<button type="submit">提交</button>
</form>
enctype="multipart/form-data" 是传统表单上传文件时的重要配置。如果少了它,后端可能收不到文件内容。使用 Ajax 和 FormData 上传时,也要确保请求体按文件表单方式发送。
accept 限制类型
accept 用来提示浏览器允许用户选择哪些类型的文件。比如只允许选择图片,可以写成 accept="image/*";只允许 PDF,可以写成 accept="application/pdf"。
<input type="file" accept="image/*">
<input type="file" accept=".pdf,.doc,.docx">
accept 只是前端选择提示,不是安全边界。用户仍然可能通过修改文件扩展名、拖拽、接口请求等方式绕过限制。因此后端必须重新检查文件类型、大小和内容。

multiple 多文件
如果希望用户一次选择多个文件,可以添加 multiple 属性。浏览器会允许用户多选,JavaScript 中可以通过 files 列表读取所有文件。
<input id="photos" name="photos" type="file" accept="image/*" multiple>
多文件上传时,前端最好给出数量限制和大小提示。比如最多选择 9 张图片、单张不超过 5 MB。即使前端做了限制,后端也要再次校验,避免用户绕过页面直接提交超大文件。
读取 File 对象
文件控件的 files 属性会返回一个类数组对象,每一项都是 File。它包含文件名、大小、类型、最后修改时间等信息。
const input = document.querySelector('#photos');
input.addEventListener('change', () => {
const files = Array.from(input.files);
files.forEach((file) => {
console.log(file.name, file.size, file.type);
});
});
file.size 的单位是字节,显示给用户时通常需要转换成 KB 或 MB。file.type 是浏览器识别出的 MIME 类型,但它也不能作为唯一安全依据。
图片预览
图片上传前,常见需求是先在页面上显示预览。可以使用 URL.createObjectURL() 为本地文件生成临时地址,再把它赋给图片的 src。
<input id="avatar" type="file" accept="image/*">
<img id="preview" alt="图片预览" width="160">
const avatar = document.querySelector('#avatar');
const preview = document.querySelector('#preview');
avatar.addEventListener('change', () => {
const file = avatar.files[0];
if (!file) return;
preview.src = URL.createObjectURL(file);
});
这种方式适合预览图片,不需要先上传到服务器。对于大图或频繁切换的预览,使用完临时地址后可以调用 URL.revokeObjectURL() 释放资源。
前端大小校验
为了避免用户选择过大的文件,可以在前端先做一次大小判断。这样能减少无效上传,也能更快给用户反馈。
const maxSize = 5 * 1024 * 1024;
if (file.size > maxSize) {
alert('文件不能超过 5MB');
}
前端大小校验主要是体验优化。真正的上传限制仍然要由后端、Web 服务器和应用配置共同控制,比如 Nginx 的请求体大小限制、PHP 或 Node.js 的上传大小限制等。
使用 FormData 上传
如果不想使用传统表单提交,可以用 FormData 结合 fetch 上传文件。不要手动设置错误的 Content-Type,浏览器会自动生成带边界的 multipart 请求头。
const formData = new FormData();
formData.append('avatar', file);
fetch('/upload', {
method: 'POST',
body: formData
});
如果是多文件,可以循环追加,字段名根据后端约定决定。有些后端喜欢 photos[],有些后端使用同名字段多次追加,前后端要提前约定清楚。
拖拽上传
拖拽上传本质上也是读取文件对象。用户把文件拖到指定区域后,可以从拖拽事件里拿到 dataTransfer.files。
dropArea.addEventListener('drop', (event) => {
event.preventDefault();
const files = Array.from(event.dataTransfer.files);
console.log(files);
});
拖拽上传体验更好,但也要处理拖入、拖出、释放、错误提示等状态。如果只是简单表单,不一定需要一开始就做拖拽交互,原生文件控件已经足够。
安全注意事项
文件上传是高风险入口。后端必须限制文件大小、扩展名、MIME 类型、真实文件内容,并且不要把用户上传文件直接放到可执行目录。图片也应重新处理或转存,避免伪装文件和恶意内容。
文件名也不能直接信任。用户上传的原始文件名可能包含特殊字符、路径片段或重复名称。更稳妥的做法是在后端生成新的文件名,并把原始文件名只作为展示信息保存。
实践建议
一个稳妥的上传流程通常是:前端用 accept 限制选择范围,用 multiple 控制是否允许多选,用 JavaScript 做大小和数量提示,用预览提升体验;后端重新校验类型、大小和权限,生成安全文件名,存储到合适位置。
不要把前端校验当成安全措施,也不要为了炫技把上传控件做得过重。先保证文件选择、预览、错误提示和后端校验闭环,再根据业务需要增加拖拽、进度条、断点续传等高级能力。














暂无评论内容