OSS提供的分片上传(Multipart Upload)功能,将要上传的较大文件(Object)分成多个分片(Part)来分别上传,上传完成后再调用CompleteMultipartUpload接口将这些Part组合成一个Object。
注意事项
本文示例代码以华东1(杭州)的地域ID
cn-hangzhou
为例,默认使用外网Endpoint,如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见OSS地域和访问域名。要分片上传,您必须有
oss:PutObject
权限。具体操作,请参见为RAM用户授权自定义的权限策略。
分片上传流程
分片上传(Multipart Upload)分为以下三个步骤:
初始化一个分片上传事件。
调用InitiateMultipartUpload方法返回OSS创建的全局唯一的uploadID。
上传分片。
调用UploadPart方法上传分片数据。
说明对于同一个uploadID,分片号(partNumber)标识了该分片在整个文件内的相对位置。如果使用同一个分片号上传了新的数据,那么OSS上该分片已有的数据将会被覆盖。
OSS将收到的分片数据的MD5值放在ETag头内返回给用户。
OSS计算上传数据的MD5值,并与SDK计算的MD5值比较,如果不一致则返回InvalidDigest错误码。
完成分片上传。
所有分片上传完成后,调用CompleteMultipartUpload方法将所有分片合并成完整的文件。
示例代码
以下代码展示如何将本地的大文件分割成多个分片文件上传到存储空间,然后合并成完整的文件对象。
<?php
// 引入自动加载文件,确保依赖库能够正确加载
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\Oss\V2 as Oss;
// 定义命令行参数的描述信息
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // Bucket所在的地域(必填)
"endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False], // 访问域名(可选)
"bucket" => ['help' => 'The name of the bucket', 'required' => True], // Bucket名称(必填)
"key" => ['help' => 'The name of the object', 'required' => True], // 对象名称(必填)
];
// 将参数描述转换为getopt所需的长选项格式
// 每个参数后面加上":"表示该参数需要值
$longopts = \array_map(function ($key) {
return "$key:";
}, array_keys($optsdesc));
// 解析命令行参数
$options = getopt("", $longopts);
// 验证必填参数是否存在
foreach ($optsdesc as $key => $value) {
if ($value['required'] === True && empty($options[$key])) {
$help = $value['help']; // 获取参数的帮助信息
echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
exit(1); // 如果必填参数缺失,则退出程序
}
}
// 从解析的参数中提取值
$region = $options["region"]; // Bucket所在的地域
$bucket = $options["bucket"]; // Bucket名称
$key = $options["key"]; // 对象名称
// 加载环境变量中的凭证信息
// 使用EnvironmentVariableCredentialsProvider从环境变量中读取Access Key ID和Access Key Secret
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
// 使用SDK的默认配置
$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider); // 设置凭证提供者
$cfg->setRegion($region); // 设置Bucket所在的地域
$cfg->setEndpoint('http://oss-cn-hangzhou.aliyuncs.com'); // 设置访问域名
// 创建OSS客户端实例
$client = new Oss\Client($cfg);
// 初始化分片上传任务
$initResult = $client->initiateMultipartUpload(
new Oss\Models\InitiateMultipartUploadRequest(
bucket: $bucket,
key: $key
)
);
// 定义大文件路径和分片大小
$bigFileName = "/Users/yourLocalPath/yourFileName"; // 填写大文件路径
$partSize = 5 * 1024 * 1024; // 设置分片大小,单位为字节(此处设置为1MB)
$fileSize = filesize($bigFileName); // 获取文件大小
$partsNum = intdiv($fileSize, $partSize) + intval(1); // 计算分片数量
$parts = []; // 用于存储每个分片的上传结果
$i = 1; // 分片编号从1开始
$file = new \GuzzleHttp\Psr7\LazyOpenStream($bigFileName, 'rb'); // 打开文件流
while ($i <= $partsNum) {
// 上传单个分片
$partResult = $client->uploadPart(
new Oss\Models\UploadPartRequest(
bucket: $bucket,
key: $key,
partNumber: $i, // 当前分片编号
uploadId: $initResult->uploadId, // 初始上传任务返回的uploadId
contentLength: null, // 可选:分片内容长度
contentMd5: null, // 可选:分片内容的MD5校验值
trafficLimit: null, // 可选:流量限制
requestPayer: null, // 可选:请求支付方
body: new \GuzzleHttp\Psr7\LimitStream($file, $partSize, ($i - 1) * $partSize) // 读取当前分片的数据
)
);
// 保存分片上传结果
$part = new Oss\Models\UploadPart(
partNumber: $i, // 分片编号
etag: $partResult->etag // 分片上传后返回的ETag值
);
array_push($parts, $part); // 将当前分片的上传结果保存到分片列表中
$i++; // 递增分片编号,准备处理下一个分片
}
// 完成分片上传任务
$comResult = $client->completeMultipartUpload(
new Oss\Models\CompleteMultipartUploadRequest(
bucket: $bucket,
key: $key,
uploadId: $initResult->uploadId, // 初始上传任务返回的uploadId
acl: null, // 可选:设置对象的访问控制列表(ACL)
completeMultipartUpload: new Oss\Models\CompleteMultipartUpload(
parts: $parts // 提交所有分片的上传结果
)
)
);
// 打印完成分片上传的结果
printf(
'status code:' . $comResult->statusCode . PHP_EOL . // HTTP状态码,例如200表示成功
'request id:' . $comResult->requestId . PHP_EOL . // 请求ID,用于调试或追踪请求
'complete multipart upload result:' . var_export($comResult, true) . PHP_EOL // 完成分片上传后的详细结果
);
相关文档
关于分片上传的完整示例代码,请参见GitHub示例。
该文章对您有帮助吗?