Copying an object using multipart upload
Multipart upload allows you to copy objects as a set of parts. The examples in this
section show you how to copy objects greater than 5 GB using the multipart upload API. For
information about multipart uploads, see Uploading and copying objects using multipart upload.
You can copy objects less than 5 GB in a single operation without using the multipart
upload API. You can copy objects less than 5 GB using the AWS Management Console, AWS CLI, REST API, or
AWS SDKs. For more information, see Copying, moving, and renaming objects.
For an end-to-end procedure on uploading an object with multipart upload with an additional checksum, see
Tutorial: Upload an object through multipart upload and
verify its data integrity.
The following section show how to copy an object with multipart upload with the REST API or AWS SDKs.
The following sections in the Amazon Simple Storage Service API Reference describe the
REST API for multipart upload. For copying an existing object, use the Upload Part
(Copy) API and specify the source object by adding the
x-amz-copy-source
request header in your request.
You can use these APIs to make your own REST requests, or you can use one of the
SDKs we provide. For more information about using Multipart Upload with the AWS CLI,
see Using the AWS CLI. For more
information about the SDKs, see AWS SDK support for multipart upload.
To copy an object using the low-level API, do the following:
-
Initiate a multipart upload by calling the
AmazonS3Client.initiateMultipartUpload()
method.
-
Save the upload ID from the response object that the
AmazonS3Client.initiateMultipartUpload()
method returns.
You provide this upload ID for each part-upload operation.
-
Copy all of the parts. For each part that you need to copy, create a new
instance of the CopyPartRequest
class. Provide the part
information, including the source and destination bucket names, source and
destination object keys, upload ID, locations of the first and last bytes of
the part, and part number.
-
Save the responses of the AmazonS3Client.copyPart()
method
calls. Each response includes the ETag
value and part number
for the uploaded part. You need this information to complete the multipart
upload.
-
Call the AmazonS3Client.completeMultipartUpload()
method to
complete the copy operation.
- Java
-
The following example shows how to use the Amazon S3 low-level Java API
to perform a multipart copy. For instructions on creating and
testing a working sample, see Getting Started in the AWS SDK for Java Developer
Guide.
import com.amazonaws.AmazonServiceException;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class LowLevelMultipartCopy {
public static void main(String[] args) throws IOException {
Regions clientRegion = Regions.DEFAULT_REGION;
String sourceBucketName = "*** Source bucket name ***";
String sourceObjectKey = "*** Source object key ***";
String destBucketName = "*** Target bucket name ***";
String destObjectKey = "*** Target object key ***";
try {
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new ProfileCredentialsProvider())
.withRegion(clientRegion)
.build();
// Initiate the multipart upload.
InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(destBucketName,
destObjectKey);
InitiateMultipartUploadResult initResult = s3Client.initiateMultipartUpload(initRequest);
// Get the object size to track the end of the copy operation.
GetObjectMetadataRequest metadataRequest = new GetObjectMetadataRequest(sourceBucketName, sourceObjectKey);
ObjectMetadata metadataResult = s3Client.getObjectMetadata(metadataRequest);
long objectSize = metadataResult.getContentLength();
// Copy the object using 5 MB parts.
long partSize = 5 * 1024 * 1024;
long bytePosition = 0;
int partNum = 1;
List<CopyPartResult> copyResponses = new ArrayList<CopyPartResult>();
while (bytePosition < objectSize) {
// The last part might be smaller than partSize, so check to make sure
// that lastByte isn't beyond the end of the object.
long lastByte = Math.min(bytePosition + partSize - 1, objectSize - 1);
// Copy this part.
CopyPartRequest copyRequest = new CopyPartRequest()
.withSourceBucketName(sourceBucketName)
.withSourceKey(sourceObjectKey)
.withDestinationBucketName(destBucketName)
.withDestinationKey(destObjectKey)
.withUploadId(initResult.getUploadId())
.withFirstByte(bytePosition)
.withLastByte(lastByte)
.withPartNumber(partNum++);
copyResponses.add(s3Client.copyPart(copyRequest));
bytePosition += partSize;
}
// Complete the upload request to concatenate all uploaded parts and make the
// copied object available.
CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(
destBucketName,
destObjectKey,
initResult.getUploadId(),
getETags(copyResponses));
s3Client.completeMultipartUpload(completeRequest);
System.out.println("Multipart copy complete.");
} catch (AmazonServiceException e) {
// The call was transmitted successfully, but Amazon S3 couldn't process
// it, so it returned an error response.
e.printStackTrace();
} catch (SdkClientException e) {
// Amazon S3 couldn't be contacted for a response, or the client
// couldn't parse the response from Amazon S3.
e.printStackTrace();
}
}
// This is a helper function to construct a list of ETags.
private static List<PartETag> getETags(List<CopyPartResult> responses) {
List<PartETag> etags = new ArrayList<PartETag>();
for (CopyPartResult response : responses) {
etags.add(new PartETag(response.getPartNumber(), response.getETag()));
}
return etags;
}
}
- .NET
-
The following C# example shows how to use the AWS SDK for .NET to copy an Amazon S3
object that is larger than 5 GB from one source location to another,
such as from one bucket to another. To copy objects that are smaller
than 5 GB, use the single-operation copy procedure described in Using the AWS SDKs. For more information
about Amazon S3 multipart uploads, see Uploading and copying objects using multipart upload.
This example shows how to copy an Amazon S3 object that is larger than 5 GB
from one S3 bucket to another using the AWS SDK for .NET multipart upload
API.
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Amazon.DocSamples.S3
{
class CopyObjectUsingMPUapiTest
{
private const string sourceBucket = "*** provide the name of the bucket with source object ***";
private const string targetBucket = "*** provide the name of the bucket to copy the object to ***";
private const string sourceObjectKey = "*** provide the name of object to copy ***";
private const string targetObjectKey = "*** provide the name of the object copy ***";
// Specify your bucket region (an example region is shown).
private static readonly RegionEndpoint bucketRegion = RegionEndpoint.USWest2;
private static IAmazonS3 s3Client;
public static void Main()
{
s3Client = new AmazonS3Client(bucketRegion);
Console.WriteLine("Copying an object");
MPUCopyObjectAsync().Wait();
}
private static async Task MPUCopyObjectAsync()
{
// Create a list to store the upload part responses.
List<UploadPartResponse> uploadResponses = new List<UploadPartResponse>();
List<CopyPartResponse> copyResponses = new List<CopyPartResponse>();
// Setup information required to initiate the multipart upload.
InitiateMultipartUploadRequest initiateRequest =
new InitiateMultipartUploadRequest
{
BucketName = targetBucket,
Key = targetObjectKey
};
// Initiate the upload.
InitiateMultipartUploadResponse initResponse =
await s3Client.InitiateMultipartUploadAsync(initiateRequest);
// Save the upload ID.
String uploadId = initResponse.UploadId;
try
{
// Get the size of the object.
GetObjectMetadataRequest metadataRequest = new GetObjectMetadataRequest
{
BucketName = sourceBucket,
Key = sourceObjectKey
};
GetObjectMetadataResponse metadataResponse =
await s3Client.GetObjectMetadataAsync(metadataRequest);
long objectSize = metadataResponse.ContentLength; // Length in bytes.
// Copy the parts.
long partSize = 5 * (long)Math.Pow(2, 20); // Part size is 5 MB.
long bytePosition = 0;
for (int i = 1; bytePosition < objectSize; i++)
{
CopyPartRequest copyRequest = new CopyPartRequest
{
DestinationBucket = targetBucket,
DestinationKey = targetObjectKey,
SourceBucket = sourceBucket,
SourceKey = sourceObjectKey,
UploadId = uploadId,
FirstByte = bytePosition,
LastByte = bytePosition + partSize - 1 >= objectSize ? objectSize - 1 : bytePosition + partSize - 1,
PartNumber = i
};
copyResponses.Add(await s3Client.CopyPartAsync(copyRequest));
bytePosition += partSize;
}
// Set up to complete the copy.
CompleteMultipartUploadRequest completeRequest =
new CompleteMultipartUploadRequest
{
BucketName = targetBucket,
Key = targetObjectKey,
UploadId = initResponse.UploadId
};
completeRequest.AddPartETags(copyResponses);
// Complete the copy.
CompleteMultipartUploadResponse completeUploadResponse =
await s3Client.CompleteMultipartUploadAsync(completeRequest);
}
catch (AmazonS3Exception e)
{
Console.WriteLine("Error encountered on server. Message:'{0}' when writing an object", e.Message);
}
catch (Exception e)
{
Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message);
}
}
}
}