All Products
Search
Document Center

Object Storage Service:Multipart upload (PHP SDK V1)

Last Updated:Mar 20, 2026

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.

Important

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_ID and OSS_ACCESS_KEY_SECRET environment variables configured with valid credentials

  • The OSS PHP SDK V1 installed

How it works

Multipart upload follows three steps:

  1. Initiate — call initiateMultipartUpload to get a unique upload ID.

  2. Upload parts — call uploadPart for each part, tracking the ETag returned for each.

  3. Complete — call completeMultipartUpload with 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

ParameterDescription
delimiterGroups 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-markerUsed 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-uploadsMaximum number of tasks to return. Maximum and default value: 1000.
prefixFilters results to object names that start with this prefix. Returned object names include the prefix.
upload-id-markerUsed 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