Angular5+Node.js上传200MB文件时出现Node.js内存堆溢出
兄弟,这个问题我太有发言权了!你这明显是Node.js内存溢出搞的鬼——看堆栈里那个超大的数组长度(217330529),肯定是你把整个200MB的文件一次性全读进内存了,Node.js默认的内存限制扛不住这种操作,直接崩给你看。
下面给你一套落地的解决方案,从前端到后端全给你捋明白:
解决方案分步走
1. 前端(Angular 5)改成分片上传
别一次性传整个大文件,切成小块(比如10MB/块),这样前端不会卡死,后端也不会爆内存。用Angular自带的File.slice()就能轻松实现:
// Angular组件里的分片上传示例逻辑 uploadLargeFile(file: File) { const chunkSize = 10 * 1024 * 1024; // 10MB每块,可根据需求调整 const totalChunks = Math.ceil(file.size / chunkSize); let currentChunk = 0; // 递归上传分片 const uploadChunk = () => { const start = currentChunk * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); const formData = new FormData(); formData.append('chunk', chunk); formData.append('fileName', file.name); formData.append('chunkIndex', currentChunk.toString()); formData.append('totalChunks', totalChunks.toString()); // 用HttpClient上传当前分片 this.http.post('/api/upload-chunk', formData, { reportProgress: true, observe: 'events' }).subscribe({ next: (event) => { // 这里可以监听上传进度,做进度条展示 if (event.type === HttpEventType.Response) { currentChunk++; if (currentChunk < totalChunks) { uploadChunk(); } else { // 所有分片传完,通知后端合并文件 this.http.post('/api/merge-chunks', { fileName: file.name, totalChunks }).subscribe(); } } }, error: (err) => console.error('分片上传失败', err) }); }; uploadChunk(); }
2. 后端(Node.js)处理分片 + 调整内存(临时方案)
临时救急:调大Node.js内存上限
如果你暂时不想改代码,可以先给Node.js加启动参数,把内存上限拉高:
node --max-old-space-size=4096 server.js # 把内存上限调到4GB,默认一般是1.4GB左右
但这只是权宜之计,大文件还是分片上传最靠谱。
长期方案:后端流式处理分片
用Express写个分片接收+合并的示例,全程流式操作,不把整个文件读进内存:
const express = require('express'); const fs = require('fs'); const path = require('path'); const multer = require('multer'); // 用来处理FormData const app = express(); const uploadDir = path.join(__dirname, 'temp_uploads'); // 确保临时上传目录存在 if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir); // 配置multer,存储分片到临时目录 const storage = multer.diskStorage({ destination: (req, file, cb) => cb(null, uploadDir), filename: (req, file, cb) => { const { fileName, chunkIndex } = req.body; cb(null, `${fileName}.chunk${chunkIndex}`); } }); const upload = multer({ storage }); // 接收分片接口 app.post('/api/upload-chunk', upload.single('chunk'), (req, res) => { res.status(200).send('分片接收成功'); }); // 合并分片接口 app.post('/api/merge-chunks', (req, res) => { const { fileName, totalChunks } = req.body; const finalFilePath = path.join(__dirname, 'uploads', fileName); const writeStream = fs.createWriteStream(finalFilePath); const mergeNextChunk = (index) => { if (index >= totalChunks) { writeStream.end(); // 合并完成后删除临时分片文件 for (let i = 0; i < totalChunks; i++) { const chunkPath = path.join(uploadDir, `${fileName}.chunk${i}`); fs.unlinkSync(chunkPath); } return res.status(200).send('文件合并完成'); } const chunkPath = path.join(uploadDir, `${fileName}.chunk${index}`); const readStream = fs.createReadStream(chunkPath); readStream.pipe(writeStream, { end: false }); readStream.on('end', () => mergeNextChunk(index + 1)); readStream.on('error', (err) => res.status(500).send(`合并失败:${err.message}`)); }; mergeNextChunk(0); }); app.listen(3000, () => console.log('服务启动在3000端口'));
3. 额外优化小技巧
- 前端可以给上传加个进度条,用上面代码里的
reportProgress就能实现,提升用户体验。 - 可以给分片加MD5校验,避免分片损坏或者丢包,上传前算好每个分片的MD5,后端接收后校验。
- 如果是生产环境,记得给上传接口加权限验证,别谁都能传文件。
内容的提问来源于stack exchange,提问作者Khan
相关产品推荐
相关产品推荐

