Объекты размером более 5 ГБ являются поддерживается с загрузкой нескольких частей. У нас уже есть файлы размером 5 ГБ + в одной корзине, которые я хотел бы переместить в другую под той же учетной записью AWS. Когда я запускаю команду с помощью s3cmd:
s3cmd mv s3://BUCKET1/OBJECT1 s3://BUCKET2[/OBJECT2]
Я возвращаюсь с ошибкой:
ERROR: S3 error: 400 (InvalidRequest): The specified copy source is larger than the maximum allowable size for a copy source: 5368709120
Если это вообще возможно, я предполагаю, что его нельзя будет перенести без дополнительной полосы пропускания / затрат. Тем не менее, я пытаюсь понять, можно ли перемещать большие файлы каким-либо методом, состоящим из нескольких частей.
В настоящее время то, что вы пытаетесь сделать, невозможно сделать за одну операцию. Перемещение в S3cmd в API - это, по сути, копирование и удаление всего в одном, и это ограничение операции копирования.
http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectCOPY.html
В Amazon S3 можно хранить отдельные объекты размером до 5 ТБ. Вы создаете копию своего объекта размером до 5 ГБ за одну атомарную операцию с помощью этого API. Однако для копирования объекта размером более 5 ГБ необходимо использовать составную часть загрузки для загрузки - API копирования.
http://docs.aws.amazon.com/AmazonS3/latest/dev/CopyingObjctsMPUapi.html
Примеры в этом разделе показывают, как копировать объекты размером более 5 ГБ с помощью API многокомпонентной загрузки. Вы можете скопировать объекты размером менее 5 ГБ за одну операцию.
Вот как я добился этого с эффективным использованием памяти с помощью NodeJS.
function copyS3MP(from_bucket, from_key, to_bucket, to_key) {
const AWS = require('aws-sdk');
AWS.config.update({region: 'us-west-2'});
let s3 = new AWS.S3();
let head, uploadId, numParts, fileSize;
let startTime = new Date();
let partNum = 0;
let partSize = 1024 * 1024 * 10; // 10mb chunks except last part
let maxUploadTries = 3;
let multiPartParams = {
Bucket: to_bucket,
Key: to_key,
ContentType: getContentType(to_key)
};
let multipartMap = {
Parts: []
};
function getHead() {
return new Promise(async (resolve, reject) => {
try {
const h = await s3.headObject({
Bucket: from_bucket,
Key: from_key
}).promise();
resolve(h);
} catch (e) {
reject(e);
}
});
}
function createMultipartUpload() {
return new Promise(async (resolve, reject) => {
try {
s3.createMultipartUpload(multiPartParams, function(mpErr, multipart) {
if (mpErr) {
console.error(mpErr);
return reject(mpErr);
}
console.log('Got upload ID', multipart.UploadId);
return resolve(multipart.UploadId);
});
} catch (e) {
reject(e);
}
});
}
function copyPart(start, partNum) {
let tryNum = 1;
function copyLogic(copyParams) {
return new Promise((resolve, reject) => {
s3.uploadPartCopy(copyParams, function(multiErr, mData) {
if (multiErr) {
console.log('Upload part error:', multiErr);
return reject(multiErr);
} else {
multipartMap.Parts[this.request.params.PartNumber - 1] = {
ETag: mData.ETag,
PartNumber: Number(this.request.params.PartNumber)
};
console.log('Completed part', this.request.params.PartNumber);
console.log('mData', mData);
return resolve();
}
}).on('httpUploadProgress', function(progress) { console.log(Math.round(progress.loaded/progress.total*100)+ '% done') });
});
}
return new Promise(async (resolve, reject) => {
let end = Math.min(start + partSize, fileSize);
try {
let partParams = {
Bucket: to_bucket,
Key: to_key,
PartNumber: String(partNum),
UploadId: uploadId,
CopySource: `${from_bucket}/${from_key}`,
CopySourceRange: `bytes=${start}-${end - 1}`
};
while (tryNum <= maxUploadTries) {
try {
await copyLogic(partParams);
return resolve();
} catch (e) {
tryNum++;
if (tryNum <= maxUploadTries) {
console.log('Retrying copy of part: #', partParams.PartNumber);
await module.exports.sleep(1);
} else {
console.log('Failed uploading part: #', partParams.PartNumber);
return reject(e);
}
}
}
resolve();
} catch (e) {
return reject(e);
}
});
}
function completeMultipartUpload() {
return new Promise((resolve, reject) => {
let doneParams = {
Bucket: to_bucket,
Key: to_key,
MultipartUpload: multipartMap,
UploadId: uploadId
};
s3.completeMultipartUpload(doneParams, function(err, data) {
if (err) {
return reject(err);
}
var delta = (new Date() - startTime) / 1000;
console.log('Completed upload in', delta, 'seconds');
console.log('Final upload data:', data);
return resolve();
});
});
}
return new Promise(async (resolve, reject) => {
try {
head = await getHead();
fileSize = head.ContentLength;
} catch (e) {
return reject(e);
}
numParts = Math.ceil(fileSize / partSize);
console.log('Creating multipart upload for:', to_key);
try {
uploadId = await createMultipartUpload();
} catch (e) {
return reject(e);
}
for (let start = 0; start < fileSize; start += partSize) {
partNum++;
console.log("Part Num: " + partNum);
try {
await copyPart(start, partNum);
} catch (e) {
console.error(e);
return reject(e);
}
}
try {
await completeMultipartUpload();
} catch (e) {
return reject(e);
}
resolve();
});
}