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

将AWS S3文件通过Pinata上传IPFS时出现Stream未定义错误如何解决

问题原因与解决方案

错误根因

  • 运行环境不匹配:你当前编写的是Node.js后端代码,而使用REACT_APP_前缀的环境变量说明你大概率在React前端浏览器环境中运行这段逻辑。浏览器原生没有内置Node.js的Stream模块,AWS SDK的createReadStream方法完全依赖Node.js Stream API,因此触发Cannot read property 'Stream' of undefined报错。
  • 配置参数错误:AWS S3 初始化时,region参数应该放在构造函数的根配置项中,而非嵌套在credentials对象内,参数配置错误会导致getObject返回异常对象,调用createReadStream失败。
  • 密钥暴露风险:所有REACT_APP_开头的环境变量都会在前端打包后暴露给所有访问用户,你当前的AWS密钥、Pinata密钥完全处于泄露状态,恶意用户可以直接篡改你的S3资源和Pinata存储空间。
  • 异步逻辑错误:createReadStream是同步调用,直接在其后链式调用catch无法捕获getObject的异步操作异常。

推荐解决方案(最优)

将这段逻辑迁移到Node.js后端运行,避免密钥泄露同时适配Stream API:

  1. 首先修正S3初始化配置:
const s3 = new AWS.S3({
  accessKeyId: s3AccessKeyId,
  secretAccessKey: s3AccessSecret,
  region: s3Region // region移出credentials对象,放在根配置
});
  1. 修正流处理逻辑,调整为完整可运行的异步函数:
const AWS = require('aws-sdk');
const axios = require('axios');
const FormData = require('form-data');

const s3AccessKeyId = process.env.S3_ID; // 改用后端环境变量,不要带REACT_APP_前缀
const s3AccessSecret = process.env.S3_SECRET;
const s3Region = process.env.S3_REGION;
const s3Bucket = process.env.S3_BUCKET_NAME;
const apiKey = process.env.PINATA_KEY;
const apiSecret = process.env.PINATA_SECRET;

const uploadToPinataFromS3 = async (fileName) => {
  const form = new FormData();
  const url = 'https://api.pinata.cloud/pinning/pinFileToIPFS';
  
  const s3 = new AWS.S3({
    accessKeyId: s3AccessKeyId,
    secretAccessKey: s3AccessSecret,
    region: s3Region
  });

  // 生成S3读取流,监听错误事件
  const s3Stream = s3.getObject({
    Bucket: s3Bucket,
    Key: fileName
  }).createReadStream();
  s3Stream.on('error', (err) => {
    console.log('S3读取错误:', err);
    throw err;
  });

  form.append('file', s3Stream, {
    filename: fileName
  });

  const config = {
    method: 'post',
    url: url,
    maxBodyLength: Infinity,
    headers: {
      'pinata_api_key': apiKey,
      'pinata_secret_api_key': apiSecret,
      ...form.getHeaders()
    },
    data: form
  };

  try {
    const response = await axios(config);
    return { success: true, data: response.data };
  } catch (error) {
    return { success: false, data: error.message };
  }
}

临时前端适配方案(不推荐,仅做参考)

如果必须在前端实现该逻辑,需更换为浏览器适配的AWS SDK v3,不使用Stream直接读取文件为Blob上传:

  1. 安装AWS SDK v3浏览器适配包:
    npm install @aws-sdk/client-s3
  2. 前端代码示例:
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import axios from "axios";

const s3Client = new S3Client({
  region: process.env.REACT_APP_REGION,
  credentials: {
    accessKeyId: process.env.REACT_APP_ID,
    secretAccessKey: process.env.REACT_APP_SECRET,
  }
});

const uploadToPinata = async (fileName) => {
  // 从S3拉取文件转Blob
  const s3Response = await s3Client.send(new GetObjectCommand({
    Bucket: process.env.REACT_APP_BUCKET_NAME,
    Key: fileName
  }));
  const fileBlob = await s3Response.Body.transformToBlob();
  const file = new File([fileBlob], fileName);

  // 构造表单上传到Pinata
  const form = new FormData();
  form.append('file', file);
  const config = {
    method: 'post',
    url: 'https://api.pinata.cloud/pinning/pinFileToIPFS',
    headers: {
      'pinata_api_key': process.env.REACT_APP_PINATA_KEY,
      'pinata_secret_api_key': process.env.REACT_APP_PINATA_SECRET,
    },
    data: form
  };
  try {
    const res = await axios(config);
    return { success: true, data: res.data };
  } catch (err) {
    return { success: false, data: err.message };
  }
}

注意:该方案仍存在密钥泄露风险,仅适合本地测试使用,生产环境必须使用后端上传方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 09:18:00