Object的分块上传

优质
小牛编辑
130浏览
2023-12-01

除了通过putObject接口上传文件到OSS以外,OSS还提供了另外一种上传模式 —— Multipart Upload。用户可以在如下的应用场景内(但不仅限于此),使用Multipart Upload上传模式,如:

  • 需要支持断点上传。
  • 上传超过100MB大小的文件。
  • 网络条件较差,和OSS的服务器之间的链接经常断开。
  • 需要流式地上传文件。
  • 上传文件之前,无法确定上传文件的大小。

下面我们将一步步介绍怎样实现Multipart Upload。

分步完成Multipart Upload

假设我们有一个文件,本地路径为 /path/to/file.zip 由于文件比较大,我们将其分块传输到OSS中。

1. 初始化Multipart Upload

<?php

$initResult = $client->initiateMultipartUpload(array(
    'Bucket' => 'your-bucket-name',
    'Key' => 'your-object-key',
));

echo $initResult->getUploadId();

initiateMultipartUpload 方法用来初始化一个分块上传事件,它会返回一个uploadId作为事件标识。在后面的步骤里,我们可以使用uploadId分块将文件上传上去。

2. 上传分块

<?php

$uploadFile = '/path/to/file.zip';

$filesize = filesize($uploadFile);

$partSize = 1024 * 1024 * 5; //5M

$partETags = array(); //Used for complete multipart

// Decide how many parts should be uploaded.
$partNumber = floor($filesize / $partSize);
if ($filesize % $partSize > 0) {
    $partNumber += 1;
}

for ($i = 1; $i <= $partNumber; $i++) {

    $upSize = $partSize;

    // The last part
    if ($i == $partNumber) {
        $upSize = $filesize - ($partNumber - 1) * $partSize;
    }
    $content = fopen($uploadFile, 'r');
    fseek($content, ($i - 1) * $partSize);

    $uploadResult = $client->uploadPart(array(
        'Bucket' => 'your-bucket-name',
        'Key' => 'your-object-key',
        'UploadId' => $initResult->getUploadId(),
        'Content' => $content,
        'PartNumber' => $i,
        'PartSize' => $upSize,
    ));

    $partETags[] = $uploadResult->getPartETag();
}

uploadPart 方法需要传入以下几个参数:

  • Bucket: Object所在的Bucket的名字
  • Key: Oject的Key
  • UploadId: 在初始化上传阶段得到的上传Id。
  • Content: 本次上传的内容。对于分块上传来讲,如果Content为resource类型,则需要在上传之前把其定位到本次上传块的开始位置。
  • PartNumber: 上传的序号,用来标识不同上传块。
  • PartSize: 本次上传块的大小,最小为5M。

在每次上传的返回结果中我们都会得到上传返回的PartETag,它是ParNumber和上传块ETag的组合。在上面的代码中,我们将其以此放入数组中,以便在完成分块上传时使用。

3. 完成分块上传

<?php

$client->completeMultipartUpload(array(
    'Bucket' => 'your-bucket-name',
    'Key' => 'your-object-key',
    'UploadId' => $initResult->getUploadId(),
    'PartETags' => $partETags,
));

上面代码中的 $partETags 就是第二部中保存的partETag的列表,OSS收到用户提交的Part列表后,会逐一验证每个数据Part的有效性。当所有的数据Part验证通过后,OSS将把这些数据part组合成一个完整的Object。

取消分块上传事件

我们可以用 abortMultipartUpload 方法取消分块上传。

<?php

$client->abortMultipartUpload(array(
    'Bucket' => 'your-bucket-name',
    'Key' => 'your-object-key',
    'UploadId' => $initResult->getUploadId(),
));

获取Bucket内所有分块上传事件

我们可以用 listMultipartUploads 方法获取Bucket内所有上传事件。

<?php
$listMultipartUploadsResult = $client->listMultipartUploads(array(
    'Bucket' => 'your-bucket-name',
));

foreach ($listMultipartUploadsResult->getMultipartUploads() as $multipartUpload) {
    echo 'Bucket: ' . $listMultipartUploadsResult->getBucketName() . ' Key: ' . $multipartUpload->getKey() . ' UploadId: ' . $multipartUpload->getUploadId();
    echo "\n";
}

Note

默认情况下,如果Bucket中的分块上传事件的数量大于1000,则只会返回1000个事件, 且返回结果中 IsTruncated 为 false,并返回 NextKeyMarkerNextUploadMarker 作为下次读取的起点。若想增大返回分块上传事件数目,可以修改 MaxUploads 参数,或者使用 KeyMarker 以及 UploadIdMarker 参数分次读取。

获取所有已上传的块信息

我们可以用 listParts 方法获取某个上传事件所有已上传的块。

<?php

$listPartsResult = $client->listParts(array(
    'Bucket' => 'your-bucket-name',
    'Key' => 'your-object-key',
    'UploadId' => $initResult->getUploadId(),
));

foreach ($listPartsResult->getParts() as $part) {
    echo  $part->getPartNumber() . "\n";
}

Note

默认情况下,如果上传块的数量大于1000,则只会返回1000个Object, 且返回结果中 IsTruncated 为 false,并返回 NextPartNumberMarker 作为下次读取的起点。若想增大返回块的数目,可以修改 MaxParts 参数,或者使用 PartNumberMarker 参数分次读取。