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

本地正常的SFTP转S3 Lambda代码线上无文件传输问题排查

问题:Node.js Lambda执行SFTP到S3文件传输时,异步代码未完成就提前终止

我有一个用于将文件从SFTP传输到S3存储桶的小型Node.js Lambda函数。本地触发代码时,可完整运行并重复完成文件传输;但上传至Lambda并通过测试事件触发后,代码执行通常无文件传输,也未收到任何错误信息。由于看不到s3.upload()内的console.log日志,推测可能是Lambda提前终止的问题。请问如何确保所有async/await代码在Lambda终止前执行完毕?(该代码曾成功运行过一次,但后续无法复现此结果!)

附原代码

const Client = require("ssh2-sftp-client");
const sftpConfig = require("./config/sftp");
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const sftpDir = "/mysftp/source/directory";
const bucketName = 'myTargetS3Bucket';

function timestampLessThanADayOld(timestampMilliseconds) {
  const currentTimestampMilliseconds = Date.now();
  const timeDifferenceMilliseconds =
    currentTimestampMilliseconds - timestampMilliseconds;
  return timeDifferenceMilliseconds < 1 * 24 * 60 * 60 * 1000;
}

const transferFile = async (file, sftp, bucketName) => {
  try {
    const sftpFilePath = `${sftpDir}/${file.name}`;
    console.log(`Attempting to transfer ${file.name}`);
    const fileData = await sftp.get(sftpFilePath);
    const s3Key = `Uploads/${file.name}`;
    const s3Params = {
      Bucket: bucketName,
      Key: s3Key,
      Body: fileData,
    };
    await s3.upload(s3Params, (err, data) => {
      console.log("data: ",data);
      if (err) {
        console.error("Error: ", err);
      } else {
        console.log(`Upload succeeded: ${data.Location}`);
      }
    });
  } catch (error) {
    console.error("Error reading file: ", error);
  }
};

const copyNewFiles = async () => {
  console.log("Transfer initiated");
  const sftp = new Client();
  try {
    await sftp.connect(sftpConfig);
    const files = await sftp.list(sftpDir);
    for (let i = 0; i < files.length; i++) {
      if (timestampLessThanADayOld(files[i].modifyTime)) {
        await transferFile(files[i], sftp, bucketName);
      }
    }
  } catch (error) {
    console.error("Error connecting to SFTP:", error);
  } finally {
    sftp.end();
  }
  console.log("Transfer complete");
};

module.exports.handler = async (event, context) => {
    await copyNewFiles();
}

// if executing locally, I add a `copyNewFiles();` here to trigger it, 
// and run it using `node sftpTransfer.js`

CloudWatch日志输出

2024-01-02T19:10:49.464+00:00   INIT_START Runtime Version: nodejs:14.v42 Runtime Version ARN: arn:aws:lambda:eu-north-1::runtime:xxxxxx

2024-01-02T19:10:50.234+00:00   START RequestId: 9eb64b6a-xxxxxx Version: $LATEST

2024-01-02T19:10:50.238+00:00   2024-01-02T19:10:50.238Z 9eb64b6a-xxxxxx INFO Transfer initiated

2024-01-02T19:10:53.319+00:00   2024-01-02T19:10:53.319Z 9eb64b6a-xxxxxx INFO Attempting to transfer MyFile.txt

2024-01-02T19:10:54.175+00:00   2024-01-02T19:10:54.175Z 9eb64b6a-xxxxxx INFO Transfer complete

2024-01-02T19:10:54.180+00:00   END RequestId: 9eb64b6a-xxxxxx

2024-01-02T19:10:54.180+00:00   REPORT RequestId: 9eb64b6a-xxxxxx Duration: 3944.60 ms Billed Duration: 3945 ms Memory Size: 1024 MB Max Memory Used: 89 MB Init Duration: 769.30 ms
解决方案

核心问题是**s3.upload()混用了回调函数和await,导致await并未真正等待上传操作完成**。AWS SDK的方法如果传入回调参数,就不会返回Promise,此时await相当于没有等待异步操作结束,Lambda会认为所有异步任务已完成,提前终止进程,导致上传操作被中断。

具体修改步骤:

  1. 将s3.upload()改为Promise形式,使用.promise()方法获取Promise对象,这样await才能正确等待上传完成。
  2. 把原来回调里的日志和错误处理逻辑,移到外层的try/catch中统一处理。

修改后的关键代码(transferFile函数):

const transferFile = async (file, sftp, bucketName) => {
  try {
    const sftpFilePath = `${sftpDir}/${file.name}`;
    console.log(`Attempting to transfer ${file.name}`);
    const fileData = await sftp.get(sftpFilePath);
    const s3Key = `Uploads/${file.name}`;
    const s3Params = {
      Bucket: bucketName,
      Key: s3Key,
      Body: fileData,
    };
    // 改用promise形式,让await等待上传完成
    const data = await s3.upload(s3Params).promise();
    console.log(`Upload succeeded: ${data.Location}`);
  } catch (error) {
    // 统一捕获SFTP读取和S3上传的错误
    console.error(`Error transferring ${file.name}: `, error);
  }
};

其他注意事项:

  • 确保Lambda的超时时间设置足够长,避免大文件上传时被强制终止(当前日志显示耗时约4秒,可根据文件大小适当调整)。
  • 检查Lambda执行角色是否具备S3上传的权限,避免因权限问题导致上传失败但无日志(不过原代码曾成功运行,权限大概率没问题)。

修改后重新上传Lambda测试,CloudWatch日志应该会出现Upload succeeded: xxx的日志,文件也能正常传输到S3。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:17:55