使用Node.js/TypeScript调用GCS的createWriteStream时遇ApiError: Not Found
解决GCS createWriteStream 出现「ApiError: Not Found」的问题
问题分析
你遇到的核心矛盾是:代码中指定了正确的bucket和文件名,打印验证也显示值正确,但实际请求URL却错误地将{organizationId}/orders.csv当作bucket名,导致资源找不到。这种情况通常不是代码逻辑的直接问题,而是Storage实例配置、SDK缓存或版本bug导致的路径解析异常。
排查与解决方案
1. 检查Storage实例的初始化
首先确认this.storage的创建过程是否正确,有没有误设置默认bucket或者错误的项目配置:
// 正确的初始化方式(无错误默认配置) import { Storage } from '@google-cloud/storage'; this.storage = new Storage({ // 仅在需要时配置认证信息,比如服务账号密钥 // keyFilename: './service-account-key.json' });
- 避免在初始化时传入
defaultBucket参数,否则会覆盖你后续指定的bucketName - 检查环境变量中是否存在
GOOGLE_CLOUD_STORAGE_BUCKET,这个变量会被SDK作为默认bucket使用
2. 验证文件的完整路径
在创建newFile后,打印完整路径确认解析是否正确:
const newFile = bucket.file(newFilePath); console.log('完整文件路径:', newFile.fullName); // 预期输出:shadowscore-data-import-production/orders.csv
如果输出不符合预期,说明bucket实例本身有问题,大概率是Storage初始化时的配置错误。
3. 升级GCS SDK到最新稳定版
旧版本的@google-cloud/storage(比如v5及更早版本)存在路径解析的bug,可能导致bucket和文件名混淆。执行以下命令升级:
npm install @google-cloud/storage@latest
4. 清除缓存并重启服务
Node.js的模块缓存可能残留旧的配置,导致代码修改后不生效:
- 重启你的Node.js服务
- 必要时删除
node_modules和package-lock.json,重新执行npm install
5. 确认bucket权限与存在性
虽然错误是「Not Found」,但也可能是权限问题导致的间接报错:
- 确认
shadowscore-data-import-production这个bucket确实存在于你的GCP项目中 - 确认服务账号拥有
storage.objects.create的权限(可在GCP控制台的IAM页面配置)
修正后的完整代码示例
import { Storage } from '@google-cloud/storage'; import { Readable } from 'stream'; class GcsUploadService { private storage: Storage; constructor() { // 初始化Storage,确保无错误配置 this.storage = new Storage({ // 按需添加认证信息 // credentials: require('./service-account.json') }); } async uploadStream(stream: Readable): Promise<string> { const bucketName = 'shadowscore-data-import-production'; const bucket = this.storage.bucket(bucketName); const newFilePath = 'orders.csv'; const newFile = bucket.file(newFilePath); // 打印验证信息 console.log('Bucket名称:', bucket.name); console.log('文件名称:', newFile.name); console.log('完整路径:', newFile.fullName); const writeStream = newFile.createWriteStream({ resumable: false, }); return new Promise((resolve, reject) => { stream.on('error', (error) => { writeStream.destroy(error); reject(error); }); writeStream.on('error', (error) => { stream.unpipe(); reject(error); }); writeStream.on('finish', () => { resolve(newFilePath); }); stream.pipe(writeStream); }); } }
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

