如何使用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
相关产品推荐
相关产品推荐

