You need to enable JavaScript to run this app.
导航
分片上传(PHP SDK)
最近更新时间:2024.03.21 15:13:15首次发布时间:2022.11.16 14:58:44

分片上传适用于较大对象的上传,其原理是将数据源切分成多个分片分别上传,最后再合并生成最终的对象。

上传步骤

使用 TOS 分片上传接口步骤如下:

  1. 通过 TosClient->createMultipartUpload 创建分片上传任务,并获取 UploadID。
  2. 通过 TosClient->uploadPart 上传多个分片。

    注意

    • 单个分片大小不能超过 5GiB,最多支持 10000 个分片。
    • 除最后一个分片外,每个分片的大小不能小于 4MiB,TOS 服务端对于分片大小的校验会在合并分片时进行。
    • 每个分片通过分片号(PartNumber)来唯一标识,分片号有效区间为 1~10000。对于同一个分片号,可以通过重复上传覆盖之前已上传的分片数据。
  3. 通过 TosClient->completeMultipartUpload 合并分片,生成最终对象。

示例代码

以下示例展示了通过分片上传实现本地大文件上传的完整代码:

<?php

// 假设使用 composer 安装
require_once __DIR__ . '/vendor/autoload.php';

use Tos\TosClient;
use Tos\Exception\TosClientException;
use Tos\Exception\TosServerException;
use Tos\Model\CompleteMultipartUploadInput;
use Tos\Model\CreateMultipartUploadInput;
use Tos\Model\Enum;
use Tos\Model\UploadedPart;
use Tos\Model\UploadPartInput;
use GuzzleHttp\Psr7\Utils;

$file = null;
try {
    $client = new TosClient([
        'region' => 'your region',
        'endpoint' => 'your endpoint',
        // 从环境变量中获取访问密钥
        'ak' => getenv('TOS_ACCESS_KEY'),
        'sk' => getenv('TOS_SECRET_KEY'),
    ]);
    
    $bucket = 'bucket-test';
    $key = 'key-test';
    
    // 步骤一:创建分片上传任务
    $input = new CreateMultipartUploadInput($bucket, $key);
    // 设置对象 ACL
    $input->setACL(Enum::ACLPublicRead);
    // 设置对象 StorageClass
    $input->setStorageClass(Enum::StorageClassStandard);
    // 设置对象自定义元数据
    $input->setMeta(['aaa' => 'bbb', '中文键' => '中文值']);
    // 设置对象 Content-Type
    $input->setContentType('text/plain');
    $output = $client->createMultipartUpload($input);
    echo $output->getRequestId() . PHP_EOL;
    echo 'createMultipartUpload succeed' . PHP_EOL;

    // 获取 UploadID
    $uploadId = $output->getUploadID();

    // 步骤二:上传多个分片
    // 假设本地大文件路径为 local_big_file_path
    $localBigFilePath = 'local_big_file_path';
    // 假设按照 20MB 切分大文件
    $partSize = intval(20 * 1024 * 1024);
    $fileSize = filesize($localBigFilePath);

    $partCount = intval($fileSize / $partSize);
    if (($lastPartSize = $fileSize % $partSize) !== 0) {
        $partCount++;
    } else {
        $lastPartSize = $partSize;
    }

    $parts = [];
    for ($i = 0; $i < $partCount; $i++) {
        $partNumber = $i + 1;
        $file = fopen($localBigFilePath, 'r');
        // 设置当前上传的文件起始位置
        fseek($file, $partSize * $i, 0);
        $input = new UploadPartInput($bucket, $key, $uploadId, $partNumber);
        if ($i === $partCount - 1) {
            // 处理最后一个分片
            $input->setContentLength($lastPartSize);
        } else {
            $input->setContentLength($partSize);
        }
        $input->setContent($file);
        $output = $client->uploadPart($input);
        echo $output->getRequestId() . PHP_EOL;
        echo sprintf('upload part %d succeed', $partNumber) . PHP_EOL;
        if (is_resource($file)) {
            fclose($file);
        }
        // 收集所有分片
        $parts[] = new UploadedPart($partNumber, $output->getETag());
    }

    // 步骤三:合并分片
    $input = new CompleteMultipartUploadInput($bucket, $key, $uploadId, $parts);
    $output = $client->completeMultipartUpload($input);
    echo $output->getRequestId() . PHP_EOL;
    echo 'completeMultipartUpload succeed' . PHP_EOL;

} catch (TosClientException $ex) {
    echo $ex->getMessage() . PHP_EOL;
} catch (TosServerException $ex) {
    echo $ex->getRequestId() . PHP_EOL;
    echo $ex->getStatusCode() . PHP_EOL;
    echo $ex->getErrorCode() . PHP_EOL;
} finally {
    if (is_resource($file)) {
        fclose($file);
    }
}