Reputation: 2532
A multi-part upload to S3 is divided in 3 steps:
Using AWS SDK for JavaScript v3 (exactly v3.22), the first two steps are successful:
UploadId
comes from the initialisation.ETag
needed to complete the
upload.The issue comes requesting the upload completion, that is done with:
const completeParams: CompleteMultipartUploadCommandInput = {
Bucket,
Key,
UploadId,
MultipartUpload: { Parts },
};
return client.send(new CompleteMultipartUploadCommand(completeParams));
Where Parts
is an array of valid CompletedPart
objects sorted by PartNumber
.
Analysing the network call, the request is done to
https://{Bucket}.s3.{Location}.scw.cloud/{Key}?uploadId={UploadId}&x-id=CompleteMultipartUpload
Note: replaced sensible data with placeholders, but they are the expected values.
And the body generated by the AWS SDK is:
<?xml version="1.0" encoding="UTF-8"?>
<CompletedMultipartUpload xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Part>
<ETag>"610c4..."</ETag>
<PartNumber>1</PartNumber>
</Part>
<Part>
<ETag>"2edb4..."</ETag>
<PartNumber>2</PartNumber>
</Part>
</CompletedMultipartUpload>
Note: showing just the first 5 chars of each ETag to show that they are different and came from uploading parts.
But the answer from S3 is:
The XML you provided was not well-formed or did not validate against our published schema.
Reading the extensive documentation, there is a subtle difference: the root element of the XML should be CompleteMultipartUpload
instead of CompletedMultipartUpload
, but the XML is generated by the AWS SDK and I would expect it to be right.
What could be wrong?
Upvotes: 2
Views: 2623
Reputation: 2532
This is an open issue at aws-sdk-js-v3 official repository.
The error is, indeed, a typo on a XML element. It must be CompleteMultipartUpload
instead of CompletedMultipartUpload
(extra d).
Using a middleware that replaces the invalid XML tag, like this:
const patchS3CompleteMultipartUpload = (client: S3Client): void => {
client.middlewareStack.add(
(next, _context) => (args: any) => {
if ('string' === typeof args.request?.body && args.request.body.includes('CompletedMultipartUpload')) {
args.request.body = args.request.body.replace(/CompletedMultipartUpload/g, 'CompleteMultipartUpload');
}
return next(args);
},
{
step: 'build',
priority: 'high',
}
);
};
Upvotes: 3