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

Node.js中@aws-sdk/client-s3内存泄漏警告问题求助

解决@aws-sdk/client-s3的MaxListenersExceededWarning内存泄漏警告

问题根源

你当前的代码在每次调用s3Upload和s3DownloadFile时都会创建新的S3Client实例。每个S3Client会初始化独立的HTTP连接池,多次调用后会累积大量事件监听器,超出Node.js默认的监听器数量限制,从而触发MaxListenersExceededWarning。

解决方案

1. 复用S3Client实例

将S3Client实例的创建移到模块顶部,整个应用复用同一个客户端实例,避免重复创建连接和监听器。

修改后的s3Service.js代码:

const {
  S3Client,
  PutObjectCommand,
  GetObjectCommand,
} = require("@aws-sdk/client-s3");

// 全局复用一个S3Client实例
const s3client = new S3Client({ region: process.env.AWS_REGION });

exports.s3Upload = async (file, fileKey) => {
  const param = {
    Bucket: process.env.AWS_BUCKET_NAME,
    Key: fileKey,
    Body: file.buffer,
  };

  return s3client.send(new PutObjectCommand(param));
};

exports.s3DownloadFile = async (fileKey) => {
  const param = {
    Bucket: process.env.AWS_BUCKET_NAME,
    Key: fileKey,
  };

  try {
    return await s3client.send(new GetObjectCommand(param));
  } catch (error) {
    console.error("Error", error);
  }
};

2. 优化下载流的处理

在下载路由中,确保流在结束或出错时正确清理,避免潜在的资源泄漏:

修改后的路由代码:

/** Import statements */
const express = require("express");

const { s3DownloadFile } = require("../s3Service");
const { Company } = require("../models/companies");
const { Backup } = require("../models/backups");

const router = express.Router();

router.get("/download/:companyName/latest", async (req, res) => {
  try {
    const company = await Company.findOne({
      where: {
        companyName: req.params.companyName,
      },
    });

    if (!company)
      return res.status(404).send("The specified company does not exist");

    const backups = await Backup.findAll({
      where: {
        companyID: company.dataValues.companyID,
      },
      order: [["createdAt", "DESC"]],
    });

    if (backups.length === 0)
      return res.status(404).send("There are no backups for this company");

    let fileKey = `${company.companyID}/${backups[0].dataValues.fileName}`;
    const data = await s3DownloadFile(fileKey);

    // Set appropriate headers for the file download
    res.attachment(fileKey);

    // 处理流的错误和结束事件,确保资源释放
    data.Body.pipe(res);
    
    data.Body.on("error", (err) => {
      if (!res.headersSent) {
        res.status(500).send("File download failed");
      }
    });

    res.on("finish", () => {
      data.Body.destroy();
    });
  } catch (error) {
    res.status(500).send(error.message);
  }
});

module.exports = router;

3. 临时调整监听器上限(不推荐)

如果上述方案无法立即生效,可以临时提高事件监听器的上限,但这只是治标不治本的方法:
在应用入口文件顶部添加:

require('events').EventEmitter.defaultMaxListeners = 20;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 13:07:39