最稳妥的二进制文件上传方式是 FormData + fetch/XMLHttpRequest;纯二进制数据则用 ArrayBuffer/Uint8Array/Blob 直接作 body 并设 Content-Type;禁用 base64 传输,避免膨胀与解码错误。
FormData 上传二进制文件最稳妥绝大多数场景下,直接用 FormData + XMLHttpRequest 或 fetch 是最可靠的方式。它自动处理 Content-Type、边界符(boundary)、编码,且兼容所有现代浏览器。
后,从 files[0] 拿到 Blob 或 File 对象FormData,调用 .append("file", file),键名按后端要求填写fetc
h 不需手动设 Content-Type —— 浏览器会自动生成带 boundary 的 multipart/form-data
XMLHttpRequest,也**不要**手动设 Content-Type,否则会禁用自动 boundary 生成const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async () => {
const file = input.files[0];
const formData = new FormData();
formData.append('upload', file); // 键名必须和后端约定一致
await fetch('/api/upload', {
method: 'POST',
body: formData // 不要加 headers: { 'Content-Type': ... }
});
});
ArrayBuffer + fetch 配 body
如果数据来自 canvas.toBlob()、WebAssembly 内存、或已解析的 ArrayBuffer,不需要走 FormData。这时应显式指定 Content-Type,并把二进制数据作为 body 直接发送。
ArrayBuffer、Uint8Array、Blob 都可直接赋给 fetch 的 body 选项headers: { 'Content-Type': 'application/octet-stream' } 或其他具体类型(如 image/png)multipart,解析方式完全不同ArrayBuffer 作 body 的支持从 iOS 16.4 / macOS 13.3 才稳定,旧版本建议转成 Uint8Array
const buffer = new ArrayBuffer(1024);
const view = new Uint8Array(buffer);
// ... 填充数据
await fetch('/api/binary', {
method: 'POST',
headers: { 'Content-Type': 'application/octet-stream' },
body: view // 或 buffer,或 new Blob([buffer])
});
XMLHttpRequest 上传时设 responseType = 'arraybuffer' 是为了接收返回的二进制上传二进制本身不依赖 responseType,但如果你上传后期待服务器返回一个二进制响应(比如生成的 PDF、压缩包),就必须提前设置 xhr.responseType = 'arraybuffer',否则 xhr.response 会是乱码字符串或 null。
'arraybuffer' 后,xhr.response 是 ArrayBuffer;设为 'blob' 则是 Blob
'json' 或 ''(空字符串)来接收二进制,会强制转字符串导致损坏base64 字符串当二进制直接发有人把图片转成 data:image/png;base64,...,然后试图把它整个塞进 FormData 或 body —— 这不是二进制,是文本,体积膨胀约 33%,后端还得额外解码,还容易因 URL 编码或换行符出错。
fetch(dataUrl) 获取 Blob,或用 atob() + Uint8Array 手动还原(仅限小数据)Blob 或 File,避免无谓的 base64 编解码data:...;base64, 前缀Blob、ArrayBuffer 还是字符串,再选对应路径。很多人卡住,是因为把 FileReader.result 当成二进制用了,其实它默认是字符串。