Node.js AWS Lambda中ssh2-sftp-client SFTP上传异常排查
问题描述
Node.js项目中使用ssh2-sftp-client实现本地文件夹单/多文件到SFTP目录的上传能力,上传逻辑采用Promise.all实现并发。代码本地测试运行正常,部署到AWS Lambda后日志显示上传成功,但实际SFTP目录内找不到对应上传文件,问题代码如下:
exports.handler= async (event)=>{ var Files = await Promise.all(FilesLIst.map(async (file) =>UploadToFtp(_customer,dir,file))); } function UploadToFtp(_customer,dir,file){ let remote = _customer.FTPPath + '/' + file; let client = new Client; const config = { host: _customer.FTPHost, port:_customer.FTPPort, username:_customer.FTPUserName, password: _customer.FTPPassword, }; return new Promise((res,rej)=>{ client.connect(config) .then(() => { let data=fs.readFileSync(dir+'/'+file); return client.put(data, remote); }) .then(() => { return client.end(); }) .catch(err => { console.error(err.message); }); }) }
排查修复方向
- 修复Promise状态流转缺陷(最高优先级)
现有代码属于典型的Promise反模式:外层自定义包装的Promise从未调用res()触发成功状态,catch块也未调用rej()抛出错误。
这会直接导致两个核心问题:Promise.all不会等待SFTP连接建立、文件上传、连接关闭的完整流程执行完成,Lambda会在事件循环清空后立刻冻结执行环境,此时文件可能还未完全写入SFTP服务端,连接未正常断开,服务端会直接丢弃未完成的临时上传文件- 所有上传过程中的报错都被
catch块静默吞掉,不会向外抛出,Lambda会默认所有任务执行成功,造成"上传成功"的假象
修复方式:移除多余的外层Promise包装,直接返回ssh2-sftp-client原生的Promise链,流程正常结束后返回明确的成功状态,报错时直接向外抛出异常,禁止静默吞错。
- 校验配置与路径正确性
- 核对Lambda环境中配置的SFTP地址、端口、账号密码、远端根路径
FTPPath,确认和本地测试环境指向同一套SFTP服务,避免出现连测试环境却到生产环境查找文件的问题 - 本地文件路径、远端SFTP路径统一使用Node.js原生
path.join()方法拼接,不要手动拼接/,避免因Lambda执行环境工作目录和本地不一致导致的文件读取失败、远端路径格式错误 - 确认Lambda中待上传的文件都存储在
/tmp目录下,Lambda执行环境仅/tmp目录具备写入权限,如果业务逻辑存在先写本地文件再上传的步骤,非/tmp路径会直接写入失败。
- 核对Lambda环境中配置的SFTP地址、端口、账号密码、远端根路径
- 优化SFTP客户端实现逻辑
- 不要为每个上传文件单独新建SFTP连接,所有文件上传复用同一个客户端实例,减少连接建立开销的同时,避免短时间内创建大量连接触发SFTP服务端的限流、IP拦截规则
- 调用
client.end()关闭连接前,确认文件上传操作已完全落盘,可在上传完成后校验远端文件大小与本地文件一致,再执行连接关闭操作 - 给SFTP客户端配置合理的连接、传输超时时间,避免连接静默断开导致的上传失败无感知。
- 调整并发与网络配置
- 不要使用无并发限制的
Promise.all执行批量上传,增加并发数控制(建议单批同时上传不超过3个文件),避免高并发下出现连接超时、服务端拒绝连接的问题 - 检查Lambda网络配置:如果SFTP服务端部署在内网,确认Lambda已接入对应VPC,安全组出方向放开SFTP对应端口的访问权限,网络ACL未拦截对应流量
- 核对SFTP服务端的访问日志,确认Lambda发起的连接是否正常到达服务端、文件写入请求是否被正常处理,排除服务端自动清理异常连接上传文件的规则影响。
- 不要使用无并发限制的
- 验证Lambda退出时机
在Promise.all执行完成后增加明确的完成日志,打印所有文件对应的远端路径、上传状态,确认Lambda是在所有上传逻辑执行完成后才退出,不存在提前终止执行的问题。
内容的提问来源于stack exchange,提问作者G S M
相关产品推荐
相关产品推荐

