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

从S3获取PDF遇TypeError: data.pipe不是函数,求无磁盘邮件附件方案

问题:从S3获取文件报错,如何在内存中存储文件作为邮件附件?

我想从S3获取文件后直接作为邮件附件,不需要存储到磁盘,但现在获取文件阶段就报错了。我的代码如下:

const s3 = require('@aws-sdk/client-s3')

const bucketParams = {
    "Bucket": "test-upload",
    "Key": 'test.pdf'
};
try {
    const data = await client.send(new s3.GetObjectCommand(bucketParams));
    await new Promise((resolve, reject) => {
        data.pipe(fs.createWriteStream('example.pdf'))
            .on('error', err => reject(err))
            .on('close', () => resolve())
    })
    console.log('File saved successfully');
} catch (err) {
    console.log(err);
}

运行后出现以下错误:

TypeError: data.pipe is not a function

而且生成的文件是空的,格式也不正确。请问我哪里出错了?以及如何在内存中存储该文件,以便无需存磁盘即可作为邮件附件?


问题原因与解决方案

1. 报错原因:错误调用流对象

AWS SDK v3中,GetObjectCommand返回的data对象本身不是可读流,真正的可读流在data.Body属性里。你直接调用data.pipe()自然会报错,因为data没有这个方法。

如果只是想修复存磁盘的代码,只需要把data.pipe改成data.Body.pipe:

const s3 = require('@aws-sdk/client-s3');
const fs = require('fs');

// 假设client已完成初始化
const bucketParams = {
  Bucket: "test-upload",
  Key: 'test.pdf'
};
try {
  const data = await client.send(new s3.GetObjectCommand(bucketParams));
  await new Promise((resolve, reject) => {
    // 改为data.Body.pipe
    data.Body.pipe(fs.createWriteStream('example.pdf'))
      .on('error', err => reject(err))
      .on('close', () => resolve())
  })
  console.log('File saved successfully');
} catch (err) {
  console.log(err);
}

2. 内存存储文件(转为Buffer)

要实现无需存磁盘直接作为邮件附件,你需要把S3返回的流转换成Buffer,之后就可以直接将Buffer作为附件内容传入邮件库(比如nodemailer)。

方法一:手动收集流数据

不需要额外依赖,手动监听流的data和end事件:

const getS3FileAsBuffer = async () => {
  const data = await client.send(new s3.GetObjectCommand(bucketParams));
  const chunks = [];
  
  return new Promise((resolve, reject) => {
    data.Body.on('data', (chunk) => chunks.push(chunk));
    data.Body.on('end', () => resolve(Buffer.concat(chunks)));
    data.Body.on('error', (err) => reject(err));
  });
};

// 使用示例
const fileBuffer = await getS3FileAsBuffer();
// 用nodemailer发送邮件时的附件配置
// attachments: [{ filename: 'test.pdf', content: fileBuffer }]

方法二:使用concat-stream简化操作

安装依赖:npm install concat-stream,然后用它直接把流转为Buffer:

const concat = require('concat-stream');

const getS3FileAsBuffer = async () => {
  const data = await client.send(new s3.GetObjectCommand(bucketParams));
  
  return new Promise((resolve, reject) => {
    data.Body.pipe(concat({ encoding: 'buffer' }, (buffer) => {
      resolve(buffer);
    })).on('error', reject);
  });
};

// 使用方式同上,直接将buffer作为邮件附件内容

总结

  • 核心错误是没有正确访问S3返回的流对象:必须用data.Body而不是data来调用流方法。
  • 内存存储的关键是将可读流转换为Buffer,之后可以直接传递给邮件库作为附件内容,完全不需要写入磁盘。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:33:25