By using the multipart upload feature provided by OSS, you can split a large object into multiple parts and upload them separately. After all parts are uploaded, call the CompleteMultipartUpload operation to combine these parts into a single object to implement resumable upload.

Multipart upload process

To implement multipart upload, perform the following operations:

  1. Initiate a multipart upload task.

    You can call the oss.initMultipartUpload method to create a unique upload ID in Object Storage Service (OSS).

  2. Upload the parts.

    You can call the oss.uploadPart method to upload parts.

    Note
    • Part numbers identify the relative positions of parts in an object that share the same upload ID. If you upload a part that has the same part number as an existing part, the existing part is overwritten by the uploaded part.
    • OSS includes the MD5 hash of part data in the ETag header and returns the MD5 hash to the user.
    • OSS calculates the MD5 hash of uploaded data and compares the MD5 hash with the MD5 hash calculated by the SDK. If the two hashes are different, the InvalidDigest error code is returned.
  3. Complete the multipart upload.

    After all parts are uploaded, you can call the oss.CompleteMultipartUpload method to combine the parts into a complete object.

Complete sample code

The following code provides a complete example that describes the process of multipart upload:

// Specify the name of the bucket to which you want to upload the object. Example: examplebucket. 
String bucketName = "examplebucket";
// Specify the full path of the object. Example: exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. 
String objectName = "exampledir/exampleobject.txt";
// Specify the full path of the local file that you want to upload to OSS by using multipart upload. Example: /storage/emulated/0/oss/examplefile.txt. 
String localFilepath = "/storage/emulated/0/oss/examplefile.txt";

// Initiate a multipart upload task. 
InitiateMultipartUploadRequest init = new InitiateMultipartUploadRequest(bucketName, objectName);
InitiateMultipartUploadResult initResult = oss.initMultipartUpload(init);
// Obtain the upload ID, which uniquely identifies the multipart upload task. You can use the upload ID to cancel or query the multipart upload task. 
String uploadId = initResult.getUploadId();

// Set the size of a single part. Unit: bytes. Valid values: 100 KB to 5 GB. 
int partCount = 100 * 1024;
// Implement the multipart upload task. 
List<PartETag> partETags = new ArrayList<>();
for (int i = 1; i < 5; i++) {
    byte[] data = new byte[partCount];

    RandomAccessFile raf = new RandomAccessFile(localFilepath, "r");
    long skip = (i-1) * partCount;
    raf.seek(skip);
    raf.readFully(data, 0, partCount);

    UploadPartRequest uploadPart = new UploadPartRequest();
    uploadPart.setBucketName(bucketName);
    uploadPart.setObjectKey(objectName);
    uploadPart.setUploadId(uploadId);
    // Set the part number for each part. The number starts from 1. Each part to upload has a part number. Valid values: 1 to 10000. 
    uploadPart.setPartNumber(i); 
    uploadPart.setPartContent(data);
    try {
        UploadPartResult result = oss.uploadPart(uploadPart);
        PartETag partETag = new PartETag(uploadPart.getPartNumber(), result.getETag());
        partETags.add(partETag);
    } catch (ServiceException serviceException) {
        OSSLog.logError(serviceException.getErrorCode());
    }
}
Collections.sort(partETags, new Comparator<PartETag>() {
    @Override
    public int compare(PartETag lhs, PartETag rhs) {
        if (lhs.getPartNumber() < rhs.getPartNumber()) {
            return -1;
        } else if (lhs.getPartNumber() > rhs.getPartNumber()) {
            return 1;
        } else {
            return 0;
        }
    }
});

// Complete the multipart upload task. 
CompleteMultipartUploadRequest complete = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);

// Configure upload callback. You can set the CALLBACK_SERVER parameter when you complete the multipart upload request. A callback request is sent to the specified server address after the multipart upload task is completed. You can view the servercallback result in completeResult.getServerCallbackReturnBody() of the response. 
complete.setCallbackParam(new HashMap<String, String>() {
    {
        put("callbackUrl", CALLBACK_SERVER); // Set the CALLBACK_SERVER parameter to your server address. 
        put("callbackBody", "test");
    }
});
CompleteMultipartUploadResult completeResult = oss.completeMultipartUpload(complete);
OSSLog.logError("-------------- serverCallback: " + completeResult.getServerCallbackReturnBody());

The preceding code uses uploadPart to upload each part.

  • You must specify the upload ID and part numbers for each multipart upload request. Valid values of PartNumber range from 1 to 10000. If a part number is not within this range, the InvalidArgument error code is returned.
  • If you use uploadPart, each part except the last part must be larger than 100 KB in size. When you use uploadPart, the size of each part is verified only after all parts are uploaded.
  • Each time you upload a part, make sure that the stream is directed to the start position of the part to upload.
  • Each time a part is uploaded, OSS returns a response that contains the ETag value of the part. If the ETag value is the same as the MD5 hash, combine the ETag value with the part number into a PartETag and save the PartETag for subsequent uploads of parts.

Upload a local file by using multipart upload

You can upload local files to OSS by using multipart upload in synchronous mode.

Note The preceding complete sample code is used to implement a multipart upload task by following the multipart upload process. The complete sample code is encapsulated to upload a local file by using multipart upload. This way, you need only to use MultipartUploadRequest to implement a multipart upload task for a local file.
  • Upload a local file by using multipart upload in synchronous mode

    The following code provides an example on how to upload a local file named examplefile.txt to the exampleobject.txt object in the exampledir directory of the examplebucket bucket by using multipart upload in synchronous mode:

    // Specify the name of the bucket to which you want to upload the object. Example: examplebucket. 
    String bucketName = "examplebucket";
    // Specify the full path of the object. Example: exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. 
    String objectName = "exampledir/exampleobject.txt";
    // Specify the full path of the local file that you want to upload to OSS by using multipart upload. Example: /storage/emulated/0/oss/examplefile.txt. 
    String localFilepath = "/storage/emulated/0/oss/examplefile.txt";
    
    ObjectMetadata meta = new ObjectMetadata();
    // Configure object metadata. 
    meta.setHeader("x-oss-object-acl", "public-read-write");
    MultipartUploadRequest rq = new MultipartUploadRequest(bucketName, objectName, localFilepath, meta);
    // Set PartSize. The default value of PartSize is 256 KB. The minimum value is 100 KB. 
    rq.setPartSize(1024 * 1024);
    rq.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() {
        @Override
        public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) {
            OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false);
        }
    });
    
    CompleteMultipartUploadResult result = oss.multipartUpload(rq);

    For partition storage of Android 10 and later, you can use the Uniform Resource Identifier (URI) of an object to upload the object to OSS.

    // Specify the name of the bucket to which you want to upload the object. Example: examplebucket. 
    String bucketName = "examplebucket";
    // Specify the full path of the object. Example: exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. 
    String objectName = "exampledir/exampleobject.txt";
    
    ObjectMetadata meta = new ObjectMetadata();
    // Configure object metadata. 
    meta.setHeader("x-oss-object-acl", "public-read-write");
    MultipartUploadRequest rq = new MultipartUploadRequest(bucketName, objectName, fileUri, meta);
    // Set PartSize. The default value of PartSize is 256 KB. The minimum value is 100 KB. 
    rq.setPartSize(1024 * 1024);
    rq.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() {
        @Override
        public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) {
            OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false);
        }
    });
    
    CompleteMultipartUploadResult result = oss.multipartUpload(rq);
  • Upload a local file by using multipart upload in asynchronous mode

    The following code provides an example on how to upload a local file named examplefile.txt to the exampleobject.txt object in the exampledir directory of the examplebucket bucket by using multipart upload in asynchronous mode:

    // Specify the name of the bucket to which you want to upload the object. Example: examplebucket. 
    String bucketName = "examplebucket";
    // Specify the full path of the object. Example: exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. 
    String objectName = "exampledir/exampleobject.txt";
    // Specify the full path of the local file that you want to upload to OSS by using multipart upload. Example: /storage/emulated/0/oss/examplefile.txt. 
    String localFilepath = "/storage/emulated/0/oss/examplefile.txt";
    
    MultipartUploadRequest request = new MultipartUploadRequest(bucketName, objectName, localFilepath);
    
    request.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() {
        @Override
        public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) {
            OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false);
        }
    });
    
    OSSAsyncTask task = oss.asyncMultipartUpload(request, new OSSCompletedCallback<MultipartUploadRequest, CompleteMultipartUploadResult>() {
        @Override
        public void onSuccess(MultipartUploadRequest request, CompleteMultipartUploadResult result) {
            OSSLog.logInfo(result.getServerCallbackReturnBody());
        }
    
        @Override
        public void onFailure(MultipartUploadRequest request, ClientException clientException, ServiceException serviceException) {
            OSSLog.logError(serviceException.getRawMessage());
        }
    });
    
    //Thread.sleep(100);
    // Cancel the multipart upload task. 
    //task.cancel();   
    
    task.waitUntilFinished();

    For partition storage of Android 10 and later, you can use the URI of an object to upload the object to OSS.

    // Specify the name of the bucket to which you want to upload the object. Example: examplebucket. 
    String bucketName = "examplebucket";
    // Specify the full path of the object. Example: exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. 
    String objectName = "exampledir/exampleobject.txt";
    
    MultipartUploadRequest request = new MultipartUploadRequest(bucketName, objectName, fileUri);
    
    request.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() {
        @Override
        public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) {
            OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false);
        }
    });
    
    OSSAsyncTask task = oss.asyncMultipartUpload(request, new OSSCompletedCallback<MultipartUploadRequest, CompleteMultipartUploadResult>() {
        @Override
        public void onSuccess(MultipartUploadRequest request, CompleteMultipartUploadResult result) {
            OSSLog.logInfo(result.getServerCallbackReturnBody());
        }
    
        @Override
        public void onFailure(MultipartUploadRequest request, ClientException clientException, ServiceException serviceException) {
            OSSLog.logError(serviceException.getRawMessage());
        }
    });
    
    //Thread.sleep(100);
    // Cancel the multipart upload task. 
    //task.cancel();   
    
    task.waitUntilFinished();

List uploaded parts

You can call the oss.listParts method to obtain all the parts that are uploaded in a multipart upload task.

The following code provides an example on how to list uploaded parts:

// Specify the name of the bucket to which the parts are uploaded. Example: examplebucket. 
String bucketName = "examplebucket";
// Specify the full path of the object. Example: exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. 
String objectName = "exampledir/exampleobject.txt";
// Specify the upload ID. 
String uploadId = "0004B999EF518A1FE585B0C9360D";

// List uploaded parts. 
ListPartsRequest listParts = new ListPartsRequest(bucketName, objectName, uploadId);
ListPartsResult result = oss.listParts(listParts);

List<PartETag> partETagList = new ArrayList<PartETag>();
for (PartSummary part : result.getParts()) {
    partETagList.add(new PartETag(part.getPartNumber(), part.getETag()));
}
Notice By default, if a bucket contains more than 1,000 parts that are uploaded by using multipart upload, OSS returns the first 1,000 parts. In the response, the IsTruncated field is set to false and NextPartNumberMarker is returned to indicate the next starting position to continue reading data.

Cancel a multipart upload task

You can call the ossClient.abortMultipartUpload method to cancel a multipart upload task. If you cancel a multipart upload task, you cannot use the upload ID to upload parts. The uploaded parts are deleted.

The following code provides an example on how to cancel a multipart upload task:

// Specify the name of the bucket to which the parts are uploaded. Example: examplebucket. 
String bucketName = "examplebucket";
// Specify the full path of the object. Example: exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. 
String objectName = "exampledir/exampleobject.txt";
// Specify the upload ID. 
String uploadId = "0004B999EF518A1FE585B0C9360D";

// Cancel the multipart upload task. 
AbortMultipartUploadRequest abort = new AbortMultipartUploadRequest(bucketName, objectName, uploadId);
AbortMultipartUploadResult abortResult = oss.abortMultipartUpload(abort);