Multipart upload lets you split a large object into parts, upload each part independently, then combine them into a complete object.
Benefits:
Higher throughput — upload parts in parallel to maximize bandwidth utilization.
Network resilience — if a part fails, only that part needs to be re-uploaded, not the entire object.
Unknown final size — start uploading before the full object size is known.
Uploaded parts consume storage and incur charges until you either complete or abort the upload. Always call completeMultipartUpload or abortMultipartUpload when you are done.
Prerequisites
Before you begin, ensure that you have:
An OSS bucket
The
OSS_ACCESS_KEY_IDandOSS_ACCESS_KEY_SECRETenvironment variables configured with valid credentialsThe OSS PHP SDK V1 installed
How it works
Multipart upload follows three steps:
Initiate — call
initiateMultipartUploadto get a unique upload ID.Upload parts — call
uploadPartfor each part, tracking the ETag returned for each.Complete — call
completeMultipartUploadwith the full part list to merge all parts into the final object.
To cancel an in-progress upload, call abortMultipartUpload. This deletes all uploaded parts and invalidates the upload ID.
Upload a file
For most use cases, use multiuploadFile. It handles part splitting, MD5 verification, and completion automatically.
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\Core\OssException;
// Load credentials from environment variables.
$provider = new EnvironmentVariableCredentialsProvider();
$config = [
'provider' => $provider,
'endpoint' => 'https://oss-cn-hangzhou.aliyuncs.com',
'signatureVersion' => OssClient::OSS_SIGNATURE_VERSION_V4,
'region' => 'cn-hangzhou',
];
$ossClient = new OssClient($config);
$bucket = 'examplebucket';
$object = 'exampledir/exampleobject.txt';
$file = 'D:\\localpath\\examplefile.txt';
$options = [
OssClient::OSS_CHECK_MD5 => true,
OssClient::OSS_PART_SIZE => 1,
];
try {
$ossClient->multiuploadFile($bucket, $object, $file, $options);
print('multiuploadFile: OK' . "\n");
} catch (OssException $e) {
printf("multiuploadFile: FAILED\n%s\n", $e->getMessage());
}Upload a directory
To upload all files in a local directory, use uploadDir.
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\Core\OssException;
$provider = new EnvironmentVariableCredentialsProvider();
$config = [
'provider' => $provider,
'endpoint' => 'https://oss-cn-hangzhou.aliyuncs.com',
'signatureVersion' => OssClient::OSS_SIGNATURE_VERSION_V4,
'region' => 'cn-hangzhou',
];
$ossClient = new OssClient($config);
$bucket = 'examplebucket';
$localDirectory = 'D:\\localpath';
$prefix = 'samples/codes/'; // Object key prefix in OSS
try {
$ossClient->uploadDir($bucket, $prefix, $localDirectory);
print('uploadDir: OK' . "\n");
} catch (OssException $e) {
printf("uploadDir: FAILED\n%s\n", $e->getMessage());
}Run the three-step upload manually
Use the low-level API when you need full control — for example, to set headers on initialization, apply custom part sizes, or resume from a known upload ID.
The example below follows the three-step pattern: get an upload ID from initiateMultipartUpload, pass it to uploadPart for each chunk, then finalize with completeMultipartUpload.
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\Core\OssException;
use OSS\Core\OssUtil;
$provider = new EnvironmentVariableCredentialsProvider();
$config = [
'provider' => $provider,
'endpoint' => 'https://oss-cn-hangzhou.aliyuncs.com',
'signatureVersion' => OssClient::OSS_SIGNATURE_VERSION_V4,
'region' => 'cn-hangzhou',
];
$ossClient = new OssClient($config);
$bucket = 'examplebucket';
$object = 'exampledir/exampleobject.txt';
$uploadFile = 'D:\\localpath\\examplefile.txt';
// Optional headers for the initiation request.
$initOptions = [
OssClient::OSS_HEADERS => [
// 'Cache-Control' => 'no-cache',
// 'Content-Disposition' => 'attachment;filename=oss_download.jpg',
// 'Expires' => 150, // milliseconds
// 'x-oss-forbid-overwrite' => 'true',
// 'x-oss-server-side-encryption' => 'KMS',
// 'x-oss-server-side-data-encryption'=> 'SM4',
// 'x-oss-server-side-encryption-key-id' => '9468da86-3509-4f8d-a61e-6eab1eac****',
// 'x-oss-storage-class' => 'Standard',
// 'x-oss-tagging' => 'TagA=A&TagB=B',
],
];
// Step 1: Initiate the upload and get an upload ID.
// Use the upload ID to upload parts, check progress, or abort the task.
try {
$uploadId = $ossClient->initiateMultipartUpload($bucket, $object, $initOptions);
print('initiateMultipartUpload: OK' . "\n");
} catch (OssException $e) {
printf("initiateMultipartUpload: FAILED\n%s\n", $e->getMessage());
return;
}
// Step 2: Split the file and upload each part.
// Part size: minimum 100 KB, maximum 5 GB. The last part may be smaller than 100 KB.
$partSize = 10 * 1024 * 1024; // 10 MB per part
$uploadFileSize = sprintf('%u', filesize($uploadFile));
$pieces = $ossClient->generateMultiuploadParts($uploadFileSize, $partSize);
$responseUploadPart = [];
$uploadPosition = 0;
$isCheckMd5 = true;
foreach ($pieces as $i => $piece) {
$fromPos = $uploadPosition + (int)$piece[$ossClient::OSS_SEEK_TO];
$toPos = (int)$piece[$ossClient::OSS_LENGTH] + $fromPos - 1;
$upOptions = [
$ossClient::OSS_FILE_UPLOAD => $uploadFile,
$ossClient::OSS_PART_NUM => ($i + 1), // Part numbers start at 1.
$ossClient::OSS_SEEK_TO => $fromPos,
$ossClient::OSS_LENGTH => $toPos - $fromPos + 1,
$ossClient::OSS_CHECK_MD5 => $isCheckMd5,
];
if ($isCheckMd5) {
$upOptions[$ossClient::OSS_CONTENT_MD5] =
OssUtil::getMd5SumForFile($uploadFile, $fromPos, $toPos);
}
try {
$responseUploadPart[] = $ossClient->uploadPart($bucket, $object, $uploadId, $upOptions);
printf("uploadPart - part#%d: OK\n", $i);
} catch (OssException $e) {
printf("uploadPart - part#%d: FAILED\n%s\n", $i, $e->getMessage());
return;
}
}
// Build the part list required by completeMultipartUpload.
// Each element contains the part number and the ETag returned by uploadPart.
$uploadParts = [];
foreach ($responseUploadPart as $i => $eTag) {
$uploadParts[] = [
'PartNumber' => ($i + 1),
'ETag' => $eTag,
];
}
// Step 3: Combine all parts into the final object.
$comOptions = [
'headers' => [
// 'x-oss-forbid-overwrite' => 'true',
// 'x-oss-complete-all' => 'yes', // OSS lists all parts uploaded with this upload ID, sorts them by part number, and performs CompleteMultipartUpload.
],
];
try {
$ossClient->completeMultipartUpload($bucket, $object, $uploadId, $uploadParts, $comOptions);
print('completeMultipartUpload: OK' . "\n");
} catch (OssException $e) {
printf("completeMultipartUpload: FAILED\n%s\n", $e->getMessage());
}Part number behavior
Part numbers identify the position of each part in the final object.
If you upload a new part with the same part number as an existing part, the existing part is overwritten.
OSS includes the MD5 hash of each received part in the ETag response header. If the hash does not match the value sent by the SDK, OSS returns
InvalidDigest.
Abort an upload
Call abortMultipartUpload to cancel an in-progress upload. This deletes all uploaded parts and makes the upload ID unusable.
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\Core\OssException;
$provider = new EnvironmentVariableCredentialsProvider();
$config = [
'provider' => $provider,
'endpoint' => 'https://oss-cn-hangzhou.aliyuncs.com',
'signatureVersion' => OssClient::OSS_SIGNATURE_VERSION_V4,
'region' => 'cn-hangzhou',
];
$ossClient = new OssClient($config);
$bucket = 'examplebucket';
$object = 'exampledir/exampleobject.txt';
$upload_id = '0004B999EF518A1FE585B0C9360D****'; // From initiateMultipartUpload
try {
$ossClient->abortMultipartUpload($bucket, $object, $upload_id);
print('abortMultipartUpload: OK' . "\n");
} catch (OssException $e) {
printf("abortMultipartUpload: FAILED\n%s\n", $e->getMessage());
}List uploaded parts
Use listParts to check which parts have been uploaded for a given upload ID. Call this before completing or resuming an upload.
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\Core\OssException;
$provider = new EnvironmentVariableCredentialsProvider();
$config = [
'provider' => $provider,
'endpoint' => 'https://oss-cn-hangzhou.aliyuncs.com',
'signatureVersion' => OssClient::OSS_SIGNATURE_VERSION_V4,
'region' => 'cn-hangzhou',
];
$ossClient = new OssClient($config);
$bucket = 'examplebucket';
$object = 'exampledir/exampleobject.txt';
$uploadId = '0004B999EF518A1FE585B0C9360D****'; // From initiateMultipartUpload
try {
$listPartsInfo = $ossClient->listParts($bucket, $object, $uploadId);
foreach ($listPartsInfo->getListPart() as $partInfo) {
printf(
"%s\t%s\t%s\t%s\n",
$partInfo->getPartNumber(),
$partInfo->getSize(),
$partInfo->getETag(),
$partInfo->getLastModified()
);
}
print('listParts: OK' . "\n");
} catch (OssException $e) {
printf("listParts: FAILED\n%s\n", $e->getMessage());
}List multipart upload tasks
Use listMultipartUploads to list all ongoing multipart upload tasks for a bucket. Ongoing tasks are those that have been initiated but not yet completed or aborted.
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\Core\OssException;
$provider = new EnvironmentVariableCredentialsProvider();
$config = [
'provider' => $provider,
'endpoint' => 'https://oss-cn-hangzhou.aliyuncs.com',
'signatureVersion' => OssClient::OSS_SIGNATURE_VERSION_V4,
'region' => 'cn-hangzhou',
];
$ossClient = new OssClient($config);
$bucket = 'examplebucket';
$options = [
'delimiter' => '/',
'max-uploads' => 100,
'key-marker' => '',
'prefix' => '',
'upload-id-marker' => '',
];
try {
$listMultipartUploadInfo = $ossClient->listMultipartUploads($bucket, $options);
printf('listMultipartUploads: OK' . "\n");
$listUploadInfo = $listMultipartUploadInfo->getUploads();
var_dump($listUploadInfo);
} catch (OssException $e) {
printf("listMultipartUploads: FAILED\n%s\n", $e->getMessage());
}Parameters
| Parameter | Description |
|---|---|
delimiter | Groups object names by prefix. Objects whose names share the same prefix up to the delimiter are grouped together and returned as a single entry in commonPrefixes. |
key-marker | Used with upload-id-marker to set the starting position for listing. Only tasks for object keys that are alphabetically greater than this value are returned. |
max-uploads | Maximum number of tasks to return. Maximum and default value: 1000. |
prefix | Filters results to object names that start with this prefix. Returned object names include the prefix. |
upload-id-marker | Used with key-marker. If key-marker is not set, this parameter has no effect. If key-marker is set, results include: (1) all objects alphabetically greater than key-marker, and (2) all tasks for objects with the same name as key-marker whose upload ID is greater than upload-id-marker. |
What's next
For the complete sample code, see MultipartUpload.php on GitHub.
API reference for the underlying operations: