You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 10:07:36