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

如何使用Node.js配合aws-sdk实现定时S3桶跨桶同步任务?

Node.js实现S3桶间定时全量/增量同步方案

1. 前置依赖安装

优先使用AWS SDK v2(兼容性更好,存量项目适配成本低),执行命令安装:
npm install aws-sdk

如果使用AWS SDK v3,安装对应组件:
npm install @aws-sdk/client-s3

2. 可直接运行的同步代码(AWS SDK v2版本)

const AWS = require('aws-sdk');

// 配置AWS凭证,也可以通过环境变量、~/.aws/credentials、IAM角色自动获取,无需硬编码
AWS.config.update({
  // accessKeyId: '你的AK', 
  // secretAccessKey: '你的SK',
  region: '源桶所在区域,比如ap-southeast-1'
});

const s3 = new AWS.S3();
const sourceBucket = '源桶名称';
const targetBucket = '目标桶名称';
// 配置是否删除目标桶中源桶已不存在的文件,对应aws cli的 --delete 参数
const enableDelete = false;

// 列出指定桶的所有对象,处理1000条分页限制
async function listAllObjects(bucket) {
  let allObjects = [];
  let continuationToken = null;
  do {
    const params = {
      Bucket: bucket,
      ContinuationToken: continuationToken
    };
    const response = await s3.listObjectsV2(params).promise();
    allObjects = allObjects.concat(response.Contents);
    continuationToken = response.NextContinuationToken;
  } while (continuationToken);
  return allObjects;
}

// 同步主逻辑
async function syncS3Buckets() {
  console.log('开始同步S3桶:', sourceBucket, '->', targetBucket);
  // 分别获取源桶和目标桶的所有对象
  const [sourceObjects, targetObjects] = await Promise.all([
    listAllObjects(sourceBucket),
    listAllObjects(targetBucket)
  ]);

  // 把目标桶对象转成Key为索引的map,方便快速对比
  const targetObjMap = new Map();
  targetObjects.forEach(obj => {
    targetObjMap.set(obj.Key, obj);
  });

  // 遍历源桶对象,需要同步的加入任务队列
  const copyTasks = [];
  sourceObjects.forEach(sourceObj => {
    const targetObj = targetObjMap.get(sourceObj.Key);
    // 目标桶不存在该文件,或文件大小/最后修改时间不一致则需要同步
    if (!targetObj || sourceObj.Size !== targetObj.Size || sourceObj.LastModified > targetObj.LastModified) {
      copyTasks.push(
        s3.copyObject({
          Bucket: targetBucket,
          Key: sourceObj.Key,
          CopySource: encodeURIComponent(`${sourceBucket}/${sourceObj.Key}`),
          // 可选:保留源文件的元数据、ACL等
          MetadataDirective: 'COPY',
          ACL: 'private'
        }).promise()
      );
    }
    // 从目标map中删除已匹配的key,剩下的就是源桶不存在的文件
    targetObjMap.delete(sourceObj.Key);
  });

  console.log(`需要复制的文件数量: ${copyTasks.length}`);
  // 批量执行复制任务,可根据需要调整并发数,避免触发限流
  await Promise.all(copyTasks.map(task => task.catch(err => console.error('复制失败:', err))));

  // 如果开启删除逻辑,删除目标桶多余的文件
  if (enableDelete && targetObjMap.size > 0) {
    console.log(`需要删除的目标桶多余文件数量: ${targetObjMap.size}`);
    const deleteParams = {
      Bucket: targetBucket,
      Delete: {
        Objects: Array.from(targetObjMap.keys()).map(key => ({ Key: key }))
      }
    };
    await s3.deleteObjects(deleteParams).promise();
  }

  console.log('S3桶同步完成');
}

// 执行同步
syncS3Buckets().catch(err => {
  console.error('同步失败:', err);
  process.exit(1);
});

3. 功能说明

  • 同时支持全量同步和增量同步:首次运行会全量拷贝所有文件,后续运行仅同步有变更的文件,减少不必要的资源消耗
  • 自动处理对象列表分页:即使桶内文件超过1000条也不会漏同步
  • 可配置删除逻辑:按需决定是否清理目标桶中源桶已删除的文件
  • 兼容跨区域、跨账号同步:仅需要给对应AK授予对应桶的读写权限即可

4. Cron定时调度配置

执行crontab -e编辑定时任务,添加如下配置即可实现每天凌晨2点执行同步,同时输出日志到指定路径:

0 2 * * * /usr/bin/node /data/scripts/s3-sync.js >> /var/log/s3-sync.log 2>&1

注意替换成你服务器上实际的node路径和脚本存放路径。

注意事项

  • 超过5GB的大文件需要改用multipart分片拷贝接口,避免单次拷贝超时,可根据实际场景扩展上述代码
  • 桶内文件数量超过10万的话,建议加并发控制逻辑,避免触发S3的API限流阈值
  • 不要在代码中硬编码AK/SK,优先使用IAM角色、本地凭证文件、环境变量等方式注入凭证,避免泄露风险

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 06:06:02