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

NextJS应用因大文件上传内存溢出崩溃,求优化方案

大文件上传内存占用过高的解决方案建议

你的核心问题是大文件上传时,请求体被完整加载到内存中,导致Docker内存耗尽崩溃。以下是针对性的解决方案:


1. 优化Multer配置,强制流式写入磁盘

当前代码用了diskStorage但仍可能存在内存缓存,通过以下配置确保文件直接落地磁盘,不经过内存缓冲:

import type { NextApiRequest, NextApiResponse } from "next";
import { createRouter } from "next-connect";
import multer from "multer";
// 扩展NextApiRequest类型,解决file属性的类型报错,无需//@ts-ignore
declare module 'next' {
  interface NextApiRequest {
    file?: Express.Multer.File;
  }
}

export const config = {
  api: {
    bodyParser: false,
  },
};

const upload = multer({
  storage: multer.diskStorage({
    destination: "./uploads",
    filename(req, file, callback) {
      callback(null, file.originalname);
    },
  }),
  // 设置内存阈值,超过该值直接写入磁盘
  limits: {
    fileSize: 10 * 1024 * 1024 * 1024, // 允许最大10GB文件(按需调整)
    fieldSize: 256, // 限制表单字段大小,减少内存占用
  },
  // 冗余配置确保文件直接写入磁盘
  dest: "./uploads",
});

const uploadMiddleware = upload.single("file");
const router = createRouter<NextApiRequest, NextApiResponse>();

router.use(uploadMiddleware);

router.post(async (req, res) => {
  res.status(200).json({ data: "Success" });
});

export default router.handler();

2. 实现分块上传(断点续传)

这是解决大文件内存问题的根本方案,将大文件分割为10MB左右的小分片逐个上传,后端接收后合并:

前端Axios示例

// 分片上传函数
async function uploadFileChunk(file, chunkIndex, totalChunks, chunkSize = 10 * 1024 * 1024) {
  const start = chunkIndex * chunkSize;
  const end = Math.min(file.size, start + chunkSize);
  const chunk = file.slice(start, end);
  const formData = new FormData();
  formData.append('file', chunk);
  formData.append('chunkIndex', chunkIndex);
  formData.append('totalChunks', totalChunks);
  formData.append('fileName', file.name);
  
  await axios.post('/api/upload-chunk', formData, {
    headers: { 'Content-Type': 'multipart/form-data' },
  });
}

// 触发上传
const file = document.querySelector('input[type="file"]').files[0];
const chunkSize = 10 * 1024 * 1024;
const totalChunks = Math.ceil(file.size / chunkSize);
for (let i = 0; i < totalChunks; i++) {
  await uploadFileChunk(file, i, totalChunks, chunkSize);
}
// 分片上传完成后请求合并文件
await axios.post('/api/merge-chunks', { fileName: file.name, totalChunks });

后端Next.js API路由

// pages/api/upload-chunk.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { createRouter } from "next-connect";
import multer from "multer";
import path from "path";

export const config = { api: { bodyParser: false } };

// 临时存储分片
const chunkStorage = multer.diskStorage({
  destination: "./temp-chunks",
  filename(req, file, callback) {
    const { fileName, chunkIndex } = req.body;
    callback(null, `${fileName}.chunk${chunkIndex}`);
  },
});

const uploadChunk = multer({ storage: chunkStorage }).single('file');
const router = createRouter<NextApiRequest, NextApiResponse>();

router.use(uploadChunk);

router.post(async (req, res) => {
  res.status(200).json({ success: true });
});

export default router.handler();
// pages/api/merge-chunks.ts
import type { NextApiRequest, NextApiResponse } from "next";
import fs from "fs";
import path from "path";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { fileName, totalChunks } = req.body;
  const outputPath = path.join("./uploads", fileName);
  const writeStream = fs.createWriteStream(outputPath);

  // 合并所有分片
  for (let i = 0; i < totalChunks; i++) {
    const chunkPath = path.join("./temp-chunks", `${fileName}.chunk${i}`);
    const chunkData = fs.readFileSync(chunkPath);
    writeStream.write(chunkData);
    fs.unlinkSync(chunkPath); // 删除临时分片
  }

  writeStream.end();
  res.status(200).json({ success: true, filePath: `/uploads/${fileName}` });
}

3. 调整Docker与Node.js内存配置

  • 限制Node.js堆内存,避免占用过多容器内存:
    在Docker启动命令中添加参数:

    node --max-old-space-size=1536 node_modules/.bin/next start
    

    (设置为1.5GB,预留500MB给系统进程)

  • 给Docker容器添加swap空间,防止内存不足直接崩溃:
    在docker-compose.yml中配置:

    services:
      your-nextjs-app:
        image: your-app-image
        deploy:
          resources:
            limits:
              memory: 2g
            reservations:
              memory: 1g
        volumes:
          - ./uploads:/app/uploads
          - ./temp-chunks:/app/temp-chunks
        command: sh -c "dd if=/dev/zero of=/swapfile bs=1M count=2048 && mkswap /swapfile && swapon /swapfile && node --max-old-space-size=1536 node_modules/.bin/next start"
    

4. 基础环境准备

确保上传目录存在并拥有写入权限:

mkdir -p ./uploads ./temp-chunks
chmod 755 ./uploads ./temp-chunks # 生产环境使用更严格的权限

内容的提问来源于stack exchange,提问作者Drogo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:36:01