BigQuery导出数据到GCS后Cloud Function sFTP传输异常问题求助
问题根因排查
- 异步逻辑未等待完成:Cloud Function 执行生命周期中,主线程代码执行完毕后实例会直接被回收,你的代码中
sendSFTP内的client.connect、client.fastPut都是异步 Promise 操作,既没有加 await 等待执行完成,也没有将 Promise 返回到主流程,大部分情况下传输还没跑完函数就被强制终止,直接导致传输不完整、假死、耗时异常。 - 文件名硬编码错误:你当前直接根据当前日期生成要下载的文件名,完全没有用到 Cloud Storage 触发事件传入的文件信息,一旦触发事件的文件不是当日生成的,你下载的文件和触发的文件完全不匹配,甚至会出现文件不存在的错误。
- 连接未正常关闭:sftp 连接传输完成后没有手动释放连接,会导致连接泄漏,要么触发远端 sftp 服务的连接数限制,要么残留连接占用资源拖慢传输速度。
- 错误捕获逻辑缺失:下载、传输环节的错误没有向上抛出,函数执行状态永远返回成功,无法通过运行日志定位具体问题。
修复后可直接运行的代码
const Storage = require('@google-cloud/storage'); const os = require("os"); const fs = require("fs"); const Client = require('ssh2-sftp-client'); const path = require('path'); const storage = new Storage(); const cwd = os.tmpdir(); exports.gcfSendToSftp = async (event, context) => { // 从触发事件中直接获取存储桶和触发的文件名,避免硬编码错误 const bucketName = event.bucket; const srcFilename = event.name; const destFilename = path.join(cwd, srcFilename); try { // 1. 下载GCS文件到本地临时目录 await storage.bucket(bucketName).file(srcFilename).download({ destination: destFilename }); console.log(`文件下载完成:gs://${bucketName}/${srcFilename} => ${destFilename}`); // 校验本地文件大小 const stats = fs.statSync(destFilename); console.log(`本地文件大小:${stats.size} 字节`); // 2. 连接sftp并传输文件 const sftpClient = new Client(); const remotePath = `/Home/w2g/W2GInventoryIntegration/${srcFilename}`; await sftpClient.connect({ host: '111.11.11.11', port: '22', username: 'test', password: 'hokcqqEHH10g', readyTimeout: 15000 // 不要设为0,设置合理的超时时间避免无限等待 }); console.log('sftp连接成功'); await sftpClient.fastPut(destFilename, remotePath); console.log(`文件传输完成:${destFilename} => ${remotePath}`); // 关闭sftp连接释放资源 await sftpClient.end(); // 清理本地临时文件,避免占用/tmp空间 fs.unlinkSync(destFilename); return '传输成功'; } catch (err) { console.error('执行失败:', err); // 抛出错误让GCF标记执行失败,方便配置重试策略 throw err; } };
额外优化建议
- 敏感信息不要硬编码:将sftp的地址、账号、密码存入GCP Secret Manager,函数运行时动态读取,避免密码泄露。
- 调整函数配置:根据你的文件大小调整Cloud Function的超时时间(最大可设为9分钟)和内存配置,内存越大CPU性能越强,传输速度越快。
- 配置重试策略:在Cloud Function的触发器中配置失败重试,遇到网络波动等偶发问题自动重试,提升成功率。
- 大文件优化:如果文件超过1GB,可跳过下载到本地的步骤,直接创建GCS文件的读流,传给sftp的put方法,减少本地IO开销。
内容的提问来源于stack exchange,提问作者Felix Tran
相关产品推荐
相关产品推荐

