不能直接读整个File对象,因浏览器限制单次FileReader操作的内存与时长,超500MB易触发DOMException或致UI冻结;File为只读引用,slice生成新Blob需按字节分片,推荐1MB大小并用TextDecoder流式解码防UTF-8截断。

大文件无法直接用 FileReader.readAsText() 或 readAsArrayBuffer() 一次性读取,会卡死或内存溢出——必须手动分片读取。
为什么不能直接读整个 File 对象
浏览器对单次 FileReader 操作的内存和时长有限制;超过 500MB 的文本文件在 Chrome 中常触发 DOMException: Failed to execute 'readAsText' on 'FileReader': The requested file is too large.;即使不报错,主线程也会长时间阻塞,UI 冻结。
- File 对象本身是只读引用,不占内存,但
slice()后生成的Blob是新对象,需按需创建 -
File继承自Blob,所以可直接调用.slice(start, end),单位是字节(不是字符) - 文本编码(如 UTF-8)下,一个汉字可能占 2~4 字节,分片时不能按“行”或“字符”切,必须按字节边界操作
如何安全地分片读取并拼接结果
核心是:每次只 slice 一段、用 FileReader 异步读、读完再读下一段,避免并发堆积。关键要处理好 UTF-8 多字节字符被截断的问题(尤其末尾)。
- 推荐分片大小设为
1024 * 1024(1MB),太大易卡顿,太小增加 I/O 开销 - 不要用
readAsText()直接读分片——它会尝试解码,若末尾是不完整 UTF-8 字节序列,会丢字符或报错 - 改用
readAsArrayBuffer()读原始字节,再用TextDecoder流式解码(支持stream: true) - 最后一片读完后,调用
decoder.decode()(无参数)收尾,确保缓冲区残留字节被正确处理
const file = document.querySelector('input[type=file]').files[0];
const chunkSize = 1024 * 1024;
let offset = 0;
const decoder = new TextDecoder('utf-8', { stream: true });
let result = '';
<p>function readChunk() {
if (offset >= file.size) {
console.log('done:', result.length, 'chars');
return;
}
const blob = file.slice(offset, offset + chunkSize);
const reader = new FileReader();
reader.onload = e => {
const uint8 = new Uint8Array(e.target.result);
result += decoder.decode(uint8, { stream: true });
offset += chunkSize;
readChunk(); // 串行读下一片
};
reader.readAsArrayBuffer(blob);
}
readChunk();遇到换行符截断怎么办
如果业务需要按行处理(如 CSV、日志),不能依赖分片边界对齐换行符。必须在拼接后做行缓冲,等收到完整换行符(\n 或 \r\n)再 emit 一行。
立即学习“前端免费学习笔记(深入)”;
- 把
result改成字符串缓冲区,每次解码后用split('\n')提取完整行,保留最后一个不带\n的片段 - 注意 Windows 换行符是
\r\n,split('\n')会把\r留在前一行末尾,实际使用建议用正则split(/\r\n|\r|\n/) - 若文件极大(GB 级),别把整块内容存进内存,应边读边处理、边清理缓冲区
性能与兼容性要注意什么
TextDecoder 流式解码在所有现代浏览器都支持(Chrome 63+、Firefox 66+、Safari 14.1+),但 IE 完全不支持——如需兼容 IE,只能放弃流式,改用 base64 分段 + atob,或引入 encoding-indexes polyfill,代价很大。
- 不要用
async/await包裹FileReader(它不返回 Promise),封装成 Promise 是常见做法,但注意避免意外并发 - 移动端 Safari 对单个
Blob.slice()大小有限制(实测超 100MB 可能失败),建议下调chunkSize至 512KB - 上传场景中,分片读取 ≠ 分片上传;前者是客户端解析,后者是网络传输,两者逻辑独立,别混用同一套 offset 管理
最易被忽略的是:UTF-8 截断问题不在代码里显式报错,而是静默丢失字符。务必用含中文、emoji 的测试文件验证末尾是否完整。



















