如何使用适用于 Node.js 的 AWS 开发工具包将 Amazon S3 中的所有对象从一个前缀复制/移动到另一个前
How to copy/move all objects in Amazon S3 from one prefix to other using the AWS SDK for Node.js
如何将所有对象从一个前缀复制到另一个前缀?我已经尝试了所有可能的方法将一个镜头中的所有对象从一个前缀复制到另一个前缀,但似乎唯一有效的方法是循环访问对象列表并逐个复制它们。这真的很低效。如果一个文件夹中有数百个文件,我是否必须进行 100 次调用?
var params = {
Bucket: bucket,
CopySource: bucket+'/'+oldDirName+'/filename.txt',
Key: newDirName+'/filename.txt',
};
s3.copyObject(params, function(err, data) {
if (err) {
callback.apply(this, [{
type: "error",
message: "Error while renaming Directory",
data: err
}]);
} else {
callback.apply(this, [{
type: "success",
message: "Directory renamed successfully",
data: data
}]);
}
});
创建一个AWS.S3.listObjects()
来列出具有特定前缀的对象。但您是正确的,因为您需要对要从一个存储桶/前缀复制到相同或另一个存储桶/前缀的每个对象进行一次调用。
您还可以使用实用程序库(如异步)来管理您的请求。
var AWS = require('aws-sdk');
var async = require('async');
var bucketName = 'foo';
var oldPrefix = 'abc/';
var newPrefix = 'xyz/';
var s3 = new AWS.S3({params: {Bucket: bucketName}, region: 'us-west-2'});
var done = function(err, data) {
if (err) console.log(err);
else console.log(data);
};
s3.listObjects({Prefix: oldPrefix}, function(err, data) {
if (data.Contents.length) {
async.each(data.Contents, function(file, cb) {
var params = {
Bucket: bucketName,
CopySource: bucketName + '/' + file.Key,
Key: file.Key.replace(oldPrefix, newPrefix)
};
s3.copyObject(params, function(copyErr, copyData){
if (copyErr) {
console.log(copyErr);
}
else {
console.log('Copied: ', params.Key);
cb();
}
});
}, done);
}
});
希望这有帮助!
下面是一个以"异步等待"方式执行此操作的代码片段:
const AWS = require('aws-sdk');
AWS.config.update({
credentials: new AWS.Credentials(....), // credential parameters
});
AWS.config.setPromisesDependency(require('bluebird'));
const s3 = new AWS.S3();
... ...
const bucketName = 'bucketName'; // example bucket
const folderToMove = 'folderToMove/'; // old folder name
const destinationFolder = 'destinationFolder/'; // new destination folder
try {
const listObjectsResponse = await s3.listObjects({
Bucket: bucketName,
Prefix: folderToMove,
Delimiter: '/',
}).promise();
const folderContentInfo = listObjectsResponse.Contents;
const folderPrefix = listObjectsResponse.Prefix;
await Promise.all(
folderContentInfo.map(async (fileInfo) => {
await s3.copyObject({
Bucket: bucketName,
CopySource: `${bucketName}/${fileInfo.Key}`, // old file Key
Key: `${destinationFolder}/${fileInfo.Key.replace(folderPrefix, '')}`, // new file Key
}).promise();
await s3.deleteObject({
Bucket: bucketName,
Key: fileInfo.Key,
}).promise();
})
);
} catch (err) {
console.error(err); // error handling
}
以上都不处理大型目录,因为 list-objects-v2
命令一次返回的结果不超过 1000 个,提供访问其他"页面"的继续标记。
下面是使用新式 v3 SDK 的解决方案:
const copyAll = async ({
s3Client,
sourceBucket,
targetBucket = sourceBucket,
sourcePrefix,
targetPrefix,
concurrency = 1,
deleteSource = false,
}) => {
let ContinuationToken;
const copyFile = async (sourceKey) => {
const targetKey = sourceKey.replace(sourcePrefix, targetPrefix);
await s3Client.send(
new CopyObjectCommand({
Bucket: targetBucket,
Key: targetKey,
CopySource: `${sourceBucket}/${sourceKey}`,
}),
);
if (deleteSource) {
await s3Client.send(
new DeleteObjectCommand({
Bucket: sourceBucket,
Key: sourceKey,
}),
);
}
};
do {
const { Contents = [], NextContinuationToken } = await s3Client.send(
new ListObjectsV2Command({
Bucket: sourceBucket,
Prefix: sourcePrefix,
ContinuationToken,
}),
);
const sourceKeys = Contents.map(({ Key }) => Key);
await Promise.all(
new Array(concurrency).fill(null).map(async () => {
while (sourceKeys.length) {
await copyFile(sourceKeys.pop());
}
}),
);
ContinuationToken = NextContinuationToken;
} while (ContinuationToken);
};
如果Promise.all
部分不清楚,它只是一个穷人的"线程池",允许您同时复制多个文件,这可以大大加快速度。这些不使用任何带宽,因为内容是在 AWS 中复制的,因此我对 20 或更高的值没有任何问题 concurrency
.为清楚起见,它只是以下各项的并行化版本:
const sourceKeys = Contents.map(({ Key }) => Key);
while (sourceKeys.length) {
await copyFile(sourceKeys.pop());
}
对 Aditya Manohar 代码的一个小更改,它改进了 s3.copyObject 函数中的错误处理,并且实际上将在执行复制请求后通过删除源文件来完成"移动"请求:
const AWS = require('aws-sdk');
const async = require('async');
const bucketName = 'foo';
const oldPrefix = 'abc/';
const newPrefix = 'xyz/';
const s3 = new AWS.S3({
params: {
Bucket: bucketName
},
region: 'us-west-2'
});
// 1) List all the objects in the source "directory"
s3.listObjects({
Prefix: oldPrefix
}, function (err, data) {
if (data.Contents.length) {
// Build up the paramters for the delete statement
let paramsS3Delete = {
Bucket: bucketName,
Delete: {
Objects: []
}
};
// Expand the array with all the keys that we have found in the ListObjects function call, so that we can remove all the keys at once after we have copied all the keys
data.Contents.forEach(function (content) {
paramsS3Delete.Delete.Objects.push({
Key: content.Key
});
});
// 2) Copy all the source files to the destination
async.each(data.Contents, function (file, cb) {
var params = {
CopySource: bucketName + '/' + file.Key,
Key: file.Key.replace(oldPrefix, newPrefix)
};
s3.copyObject(params, function (copyErr, copyData) {
if (copyErr) {
console.log(err);
} else {
console.log('Copied: ', params.Key);
}
cb();
});
}, function (asyncError, asyncData) {
// All the requests for the file copy have finished
if (asyncError) {
return console.log(asyncError);
} else {
console.log(asyncData);
// 3) Now remove the source files - that way we effectively moved all the content
s3.deleteObjects(paramsS3Delete, (deleteError, deleteData) => {
if (deleteError) return console.log(deleteError);
return console.log(deleteData);
})
}
});
}
});
请注意,我已经将cb()
回调函数移到了 if-then-else 循环之外。这样,即使发生错误,异步模块也会触发done()
函数。
归复制文件夹的原始代码进行了更多更新。一些限制是代码不会处理每个前缀超过 1000 个对象,当然,如果您的文件夹非常深,则深度限制。
import AWS from 'aws-sdk';
AWS.config.update({ region: 'ap-southeast-1' });
/**
* Copy s3 folder
* @param {string} bucket Params for the first argument
* @param {string} source for the 2nd argument
* @param {string} dest for the 2nd argument
* @returns {promise} the get object promise
*/
export default async function s3CopyFolder(bucket, source, dest) {
// sanity check: source and dest must end with '/'
if (!source.endsWith('/') || !dest.endsWith('/')) {
return Promise.reject(new Error('source or dest must ends with fwd slash'));
}
const s3 = new AWS.S3();
// plan, list through the source, if got continuation token, recursive
const listResponse = await s3.listObjectsV2({
Bucket: bucket,
Prefix: source,
Delimiter: '/',
}).promise();
// copy objects
await Promise.all(
listResponse.Contents.map(async (file) => {
await s3.copyObject({
Bucket: bucket,
CopySource: `${bucket}/${file.Key}`,
Key: `${dest}${file.Key.replace(listResponse.Prefix, '')}`,
}).promise();
}),
);
// recursive copy sub-folders
await Promise.all(
listResponse.CommonPrefixes.map(async (folder) => {
await s3CopyFolder(
bucket,
`${folder.Prefix}`,
`${dest}${folder.Prefix.replace(listResponse.Prefix, '')}`,
);
}),
);
return Promise.resolve('ok');
}
这是我用来移动多个对象的方法。
const asyncForEach = async (array, callback) => {
for (let i = 0; i < array.length; i++) {
await callback(array[i], i, array)
}
}
const awsMove = async ({ files }) => {
try {
const s3 = new aws.S3()
const AWS_BUCKET = 'bucket'
await asyncForEach(files, async file => {
const copyParams = {
Key: file.newPath,
ACL: 'public-read',
Bucket: AWS_BUCKET,
CopySource: encodeURI(`/${AWS_BUCKET}/${file.oldPath}`)
}
await s3.copyObject(copyParams).promise()
const deleteParams = {
Key: file.oldPath,
Bucket: AWS_BUCKET
}
await s3.deleteObject(deleteParams).promise()
})
} catch (err) {
console.log(err)
}
}
const files = [
{ oldPath: 'folder/file', newPath: 'folder-copy/file' },
{ oldPath: 'another-folder/file', newPath: 'another-folder-copy/file' }
]
await awsMove({ files })
- 创建一个类似链接的按钮,并通过Javascript函数打开一个新的弹出窗口
- jQuery:循环一个具有不同超时值的循环
- 当包含另一个asp文件时,是否也包含所有引用的样式和脚本页面
- 从javascript创建一个列表
- 节点导出返回一个空对象
- 使用clickToggle并在单击另一个元素时关闭元素
- 我可以在json对象中添加一个函数吗
- 在jquery中生成一个自动前缀编号
- URL.createObjectURL返回一个前缀为null的blob.例如:Blob:null/12415-63
- 现代化与仅为CSS中的前缀提供回退.哪一个,为什么
- 我需要javascript自动前缀+后缀,因为它填充一个字段?我想用撇号把它括起来
- 我需要javascript前缀+后缀,因为它是填充一个字段?我想用撇号把它括起来
- Modernizr.前缀(不是一个函数)在Magento
- 当添加一个新的html元素(如iframe等)时,绝对url将被覆盖为相对url(即在url提供的前缀处添加base地址
- 我如何给一个号码加前缀和加零?
- 一个消除CSS中供应商前缀的脚本
- 如何创建一个jquery函数来影响id的前缀和后缀
- 是否可以在Javascript中创建一个带有前缀的XML节点
- 如何使用适用于 Node.js 的 AWS 开发工具包将 Amazon S3 中的所有对象从一个前缀复制/移动到另一个前
- Textarea ng-bind,其中value有一个前缀和后缀