更新时间:2023-11-08 GMT+08:00
分享

分段上传

开发过程中,您有任何问题可以在github上提交issue,或者在华为云对象存储服务论坛中发帖求助。接口参考文档详细介绍了每个接口的参数和使用方法。

对于较大文件上传,可以切分成段上传。用户可以在如下的应用场景内(但不仅限于此),使用分段上传的模式:

  • 上传超过100MB大小的文件。
  • 网络条件较差,和OBS服务端之间的链接经常断开。
  • 上传前无法确定将要上传文件的大小。

分段上传分为如下3个步骤:

  1. 初始化分段上传任务(ObsClient->initiateMultipartUpload)。
  2. 逐个或并行上传段(ObsClient->uploadPart)。
  3. 合并段(ObsClient->completeMultipartUpload)或取消分段上传任务(ObsClient->abortMultipartUpload)。

初始化分段上传任务

使用分段上传方式传输数据前,必须先通知OBS初始化一个分段上传任务。该操作会返回一个OBS服务端创建的全局唯一标识(Upload ID),用于标识本次分段上传任务。您可以根据这个唯一标识来发起相关的操作,如取消分段上传任务、列举分段上传任务、列举已上传的段等。

您可以通过ObsClient->initiateMultipartUpload初始化一个分段上传任务:

// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
// 创建ObsClient实例
$obsClient = new ObsClient ( [ 
      //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
      //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
      'key' => getenv('ACCESS_KEY_ID'),
      'secret' => getenv('SECRET_ACCESS_KEY'),
      'endpoint' => 'https://your-endpoint'
] );

$resp = $obsClient->initiateMultipartUpload([
       'Bucket' => 'bucketname',
       'Key' => 'objectname',
       'ContentType' => 'text/plain',
       'Metadata' => ['property' => 'property-value']
]);

printf("RequestId:%s\n",$resp['RequestId']);
printf("UploadId:%s\n",$resp['UploadId']);
  • 初始化分段上传任务时,除了指定上传对象的名称和所属桶外,您还可以使用ContentType参数Metadata参数分别指定对象MIME类型和对象自定义元数据。
  • 调用初始化分段上传任务接口成功后,会返回分段上传任务的全局唯一标识(Upload ID),在后面的操作中将用到它。

上传段

初始化一个分段上传任务之后,可以根据指定的对象名和Upload ID来分段上传数据。每一个上传的段都有一个标识它的号码——分段号(Part Number,范围是1~10000)。对于同一个Upload ID,该分段号不但唯一标识这一段数据,也标识了这段数据在整个对象内的相对位置。如果您用同一个分段号上传了新的数据,那么OBS上已有的这个段号的数据将被覆盖。除了最后一段以外,其他段的大小范围是100KB~5GB;最后段大小范围是0~5GB。每个段不需要按顺序上传,甚至可以在不同进程、不同机器上上传,OBS会按照分段号排序组成最终对象。

您可以通过ObsClient->uploadPart上传段:

// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
// 创建ObsClient实例
$obsClient = new ObsClient ( [ 
      //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
      //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
      'key' => getenv('ACCESS_KEY_ID'),
      'secret' => getenv('SECRET_ACCESS_KEY'),
      'endpoint' => 'https://your-endpoint'
] );

$resp = $obsClient->uploadPart([
       'Bucket' => 'bucketname',
       'Key' => 'objectname',
       // 设置分段号,范围是1~10000
       'PartNumber' => 1,
       // 设置Upload ID
       'UploadId' => 'upload id from initiateMultipartUpload',
       // 设置将要上传的大文件,localfile为上传的本地文件路径,需要指定到具体的文件名
       'SourceFile' => 'localfile',
       // 设置分段大小
       'PartSize' => 5 * 1024 * 1024,
       // 设置分段的起始偏移大小
       'Offset' => 0
]);

printf("RequestId:%s\n",$resp['RequestId']);
printf("ETag:%s\n",$resp['ETag']);
  • 使用PartNumber参数指定分段号;使用UploadId参数指定分段上传任务的全局唯一标识;使用SourceFile参数指定待上传的文件;使用PartSize参数指定分段大小;使用Offset参数指定分段的起始偏移大小。
  • 上传段接口要求除最后一段以外,其他的段大小都要大于100KB。但是上传段接口并不会立即校验上传段的大小(因为不知道是否为最后一段);只有调用合并段接口时才会校验。
  • OBS会将服务端收到段数据的ETag值(段数据的MD5值)返回给用户。
  • 可以通过ContentMD5参数设置上传数据的MD5值,提供给OBS服务端用于校验数据完整性。
  • 分段号的范围是1~10000。如果超出这个范围,OBS将返回400 Bad Request错误。
  • OBS 3.0的桶支持最小段的大小为100KB,OBS 2.0的桶支持最小段的大小为5MB。请在OBS 3.0的桶上执行分段上传操作。

合并段

所有分段上传完成后,需要调用合并段接口来在OBS服务端生成最终对象。在执行该操作时,需要提供所有有效的分段列表(包括分段号和分段ETag值);OBS收到提交的分段列表后,会逐一验证每个段的有效性。当所有段验证通过后,OBS将把这些分段组合成最终的对象。

您可以通过ObsClient->completeMultipartUpload合并段:

// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
// 创建ObsClient实例
$obsClient = new ObsClient ( [ 
      //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
      //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
      'key' => getenv('ACCESS_KEY_ID'),
      'secret' => getenv('SECRET_ACCESS_KEY'),
      'endpoint' => 'https://your-endpoint'
] );

$resp = $obsClient->completeMultipartUpload([
       'Bucket' => 'bucketname',
       'Key' => 'objectname',
       // 设置Upload ID
       'UploadId' => 'upload id from initiateMultipartUpload',
       'Parts' => [
                     ['PartNumber' => 1, 'ETag' => 'etag value from uploadPart']
       ]
]);

printf("RequestId:%s\n",$resp['RequestId']);
  • 如果最后一个段之外的其它段尺寸过小(小于100KB),OBS返回400 Bad Request。
  • 使用UploadId参数指定分段上传任务的全局唯一标识;使用Parts参数指定分段号与分段ETag值的列表,该列表必须按分段号升序排列。
  • 分段可以是不连续的。

取消分段上传任务

分段上传任务可以被取消,当一个分段上传任务被取消后,就不能再使用其Upload ID做任何操作,已经上传段也会被OBS删除。

采用分段上传方式上传对象过程中或上传对象失败后会在桶内产生段,这些段会占用您的存储空间,您可以通过取消该分段上传任务来清理掉不需要的段,节约存储空间。

您可以通过ObsClient->abortMultipartUpload取消分段上传任务:

// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
// 创建ObsClient实例
$obsClient = new ObsClient ( [ 
      //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
      //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
      'key' => getenv('ACCESS_KEY_ID'),
      'secret' => getenv('SECRET_ACCESS_KEY'),
      'endpoint' => 'https://your-endpoint'
] );

$resp = $obsClient->abortMultipartUpload([
       'Bucket' => 'bucketname',
       'Key' => 'objectname',
       // 设置Upload ID
       'UploadId' => 'upload id from initiateMultipartUpload'
]);

printf("RequestId:%s\n",$resp['RequestId']);

列举已上传的段

您可使用ObsClient->listParts列举出某一分段上传任务所有已经上传成功的段。

该接口可设置的参数如下:

参数

作用

UploadId

分段上传任务全局唯一标识,从ObsClient->initiateMultipartUpload返回的结果获取。

MaxParts

表示列举已上传段的返回结果最大段数目,即分页时每一页中段数目。

PartNumberMarker

表示列举已上传段的起始位置,只有Part Number大于该参数的段会被列出。

  • 简单列举
// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
// 创建ObsClient实例
$obsClient = new ObsClient ( [ 
      //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
      //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
      'key' => getenv('ACCESS_KEY_ID'),
      'secret' => getenv('SECRET_ACCESS_KEY'),
      'endpoint' => 'https://your-endpoint'
] );

$resp = $obsClient->listParts ( [ 
      'Bucket' => 'bucketname',
      'Key' => 'objectname',
      'UploadId' => 'upload id from initiateMultipartUpload' 
] );

printf ( "RequestId:%s\n", $resp ['RequestId'] );
foreach ( $resp ['Parts'] as $index => $part ) {
       printf ( "Parts[%d]\n", $index + 1 );
       // 分段号,上传时候指定
       printf ( "PartNumber:%s\n", $part ['PartNumber'] );
       // 段的最后上传时间
       printf ( "LastModified:%s\n", $part ['LastModified'] );
       // 分段的ETag值
       printf ( "ETag:%s\n", $part ['ETag'] );
       // 段数据大小
       printf ( "Size:%s\n", $part ['Size'] );
}
  • 列举段至多返回1000个段信息,如果指定的Upload ID包含的段数量大于1000,则返回结果中IsTruncated为true表明本次没有返回全部段,并可通过NextPartNumberMarker获取下次列举的起始位置。
  • 如果想获取指定Upload ID包含的所有分段,可以采用分页列举的方式。
  • 列举所有段

由于ObsClient->listParts只能列举至多1000个段,如果段数量大于1000,列举所有分段请参考如下示例:

// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
// 创建ObsClient实例
$obsClient = new ObsClient ( [ 
      //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
      //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
      'key' => getenv('ACCESS_KEY_ID'),
      'secret' => getenv('SECRET_ACCESS_KEY'),
      'endpoint' => 'https://your-endpoint'
] );

$partNumberMarker = null;
$index = 1;
do{
       $resp = $obsClient->listParts ( [ 
             'Bucket' => 'bucketname',
             'Key' => 'objectname',
             'UploadId' => 'upload id from initiateMultipartUpload',
             'PartNumberMarker' => $partNumberMarker
       ] );
       
       printf ( "RequestId:%s\n", $resp ['RequestId'] );
       foreach ( $resp ['Parts'] as $part ) {
              printf ( "Parts[%d]\n", $index );
              // 分段号,上传时候指定
              printf ( "PartNumber:%s\n", $part ['PartNumber'] );
              // 段的最后上传时间
              printf ( "LastModified:%s\n", $part ['LastModified'] );
              // 分段的ETag值
              printf ( "ETag:%s\n", $part ['ETag'] );
              // 段数据大小
              printf ( "Size:%s\n", $part ['Size'] );
              $index ++;
       }
       
       $partNumberMarker = $resp['NextPartNumberMarker'];
       
}while($resp['IsTruncated']);

列举分段上传任务

您可以通过ObsClient->listMultipartUploads列举分段上传任务。列举分段上传任务可设置的参数如下:

参数

作用

Prefix

限定返回的分段上传任务中的对象名必须带有Prefix前缀。

Delimiter

用于对分段上传任务中的对象名进行分组的字符。对于对象名中包含Delimiter的任务,其对象名(如果请求中指定了Prefix,则此处的对象名需要去掉Prefix)中从首字符至第一个Delimiter之间的字符串将作为一个分组并作为CommonPrefix返回。

MaxUploads

列举分段上传任务的最大数目,取值范围为1~1000,当超出范围时,按照默认的1000进行处理。

KeyMarker

表示列举时返回指定的KeyMarker之后的分段上传任务。

UploadIdMarker

只有与KeyMarker参数一起使用时才有意义,用于指定返回结果的起始位置,即列举时返回指定KeyMarker的UploadIdMarker之后的分段上传任务。

  • 简单列举分段上传任务
// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
// 创建ObsClient实例
$obsClient = new ObsClient ( [ 
      //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
      //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
      'key' => getenv('ACCESS_KEY_ID'),
      'secret' => getenv('SECRET_ACCESS_KEY'),
      'endpoint' => 'https://your-endpoint'
] );

$resp = $obsClient->listMultipartUploads ( [ 
      'Bucket' => 'bucketname' 
] );

printf ( "RequestId:%s\n", $resp ['RequestId'] );
foreach ( $resp ['Uploads'] as $index => $upload ) {
       printf ( "Uploads[%d]\n", $index + 1 );
       printf ( "Key:%s\n", $upload ['Key'] );
       printf ( "UploadId:%s\n", $upload ['UploadId'] );
       printf ( "Initiated:%s\n", $upload ['Initiated'] );
       printf ( "Owner[ID]:%s\n", $upload ['Owner'] ['ID'] );
       printf ( "StorageClass:%s\n", $upload ['StorageClass'] );
}
  • 列举分段上传任务至多返回1000个任务信息,如果指定的桶包含的分段上传任务数量大于1000,则返回结果中IsTruncated为true表明本次没有返回全部结果,并可通过NextKeyMarker和NextUploadIdMarker获取下次列举的起点。
  • 如果想获取指定桶包含的所有分段上传任务,可以采用分页列举的方式。
  • 列举全部分段上传任务
// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
// 创建ObsClient实例
$obsClient = new ObsClient ( [ 
      //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
      //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
      'key' => getenv('ACCESS_KEY_ID'),
      'secret' => getenv('SECRET_ACCESS_KEY'),
      'endpoint' => 'https://your-endpoint'
] );

$keyMarker = null;
$uploadIdMarker = null;
$index = 1;
do{
       $resp = $obsClient->listMultipartUploads ( [ 
             'Bucket' => 'bucketname',
             'KeyMarker' => $keyMarker,
             'UploadIdMarker' => $uploadIdMarker
       ] );
       
       printf ( "RequestId:%s\n", $resp ['RequestId'] );
       foreach ( $resp ['Uploads'] as $index => $upload ) {
              printf ( "Uploads[%d]\n", $index );
              printf ( "Key:%s\n", $upload ['Key'] );
              printf ( "UploadId:%s\n", $upload ['UploadId'] );
              printf ( "Initiated:%s\n", $upload ['Initiated'] );
              printf ( "Owner[ID]:%s\n", $upload ['Owner'] ['ID'] );
              printf ( "StorageClass:%s\n", $upload ['StorageClass'] );
              $index ++;
       }
       $keyMarker = $resp['NextKeyMarker'];
       $uploadIdMarker = $resp['NextUploadIdMarker'];
}while ($resp['IsTruncated']);

相关文档