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

分段上传

用户可以在如下的应用场景内(但不仅限于此),使用分段上传的模式:

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

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

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

初始化分段上传任务

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

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

static OBSClient *client;
NSString *endPoint = @"your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];

// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
    
// 初始化多段上传任务
OBSInitiateMultipartUploadRequest *request = [[OBSInitiateMultipartUploadRequest alloc]initWithBucketName:@"bucketname" objectKey:@"objectname"];
    
[client initiateMultipartUpload:request completionHandler:^(OBSInitiateMultipartUploadResponse *response, NSError *error) {
    NSLog(@"%@",response);
}];
  • response.uploadID返回分段上传任务的全局唯一标识(Upload ID),在后面的操作中将用到它。
  • 在OBSInitiateMultipartUploadRequest中,您可以设置对象存储类型、对象自定义元数据等对象属性。同时您还可以通过customContentType参数来设置对象上传类型。
  • 设置background为YES时,可以开启后台上传。

上传段

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

您可以通过uploadPart上传段:

static OBSClient *client;
NSString *endPoint = @"your-endpoint";

// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];

// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
   
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"fileName" ofType:@"Type"];
// 第一段
OBSUploadPartWithFileRequest* fileRequest_first =
[[OBSUploadPartWithFileRequest alloc]initWithBucketName:@"bucket-ios-test03"
                                                  objectkey:@"MultiPart"
                                                 partNumber:[NSNumber numberWithInt:1]
                                                   uploadID:@"uploadID"
                                             uploadFilePath:filePath];
// 开启后台上传,当应用退出到后台后,上传任务仍然会进行
fileRequest_first.background = YES; 
    
[client uploadPart:fileRequest_first completionHandler:^(OBSUploadPartResponse *response, NSError *error) {
    NSLog(@"第一段");
}];
    
// 第二段
OBSUploadPartWithFileRequest* fileRequest_sec =
[[OBSUploadPartWithFileRequest alloc]initWithBucketName:@"bucket-ios-test03"
                                                  objectkey:@"MultiPart"
                                                 partNumber:[NSNumber numberWithInt:2]
                                                   uploadID:@"uploadID"
                                             uploadFilePath:filePath];
    
[client uploadPart:fileRequest_sec completionHandler:^(OBSUploadPartResponse *response, NSError *error) {
    NSLog(@"第二段");
}];
  • 上传段接口要求除最后一段以外,其他的段大小都要大于100KB。但是上传段接口并不会立即校验上传段的大小(因为不知道是否为最后一块);只有调用合并段接口时才会校验。
  • OBS会将服务端收到段数据的ETag值(段数据的MD5值)返回给用户。
  • 分段号的范围是1~10000。如果超出这个范围,OBS将返回400 Bad Request错误。
  • OBS 3.0的桶支持最小段的大小为100KB,OBS 2.0的桶支持最小段的大小为5MB。请在OBS 3.0的桶上执行分段上传操作。

合并段

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

您可以通过completeMultipartUpload合并段:

static OBSClient *client;
NSString *endPoint = @"your-endpoint";

// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];

// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];

//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
    
// 列举段
__block NSMutableArray *partsList;
OBSListPartsRequest* listRequest = [[OBSListPartsRequest alloc]initWithBucketName:@"bucketname" objectKey:@"objectname" uploadID:@"uploadID"];
OBSBFTask *listTask = [client listParts:listRequest completionHandler:^(OBSListPartsResponse *response, NSError *error) {
    partsList = [response.partsList mutableCopy];
        
        // 合并段
    OBSCompleteMultipartUploadRequest* comRequest = [[OBSCompleteMultipartUploadRequest alloc]initWithBucketName:@"bucketname" objectKey:@"objectname" uploadID:@"uploadID"];
    comRequest.partsList = partsList;
   [client completeMultipartUpload:comRequest completionHandler:^(OBSCompleteMultipartUploadResponse *response, NSError *error) {
            NSLog(@"%@",response);
    }];
        
}];

取消分段上传任务

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

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

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

static OBSClient *client;
NSString *endPoint = @"your-endpoint";

// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];

// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
    
// 取消分段上传任务
OBSAbortMultipartUploadRequest *abortRequest = [[OBSAbortMultipartUploadRequest alloc] initWithBucketName:@"bucketname" objectKey:@"objectname" uploadID:@"uploadID"];
    
[client abortMultipartUpload:abortRequest completionHandler:^(OBSAbortMultipartUploadResponse *response, NSError *error) {
    NSLog(@"%@",response);
}];

列举已上传的段

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

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

参数

作用

OBS iOS SDK对应方法

bucketName

分段上传任务所属的桶名。

request.bucketName

objectKey

分段上传任务所属的对象名。

request.objectKey

uploadID

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

request.uploadID

maxParts

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

request.maxParts

partNumberMarker

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

request.partNumberMarker

  • 简单列举
static OBSClient *client;
NSString *endPoint = @"your-endpoint";
NSString *uploadID = @"upload id from OBSInitateMultipartUpload";
    
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];

// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
    
    
OBSListPartsRequest* listRequest = [[OBSListPartsRequest alloc]initWithBucketName:@"bucketname" objectKey:@"objectname" uploadID:uploadID];
[client listParts:listRequest completionHandler:^(OBSListPartsResponse *response, NSError *error) {
    NSLog(@"%@",response);
}];
  • 列举段至多返回1000个段信息,如果指定的Upload ID包含的段数量大于1000,则返回结果中response.isTruncated为YES表明本次没有返回全部段,并可通过response.getNextPartNumberMarker获取下次列举的起始位置。
  • 如果想获取指定Upload ID包含的所有分段,可以采用分页列举的方式。
  • 列举所有段

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

static OBSClient *client;
NSString *endPoint = @"your-endpoint";
NSString *uploadID = @"upload id from OBSInitateMultipartUpload";
    
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];

// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
    
    
__block OBSListPartsResponse *result;
    
OBSListPartsRequest* listRequest = [[OBSListPartsRequest alloc]initWithBucketName:@"bucketname" objectKey:@"objectname" uploadID:uploadID];
    
// 列举所有上传段
do {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    [client listParts:listRequest completionHandler:^(OBSListPartsResponse *response, NSError *error) {
        result = response;
        NSLog(@"%@",result);
        listRequest.partNumberMarker = result.nextPartNumberMarker;
        dispatch_semaphore_signal(sema);
    }];
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        
} while (result.isTruncated);

  • 分页列举所有段
上面的获取所有已上传段(每页1000个段)是分页的一种特殊情况。如果需要指定每页段的数量,请参考以下代码:
static OBSClient *client;
NSString *endPoint = @"your-endpoint";
NSString *uploadID = @"upload id from OBSInitateMultipartUpload";
    
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];

// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
    
 __block OBSListPartsResponse *result;
    
OBSListPartsRequest* listRequest = [[OBSListPartsRequest alloc]initWithBucketName:@"bucketname" objectKey:@"objectname" uploadID:uploadID];
listRequest.maxUploads = [NSNumber numberWithInt:100];
// 列举所有上传段
do {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    [client listParts:listRequest completionHandler:^(OBSListPartsResponse *response, NSError *error) {
       result = response;
        NSLog(@"%@",result);
        listRequest.partNumberMarker = result.nextPartNumberMarker;
        dispatch_semaphore_signal(sema);
    }];
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        
} while (result.isTruncated);

列举分段上传任务

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

参数

作用

OBS iOS SDK对应方法

bucketName

桶名。

request.bucketName = @"bucketname"

delimiter

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

request.delimiter = @"delimiter"

prefix

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

request.prefix = @"prefix"

maxUploads

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

request.maxUploads = [NSNumber numberWithInt:Int]

keyMarker

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

request.keyMarker = @"keymarker"

uploadIDMarker

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

request.uploadIDMarker = @"ifmarker"

  • 简单列举分段上传任务
static OBSClient *client;
NSString *endPoint = @"your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];

// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
    
// 列举分段上传任务
OBSListMultipartUploadsRequest *request = [[OBSListMultipartUploadsRequest alloc]initWithBucketName:@"bucketname"];
    
[client listMultipartUploads:request completionHandler:^(OBSListMultipartUploadsResponse *response, NSError *error) {
    NSLog(@"%@",response);
}];
  • 列举分段上传任务至多返回1000个任务信息,如果指定的桶包含的分段上传任务数量大于1000,则response.isTruncated为YES表明本次没有返回全部结果,并可通过response.nextKeyMarker和response.nextUploadIdMarker获取下次列举的起点。
  • 如果想获取指定桶包含的所有分段上传任务,可以采用分页列举的方式。
  • 列举全部分段上传任务
static OBSClient *client;
NSString *endPoint = @"your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];
    
// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
__block OBSListMultipartUploadsResponse *result;
OBSListMultipartUploadsRequest *request = [[OBSListMultipartUploadsRequest alloc]initWithBucketName:@"bucket-ios-test03"];
    
// 列举全部分段上传任务
do {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    [client listMultipartUploads:request completionHandler:^(OBSListMultipartUploadsResponse *response, NSError *error) {
        result = response;
        NSLog(@"%@",result);
        request.keyMarker = result.nextKeyMarker;
        request.uploadIDMarker = result.nextUploadIDMarker;
        dispatch_semaphore_signal(sema);
        }];
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
} while (result.isTruncated);

  • 分页列举全部分段上传任务

上面的获取所有分段上传任务(每页1000个任务)是分页的一种特殊情况。如果需要指定每页任务的数量,请参考以下代码:

static OBSClient *client;
NSString *endPoint = @"your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env];
NSString *SK = [NSString stringWithUTF8String:sk_env];
    
// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
    
    
__block OBSListMultipartUploadsResponse *result;
    
    
OBSListMultipartUploadsRequest *request = [[OBSListMultipartUploadsRequest alloc]initWithBucketName:@"bucketname"];
    
// 每页100个分段上传任务
request.maxUploads = [NSNumber numberWithInt:100];
    
// 列举全部分段上传任务
do {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    [client listMultipartUploads:request completionHandler:^(OBSListMultipartUploadsResponse *response, NSError *error) {
        result = response;
        NSLog(@"%@",result);
        request.keyMarker = result.nextKeyMarker;
        request.uploadIDMarker = result.nextUploadIDMarker;
        dispatch_semaphore_signal(sema);
            
    }];
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        
} while (result.isTruncated);

相关文档