Multipart upload of objects
Multipart Upload Scenarios
In addition to using the putObject() method for file uploads to BOS, BOS also supports another upload mode called Multipart Upload. This mode can be used in scenarios such as:
- When resumable uploads are required.
- When uploading files larger than 5GB.
- When the connection to the BOS server is frequently interrupted due to unstable network conditions.
- Enable streaming file uploads.
- The file size cannot be determined before uploading.
Multipart Upload Process
Suppose there is a file with the local path /path/to/file.zip. Since the file is large, multipart upload is used to transmit it to BOS.
Basic workflow:
- Start a multipart upload.
- Upload individual parts.
- Finalize the multipart upload.
Initialize Multipart Upload
Use initiateMultipartUpload method to initialize a multipart upload event:
Example code:
1// Initiate Multipart Upload
2InitiateMultipartUploadRequest initiateMultipartUploadRequest =
3 new InitiateMultipartUploadRequest(<BucketName>, <ObjectKey>);
4InitiateMultipartUploadResponse initiateMultipartUploadResponse =
5 client.initiateMultipartUpload(initiateMultipartUploadRequest);
6 // Print UploadId
7System.out.println("UploadId: " + initiateMultipartUploadResponse.getUploadId());
Note: The return result of
initiateMultipartUploadcontainsUploadId, which is the unique identifier for distinguishing multipart upload events, and we will use it in subsequent operations.
Upload parts
Upload the file in segments.
Example code:
1// Set each part to 5MB
2final long partSize = 1024 * 1024 * 5L;
3File partFile = new File("/path/to/file.zip");
4 // Calculate the count of parts
5int partCount = (int) (partFile.length() / partSize);
6if (partFile.length() % partSize != 0) {
7 partCount++;
8}
9 // Create a list to save the ETag and PartNumber of each uploaded part
10List<PartETag> partETags = new ArrayList<PartETag>();
11 // Get file stream
12FileInputStream fis = new FileInputStream(partFile);
13for (int i = 0; i < partCount; i++) {
14 // Calculate the size of each part
15 long skipBytes = partSize * i;
16 long size = partSize < partFile.length() - skipBytes ?
17 partSize : partFile.length() - skipBytes;
18 byte[] buf = new byte[(int)size];
19 int offset = 0;
20 while (true) {
21 int byteRead = fis.read(buf, offset, (int)size);
22 offset += byteRead;
23 if (byteRead < 0 || offset >= size) {
24 break;
25 }
26 }
27 ByteArrayInputStream bufStream = new ByteArrayInputStream(buf);
28 // Create UploadPartRequest to upload parts
29 UploadPartRequest uploadPartRequest = new UploadPartRequest();
30 uploadPartRequest.setBucketName(bucketName);
31 uploadPartRequest.setKey(objectkey);
32 uploadPartRequest.setUploadId(initiateMultipartUploadResponse.getUploadId());
33 uploadPartRequest.setInputStream(bufStream);
34 uploadPartRequest.setPartSize(size);
35 uploadPartRequest.setPartNumber(i + 1);
36 // Upload progress callback
37 uploadPartRequest.setProgressCallback(new BosProgressCallback<UploadPartRequest>() {
38 @Override
39 public void onProgress(UploadPartRequest request, long currentSize, long totalSize) {
40 Log.e(currentSize + "", totalSize + "");
41 }
42 });
43 UploadPartResponse uploadPartResponse = client.uploadPart(uploadPartRequest);
44 // Save the returned PartETag to the List.
45 partETags.add(uploadPartResponse.getPartETag());
46 System.out.println(uploadPartResponse.getPartETag());
47 }
48 // Close the file
49 fis.close();
Note: The core of the above code is to call the
UploadPartmethod to upload each part concurrently, but the following points should be noted:
- The UploadPart method requires the size of each part to either be an integer multiple of 1MB or greater than 5MB. However, the Upload Part interface does not validate the part size immediately; this check happens only during the finalization of the multipart upload.
- To ensure no errors during network transmission, it is recommended to use the Content-MD5 value returned by BOS for each part after
UploadPartto verify the correctness of the uploaded part data. When all part data is combined into one Object, it no longer contains the MD5 value.- The part number must be within the range of 1 to 10,000. If this limit is exceeded, BOS will return an InvalidArgument error code.
- For each uploaded part, the stream must be positioned at the beginning of the respective part.
- After each Part upload, the return result of BOS will include a
PartETagobject, which is a combination of the uploaded block's ETag and block number (PartNumber). It will be used in subsequent steps to complete the multipart upload, so it must be saved. Generally speaking, thesePartETagobjects will be saved in a List.- For details on using the progress callback API, refer to the chapter titled "Obtain Upload Progress."
Complete multipart upload
Example code
1CompleteMultipartUploadRequest completeMultipartUploadRequest =
2 new CompleteMultipartUploadRequest(<BucketName>, <ObjectKey>,
3 initiateMultipartUploadResponse.getUploadId(), partETags);
4 // Complete multipart upload
5CompleteMultipartUploadResponse completeMultipartUploadResponse =
6 client.completeMultipartUpload(completeMultipartUploadRequest);
7 // Print Object's ETag
8System.out.println(completeMultipartUploadResponse.getETag());
Note: The
partETagsin the above code is the list of partETag saved in the second step. After BOS receives the Part list submitted by the user, it will verify the validity of each data Part one by one. Once all data parts are validated, BOS will assemble the data parts into a complete Object.
Complete example
1import java.io.File;
2import java.io.FileInputStream;
3import java.io.IOException;
4import java.util.ArrayList;
5import java.util.List;
6import org.json.JSONException;
7import android.app.Activity;
8import android.os.Bundle;
9import com.baidubce.BceClientException;
10import com.baidubce.BceServiceException;
11import com.baidubce.auth.DefaultBceCredentials;
12import com.baidubce.demo.R;
13import com.baidubce.services.bos.BosClient;
14import com.baidubce.services.bos.BosClientConfiguration;
15import com.baidubce.services.bos.model.CompleteMultipartUploadRequest;
16import com.baidubce.services.bos.model.CompleteMultipartUploadResponse;
17import com.baidubce.services.bos.model.InitiateMultipartUploadRequest;
18import com.baidubce.services.bos.model.InitiateMultipartUploadResponse;
19import com.baidubce.services.bos.model.PartETag;
20import com.baidubce.services.bos.model.UploadPartRequest;
21import com.baidubce.services.bos.model.UploadPartResponse;
22public class ExampleActivity extends Activity {
23private String bucketName = <BucketName>;
24private String objectKey = <ObjectKey>;
25
26@Override
27protected void onCreate(Bundle savedInstanceState) {
28 super.onCreate(savedInstanceState);
29 setContentView(R.layout.activity_main);
30 new Thread(new Runnable() {
31 @Override
32 public void run() {
33 try {
34 BosClientConfiguration config = new BosClientConfiguration();
35 config.setCredentials(new DefaultBceCredentials(<AccessKeyID>, <SecretAccessKey>));
36 config.setEndpoint(<EndPoint>);
37 BosClient client = new BosClient(config);
38 // Initiate Multipart Upload
39 InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(<BucketName>, <ObjectKey>);
40 InitiateMultipartUploadResponse initiateMultipartUploadResponse =
41 client.initiateMultipartUpload(initiateMultipartUploadRequest);
42 // Print UploadId
43 System.out.println("UploadId: " + initiateMultipartUploadResponse.getUploadId());
44 // Set each part to 5MB
45 final long partSize = 1024 * 1024 * 5L;
46 File partFile = new File("/path/to/file.zip");
47 // Calculate the count of parts
48 int partCount = (int) (partFile.length() / partSize);
49 if (partFile.length() % partSize != 0) {
50 partCount++;
51 }
52 // Create a list to save the ETag and PartNumber of each uploaded part
53 List<PartETag> partETags = new ArrayList<PartETag>();
54 // Get file stream
55 FileInputStream fis = new FileInputStream(partFile);
56 for (int i = 0; i < partCount; i++) {
57 // Calculate the size of each part
58 long skipBytes = partSize * i;
59 long size = partSize < partFile.length() - skipBytes ?
60 partSize : partFile.length() - skipBytes;
61 byte[] buf = new byte[(int)size];
62 int offset = 0;
63 while (true) {
64 int byteRead = fis.read(buf, offset, (int)size);
65 offset += byteRead;
66 if (byteRead < 0 || offset >= size) {
67 break;
68 }
69 }
70 ByteArrayInputStream bufStream = new ByteArrayInputStream(buf);
71 // Create UploadPartRequest to upload parts
72 UploadPartRequest uploadPartRequest = new UploadPartRequest();
73 uploadPartRequest.setBucketName(<BucketName>);
74 uploadPartRequest.setKey(<ObjectKey>);
75 uploadPartRequest.setUploadId(initiateMultipartUploadResponse.getUploadId());
76 uploadPartRequest.setInputStream(fis);
77 uploadPartRequest.setPartSize(size);
78 uploadPartRequest.setPartNumber(i + 1);
79 UploadPartResponse uploadPartResponse = client.uploadPart(uploadPartRequest);
80 // Save the returned PartETag to the List.
81 partETags.add(uploadPartResponse.getPartETag());
82 System.out.println(uploadPartResponse.getPartETag());
83 }
84 // Close the file
85 fis.close();
86 CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(<BucketName>, <ObjectKey>, initiateMultipartUploadResponse.getUploadId(), partETags);
87 // Complete multipart upload
88 CompleteMultipartUploadResponse completeMultipartUploadResponse =
89 client.completeMultipartUpload(completeMultipartUploadRequest);
90 // Print Object's ETag
91 System.out.println(completeMultipartUploadResponse.getETag());
92 } catch (BceServiceException e) {
93 System.out.println("Error ErrorCode: " + e.getErrorCode());
94 System.out.println("Error RequestId: " + e.getRequestId());
95 System.out.println("Error StatusCode: " + e.getStatusCode());
96 System.out.println("Error Message: " + e.getMessage());
97 System.out.println("Error ErrorType: " + e.getErrorType());
98 } catch (BceClientException e) {
99 System.out.println("Error Message: " + e.getMessage());
100 } catch (IOException e) {
101 // TODO Auto-generated catch block
102 e.printStackTrace();
103 } catch (JSONException e) {
104 // TODO Auto-generated catch block
105 e.printStackTrace();
106 }
107 }
108 }).start();
109}}
Cancel multipart upload
Users can cancel multipart uploads by using the abortMultipartUpload method.
Example code:
1AbortMultipartUploadRequest abortMultipartUploadRequest =
2 new AbortMultipartUploadRequest(<BucketName>, <ObjectKey>, <UploadId>);
3 // Cancel multipart upload
4client.abortMultipartUpload(abortMultipartUploadRequest);
Retrieve unfinished multipart uploads
Users can obtain the unfinished multipart upload events in the bucket by the listMultipartUploads method.
Basic workflow
- Create an instance of the listMultipartUploadsRequest class, and pass in the parameter
<BucketName>. - Create a BOSClient instance and invoke the BOSClient.listMultipartUploads() method.
- The listMultipartUploads() method provides details about all uploaded parts.
Example code
1ListMultipartUploadsRequest listMultipartUploadsRequest =
2 new ListMultipartUploadsRequest(<BucketName>);
3 // Retrieve all upload events within the bucket
4ListMultipartUploadsResponse list = client.listMultipartUploads(listMultipartUploadsRequest);
5
6 // Traverse all upload events
7for (MultipartUploadSummary multipartUpload : list.getMultipartUploads()) {
8 System.out.println("Key: " + multipartUpload.getKey() + " UploadId: " + multipartUpload.getUploadId());
9}
Note:
- By default, if the number of multipart upload events in a bucket surpasses 1,000, only 1,000 records will be returned. In such cases, the IsTruncated value in the response will be True, and the NextKeyMarker will indicate the starting point for the next query.
- To fetch more multipart upload events, use the KeyMarker parameter to retrieve data in batches.
Complete example
1import android.app.Activity;
2import android.os.Bundle;
3import com.baidubce.BceClientException;
4import com.baidubce.BceServiceException;
5import com.baidubce.auth.DefaultBceCredentials;
6import com.baidubce.demo.R;
7import com.baidubce.services.bos.BosClient;
8import com.baidubce.services.bos.BosClientConfiguration;
9import com.baidubce.services.bos.model.ListMultipartUploadsRequest;
10import com.baidubce.services.bos.model.ListMultipartUploadsResponse;
11import com.baidubce.services.bos.model.MultipartUploadSummary;
12public class ExampleActivity extends Activity {
13private String bucketName = <BucketName>;
14@Override
15protected void onCreate(Bundle savedInstanceState) {
16 super.onCreate(savedInstanceState);
17 setContentView(R.layout.activity_main);
18 new Thread(new Runnable() {
19 @Override
20 public void run() {
21 try {
22 BosClientConfiguration config = new BosClientConfiguration();
23 config.setCredentials(new DefaultBceCredentials(<AccessKeyID>, <SecretAccessKey>));
24 config.setEndpoint(<EndPoint>);
25 BosClient client = new BosClient(config);
26 ListMultipartUploadsRequest listMultipartUploadsRequest =
27 new ListMultipartUploadsRequest(<BucketName>);
28 // Retrieve all upload events within the bucket
29 ListMultipartUploadsResponse listing = client.listMultipartUploads(listMultipartUploadsRequest);
30 // Traverse all upload events
31 for (MultipartUploadSummary multipartUpload : listing.getMultipartUploads()) {
32 System.out.println("Key: " + multipartUpload.getKey() + " UploadId: " + multipartUpload.getUploadId());
33 }
34 } catch (BceServiceException e) {
35 System.out.println("Error ErrorCode: " + e.getErrorCode());
36 System.out.println("Error RequestId: " + e.getRequestId());
37 System.out.println("Error StatusCode: " + e.getStatusCode());
38 System.out.println("Error ErrorType: " + e.getErrorType());
39 System.out.println("Error Message: " + e.getMessage());
40 } catch (BceClientException e) {
41 System.out.println("Error Message: " + e.getMessage());
42 }
43 }
44 }).start();
45}}
Get all uploaded part information
Users can obtain all uploaded parts in an upload event by the listParts method.
Basic workflow
- Create an instance of the ListPartsRequest class, and pass in the parameters
<BucketName>,<ObjectKey>and<UploadId> - Create a BOSClient instance and invoke the BOSClient.listParts() method.
- The listParts() method provides details about all uploaded parts.
Example code
1ListPartsRequest listPartsRequest = new ListPartsRequest(<BucketName>, <ObjectKey>, <UploadId>);
2
3 // Retrieve all uploaded part information
4ListPartsResponse partListing = client.listParts(listPartsRequest);
5
6 // Traverse all parts
7for (PartSummary part : partListing.getParts()) {
8 System.out.println("PartNumber: " + part.getPartNumber() + " ETag: " + part.getETag());
9}
Note:
- By default, if the number of multipart upload events in a bucket surpasses 1,000, only 1,000 records will be returned. In such cases, the IsTruncated value in the response will be True, and the NextPartNumberMarker will indicate the starting point for the next query.
- To retrieve more information about uploaded parts, use the PartNumberMarker parameter to fetch data in batches.
Complete example
1import android.app.Activity;
2import android.os.Bundle;
3import com.baidubce.BceClientException;
4import com.baidubce.BceServiceException;
5import com.baidubce.auth.DefaultBceCredentials;
6import com.baidubce.demo.R;
7import com.baidubce.services.bos.BosClient;
8import com.baidubce.services.bos.BosClientConfiguration;
9import com.baidubce.services.bos.model.ListPartsRequest;
10import com.baidubce.services.bos.model.ListPartsResponse;
11import com.baidubce.services.bos.model.PartSummary;
12public class ExampleActivity extends Activity {
13private String bucketName = <BucketName>;
14private String objectKey = <ObjectKey>;
15private String uploadId = <UploadId>;
16@Override
17protected void onCreate(Bundle savedInstanceState) {
18 super.onCreate(savedInstanceState);
19 setContentView(R.layout.activity_main);
20 new Thread(new Runnable() {
21 @Override
22 public void run() {
23 try {
24 BosClientConfiguration config = new BosClientConfiguration();
25 config.setCredentials(new DefaultBceCredentials(<AccessKeyID>, <SecretAccessKey>));
26 config.setEndpoint(<EndPoint>);
27 BosClient client = new BosClient(config);
28 ListPartsRequest listPartsRequest = new ListPartsRequest(<BucketName>, <ObjectKey>, <UploadId>);
29 listPartsRequest.setMaxParts(100);
30 listPartsRequest.setPartNumberMarker(50);
31 // Retrieve all uploaded part information
32 ListPartsResponse partListing = client.listParts(listPartsRequest);
33 // Traverse all parts
34 for (PartSummary part : partListing.getParts()) {
35 System.out.println("PartNumber: " + part.getPartNumber() + " ETag: " + part.getETag());
36 }
37 } catch (BceServiceException e) {
38 System.out.println("Error ErrorCode: " + e.getErrorCode());
39 System.out.println("Error RequestId: " + e.getRequestId());
40 System.out.println("Error StatusCode: " + e.getStatusCode());
41 System.out.println("Error ErrorType: " + e.getErrorType());
42 System.out.println("Error Message: " + e.getMessage());
43 } catch (BceClientException e) {
44 System.out.println("Error Message: " + e.getMessage());
45 }
46 }
47 }).start();
48}}
Encapsulate multipart upload
In the Android SDK, BOS provides the putSuperObjectFromFile interface, which consolidates the three main methods for multipart uploads: initiateMultipartUpload, UploadPart, and completeMultipartUpload. Using this interface, users can complete a multipart upload with built-in support for progress synchronization callbacks.
-
Simple example:
Java1File file = new File("/path/to/file.zip"); 2PutSuperObjectRequest request = new PutSuperObjectRequest(bucketName, objectKey, file); 3bosClient.putSuperObjectFromFile(request); -
Example: Set the part size to 2MB, the thread count to 4, and synchronize progress callbacks every 1,024 bytes
Java1BosClientConfiguration config=new BosClientConfiguration(); 2config.setUploadSegmentPart(1024); 3File file = new File("/path/to/file.zip"); 4PutSuperObjectRequest request = new PutSuperObjectRequest(bucketName, objectKey, 5 file, 1024 * 1024 * 2L, 4); 6request.setProgressCallback(new BosProgressCallback<PutSuperObjectRequest>() { 7 @Override 8 public void onProgress(PutSuperObjectRequest request, long currentSize, long totalSize) { 9 Log.e(currentSize + "", totalSize + ""); 10 } 11}); 12bosClient.putSuperObjectFromFile(request);
Note:
- By default, the part size is 5MB, the thread count is 5, and the progress callback period is every 2,048 bytes.
- If a large file takes a long time to upload and the user wants to end the multipart upload, they can call the cancel() method in PutSuperObjectRequest.
