JavaScript向服务器上传文件,核心路径是:用<input type="file">或拖拽拿到File对象,装进FormData,再通过fetch或XMLHttpRequest以multipart/form-data发到后端接口,由后端落盘或转存对象存储。 小文件直接传,大文件切片传,跨域问题交给开发代理和服务端CORS配合解决。
js上传文件到服务器怎么实现:从选文件到发请求
先拿到File对象
浏览器不会把本地文件路径直接交给前端脚本,你拿到的是一个File对象,它继承自Blob,带有name、size、type等属性,常见入口有两个:
- 文件选择框:监听
input的change事件,读取input.files。 - 拖拽上传:监听
drop事件,读取event.dataTransfer.files。
<input type="file" id="fileInput" accept="image/,.pdf">
const input = document.getElementById('fileInput');
input.addEventListener('change', () => {
const file = input.files[0];
if (!file) return;
console.log(file.name, file.size, file.type);
});
拿到File后,先做前端基础校验:大小、扩展名、MIME类型,前端校验只是体验层,后端仍要再校验一次。
用FormData组装上传内容
FormData是浏览器提供的表单数据容器,用来构造multipart/form-data请求体,据MDN文档,FormData可以直接追加File或Blob,浏览器会自动处理边界字符串。
const formData = new FormData();
formData.append('file', file);
formData.append('bizType', 'avatar');
这里有个常见坑:用FormData时不要手动设置Content-Type,浏览器需要自动生成multipart/form-data; boundary=...,你手动写死Content-Type反而会丢掉boundary,后端解析失败。
fetch上传与进度反馈
简单上传用fetch很清爽:
await fetch('/api/upload', {
method: 'POST',
body: formData
});
但fetch原生不提供上传进度,需要进度条、取消上传时,用XMLHttpRequest更稳:
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload');
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) 100);
console.log(percent);
}
};
xhr.onload = () => console.log(xhr.status, xhr.responseText);
xhr.send(formData);
后端接收方式取决于技术栈:Node/Express常用multer,Java Spring用MultipartFile,PHP用$_FILES,Nginx还要关注client_max_body_size,否则请求可能在网关层就被拦掉。
FormData和Base64上传文件有什么区别?先看请求体
请求体与体积对比
| 对比项 | FormData | Base64 |
|---|---|---|
| 传输格式 | multipart/form-data |
JSON或文本字符串 |
| 体积变化 | 接近原始文件 | 明显变大 |
| 后端处理 | 需要multipart解析 | 需要解码字符串 |
| 适用场景 | 文档、图片、视频、多文件 | 小图标、配置内嵌、无文件接口 |
| 进度与中断 | XHR可做上传进度 | 需要额外封装 |
FormData更适合真实文件上传,Base64把二进制转成字符串,体积会变大,还会占用更多内存,传大文件不划算,小图标、头像裁剪后的短数据,偶尔用Base64塞进JSON可以省一个接口。
什么时候选哪个
- 大文件、多文件、带表单字段:选
FormData。 - 小图片内嵌、后端只收JSON:可以权衡Base64。
- 云存储直传:后端签发临时URL,前端用
PUT发原始二进制,不经过业务服务器中转。
后端接收要点
- Express:
multer({ dest: 'uploads/', limits: { fileSize: ... } })。 - Spring:接口参数用
MultipartFile,配置文件大小限制。 - Nginx:设置
client_max_body_size,例如20m,按业务调整。 - 网关:关注超时时间,大文件上传别被短超时切断。
前端上传大文件到服务器用什么方法:切片、断点与进度条
分片上传
大文件直接扔给接口,弱网下失败重传成本很高,分片思路是:把文件切成若干块,逐块上传,全部成功后再通知后端合并。
const CHUNK_SIZE = 2 1024 1024;
const chunks = [];
for (let start = 0; start < file.size; start += CHUNK_SIZE) {
chunks.push(file.slice(start, start + CHUNK_SIZE));
}
async function uploadChunk(chunk, index) {
const fd = new FormData();
fd.append('file', chunk);
fd.append('chunkIndex', index);
fd.append('totalChunks', chunks.length);
await fetch('/api/upload/chunk', { method: 'POST', body: fd });
}
业内常见的分片大小在1MB到5MB之间,具体看网络环境和后端限制,并发数别拉太高,3到5个并发比较常见,再配合失败重试。
断点续传与秒传
断点续传的关键是给文件一个稳定标识,常用做法是计算文件哈希,比如用SparkMD5分片计算,再在上传前问后端哪些分片已存在,秒传则是后端发现相同哈希的文件已存在,直接返回成功。
- 上传前:请求
/api/upload/check,带文件哈希和分片信息。 - 上传中:只传缺失分片,记录成功分片。
- 上传后:请求
/api/upload/merge,后端按顺序合并。
进度与取消
XMLHttpRequest的upload.onprogress能给单分片进度;总进度按已上传分片大小累加,取消上传时,xhr.abort()或AbortController都能中断请求,直传对象存储时,后端签名要限制文件类型、大小和过期时间,前端只负责PUT。
本地开发环境js上传文件跨域怎么解决
跨域来自协议、域名、端口不一致,开发阶段最省事的办法是配代理,让浏览器以为请求同源。
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
};
生产环境要么同域部署,要么后端明确配置CORS:
Access-Control-Allow-Origin写具体域名,不要图省事写还带凭证。Access-Control-Allow-Methods包含POST、OPTIONS。- 带Cookie时,前端设置
credentials,后端不能再用通配符。 - Nginx反向代理也能把上传请求转到后端服务。
上传接口还要防一类问题:只靠前端校验文件类型很容易被绕过,业内专家指出,后端应同时校验MIME、扩展名、文件头和大小,并把上传目录设置为不可执行,避免恶意文件被直接访问。
国内项目js上传文件到服务器成本怎么算
成本大头通常不在前端代码,而在流量、存储和请求次数,国内项目常见选择是自建服务器磁盘或使用对象存储加CDN,行业共识认为,文件越多、访问越频繁,越应该把存储和带宽压力从业务服务器上拆出去。
- 小规模内部系统:服务器磁盘加定期备份,成本直观,运维简单。
- 对外图片、视频、附件:对象存储更合适,按量计费,扩容方便。
- 大文件下载多:CDN回源和流量费用要提前估算。
- 合规要求高:关注日志审计、访问权限、私有读和临时签名。
选型时别只看单价,还要算上传失败重试、带宽峰值、图片处理、转码和备份。普通文件用FormData加fetch/XHR,大文件切片后直传对象存储,跨域交给代理和CORS,这套组合在多数项目里够用。
js上传文件到服务器常见问题
fetch上传文件需要手动设置Content-Type吗?
不需要,使用FormData时,浏览器会自动生成multipart/form-data和boundary,手动设置Content-Type会破坏边界信息,后端可能解析不到文件。
上传大文件一定要用分片吗?
不一定,文件小、网络稳定时直接传更简单,文件大、弱网、需要断点续传或失败重试时,分片更合适,是否分片要结合文件大小、失败成本和后端合并能力评估。
前端上传文件能直接传到服务器磁盘吗?
不能,浏览器只能把文件发送到服务端接口,落盘由后端程序或对象存储完成,前端脚本没有直接写服务器磁盘的权限。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/790036.html


