Object Storage Service (OSS) lets you split a large object into multiple parts and upload them separately. After all parts are uploaded, call the CompleteMultipartUpload operation to assemble them into a single object.
Multipart upload is ideal for large files or unstable network conditions. Upload parts in parallel over a fast network to maximize throughput, or use smaller parts over an unreliable network to minimize retry overhead.
Prerequisites
Before you begin, make sure that you have:
An
OSSClientinstance initialized with an endpoint that maps to your bucket's region. For more information, see InitializationThe
oss:PutObjectpermission granted to your RAM user. For more information, see Attach a custom policy to a RAM user
Limits
| Item | Limit |
|---|---|
| Minimum part size | 100 KB (except the last part) |
Default part size (OSSMultipartUploadRequest) | 256 KB |
| Part number range | 1 to 10,000 |
Maximum parts returned per ListParts request | 1,000 |
Upload a local file (recommended)
OSSMultipartUploadRequest handles the multipart upload workflow automatically. It splits the file, uploads each part, and completes the upload. Use this approach unless you need fine-grained control over individual parts.
// Specify the bucket name. Example: examplebucket.
NSString *bucketName = @"examplebucket";
// Specify the full path of the object. Do not include the bucket name.
// Example: exampledir/exampleobject.txt.
NSString *objectKey = @"exampledir/exampleobject.txt";
OSSMultipartUploadRequest * multipartUploadRequest = [OSSMultipartUploadRequest new];
multipartUploadRequest.bucketName = bucketName;
multipartUploadRequest.objectKey = objectKey;
// Set the part size in bytes. Default: 256 KB.
multipartUploadRequest.partSize = 1024 * 1024;
multipartUploadRequest.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
NSLog(@"progress: %lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
};
multipartUploadRequest.uploadingFileURL = [[NSBundle mainBundle] URLForResource:@"wangwang" withExtension:@"zip"];
OSSTask * multipartTask = [client multipartUpload:multipartUploadRequest];
[[multipartTask continueWithBlock:^id(OSSTask *task) {
if (task.error) {
NSLog(@"error: %@", task.error);
} else {
NSLog(@"Upload file success");
}
return nil;
}] waitUntilFinished];Key parameters:
| Parameter | Description |
|---|---|
bucketName | Target bucket name |
objectKey | Full object path, excluding the bucket name |
partSize | Size of each part in bytes. Default: 256 KB |
uploadProgress | Callback that reports bytes sent, total bytes sent, and total bytes expected |
uploadingFileURL | Local file URL to upload |
Step-by-step multipart upload
For scenarios that require fine-grained control, such as custom retry logic per part or parallel uploads with a specific concurrency level, use the three-step multipart upload process directly.
How it works
Call
InitiateMultipartUploadto start a multipart upload task and get an upload ID.Call
UploadPartto upload each part. Parts can be uploaded sequentially or in parallel.Call
CompleteMultipartUploadto assemble all parts into a single object.
Part position within the object is determined by the part number you specify during upload. Parts do not need to be uploaded in order.
Complete example
__block NSString * uploadId = nil;
__block NSMutableArray * partInfos = [NSMutableArray new];
// Specify the bucket name. Example: examplebucket.
NSString * uploadToBucket = @"examplebucket";
// Specify the full path of the object. Do not include the bucket name.
// Example: exampledir/exampleobject.txt.
NSString * uploadObjectkey = @"exampledir/exampleobject.txt";
// Step 1: Initiate the multipart upload task.
OSSInitMultipartUploadRequest * init = [OSSInitMultipartUploadRequest new];
init.bucketName = uploadToBucket;
init.objectKey = uploadObjectkey;
// init.contentType = @"application/octet-stream";
OSSTask * initTask = [client multipartUploadInit:init];
[initTask waitUntilFinished];
if (!initTask.error) {
OSSInitMultipartUploadResult * result = initTask.result;
// The upload ID uniquely identifies this multipart upload task.
// Use it to upload parts, list parts, or cancel the task.
uploadId = result.uploadId;
} else {
NSLog(@"multipart upload failed, error: %@", initTask.error);
return;
}
// Step 2: Upload each part.
NSString * filePath = @"<filepath>";
uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil] fileSize];
int chuckCount = 10;
uint64_t offset = fileSize/chuckCount;
for (int i = 1; i <= chuckCount; i++) {
OSSUploadPartRequest * uploadPart = [OSSUploadPartRequest new];
uploadPart.bucketName = uploadToBucket;
uploadPart.objectkey = uploadObjectkey;
uploadPart.uploadId = uploadId;
uploadPart.partNumber = i; // Part numbers start from 1.
NSFileHandle* readHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
[readHandle seekToFileOffset:offset * (i -1)];
NSData* data = [readHandle readDataOfLength:offset];
uploadPart.uploadPartData = data;
OSSTask * uploadPartTask = [client uploadPart:uploadPart];
[uploadPartTask waitUntilFinished];
if (!uploadPartTask.error) {
OSSUploadPartResult * result = uploadPartTask.result;
uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:uploadPart.uploadPartFileURL.absoluteString error:nil] fileSize];
[partInfos addObject:[OSSPartInfo partInfoWithPartNum:i eTag:result.eTag size:fileSize]];
} else {
NSLog(@"upload part error: %@", uploadPartTask.error);
return;
}
}
// Step 3: Complete the multipart upload.
OSSCompleteMultipartUploadRequest * complete = [OSSCompleteMultipartUploadRequest new];
complete.bucketName = uploadToBucket;
complete.objectKey = uploadObjectkey;
complete.uploadId = uploadId;
complete.partInfos = partInfos;
OSSTask * completeTask = [client completeMultipartUpload:complete];
[[completeTask continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
OSSCompleteMultipartUploadResult * result = task.result;
// ...
} else {
// ...
}
return nil;
}] waitUntilFinished];Replace <filepath> with the absolute path to the local file.
Concurrency tuning
When uploading parts in parallel, set the concurrency level based on network conditions and device load. A higher concurrency level does not always result in faster uploads. Use larger parts when the network is stable, and smaller parts when it is not.
List uploaded parts
Call the listParts method to retrieve all uploaded parts for a specific multipart upload task. The upload ID is required.
OSSListPartsRequest * listParts = [OSSListPartsRequest new];
// Specify the bucket name. Example: examplebucket.
listParts.bucketName = @"examplebucket";
// Specify the full path of the object. Do not include the bucket name.
// Example: exampledir/exampleobject.txt.
listParts.objectKey = @"exampledir/exampleobject.txt";
// Specify the upload ID. Get it from the InitiateMultipartUpload response
// before calling CompleteMultipartUpload.
listParts.uploadId = @"0004B999EF518A1FE585B0C9****";
OSSTask * listPartTask = [client listParts:listParts];
[listPartTask continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
NSLog(@"list part result success!");
OSSListPartsResult * listPartResult = task.result;
for (NSDictionary * partInfo in listPartResult.parts) {
NSLog(@"each part: %@", partInfo);
}
} else {
NSLog(@"list part result error: %@", task.error);
}
return nil;
}];By default, if more than 1,000 parts exist for a multipart upload, OSS returns the first 1,000 parts. In the response,IsTruncatedis set tofalseandNextPartNumberMarkerindicates the starting position for the next list request.
Cancel a multipart upload task
Call the abortMultipartUpload method to cancel a multipart upload task and delete all uploaded parts.
OSSAbortMultipartUploadRequest * abort = [OSSAbortMultipartUploadRequest new];
// Specify the bucket name. Example: examplebucket.
abort.bucketName = @"examplebucket";
// Specify the full path of the object. Do not include the bucket name.
// Example: exampledir/exampleobject.txt.
abort.objectKey = @"exampledir/exampleobject.txt";
// Specify the upload ID. Get it from the InitiateMultipartUpload response.
abort.uploadId = @"0004B999EF518A1FE585B0C9****";
OSSTask * abortTask = [client abortMultipartUpload:abort];
[abortTask waitUntilFinished];
if (!abortTask.error) {
OSSAbortMultipartUploadResult * result = abortTask.result;
uploadId = result.uploadId;
} else {
NSLog(@"multipart upload failed, error: %@", abortTask.error);
return;
}If you upload parts but do not call CompleteMultipartUpload, the parts remain in storage and incur costs. Either call AbortMultipartUpload to delete them, or configure a lifecycle rule to automatically clean up incomplete multipart uploads.
References
For the complete sample code, visit GitHub.
The multipart upload workflow uses three API operations:
ListParts -- List uploaded parts for a multipart upload task.
AbortMultipartUpload -- Cancel a multipart upload task and delete uploaded parts.