
# 断点续传下载
当下载大对象到本地文件时，经常出现因网络不稳定或程序崩溃导致下载失败的情况。失败后再次重新下载不仅浪费资源，而且当网络不稳定时仍然有下载失败的风险。断点续传下载接口能有效地解决此类问题引起的下载失败，其原理是将待下载的对象分成若干个分段分别下载，并实时地将每段下载结果统一记录在checkpoint文件中，仅当所有分段都下载成功时返回下载成功的结果，否则抛出异常提醒用户再次调用接口进行重新下载（重新下载时因为有checkpoint文件记录当前的下载进度，避免重新下载所有分段，从而节省资源提高效率）。
您可以通过downloadFile进行断点续传下载。该接口可设置的参数如下：
| **参数**               | **作用**                                          | **OBS iOS SDK对应方法**          |
|:---|:---|:---|
| bucketName           | 桶名，必选参数。                                        | request.bucketName           |
| objectKey            | 对象名，必选参数。                                       | request.objectKey            |
| downloadFilePath     | 下载对象的本地文件全路径。                                   | request.downloadFilePath     |
| versionID            | 对象的版本号。                                         | request.versionID            |
| enableCheckpoint     | 是否开启断点续传模式，默认为NO，表示不开启。                         | request.enableCheckpoint     |
| enableMD5Check       | 是否开启MD5校验。                                      | request.enableMD5Check       |
| enableForceOverwrite | 是否开启强制覆盖。                                       | request.enableForceOverwrite |
| checkpointFilePath   | 记录下载进度的文件，只在断点续传模式下有效。当该值为空时，默认与下载对象的本地文件路径同目录。 | request.checkpointFilePath   |
| partSize             | 分段大小，单位字节，取值范围是5MB\~5GB。                        | request.partSize             |
| ifModifiedSince      | 如果对象在指定的时间后有修改，则返回对象内容，否则返回错误。                  | request.ifModifiedSince      |
| ifUnmodifiedSince    | 如果对象在指定的时间后没有修改，则返回对象内容，否则返回错误。                 | request.ifUnmodifiedSince    |
| ifETagMatch          | 如果对象的ETag值与该参数值相同，则返回对象内容，否则抛出异常。               | request.ifETagMatch          |
| ifETagNoneMatch      | 如果对象的ETag值与该参数值不相同，则返回对象内容，否则抛出异常。              | request.ifETagNoneMatch      |
   
以下代码展示了如何使用断点续传下载接口下载对象到本地文件：
```
static OBSClient *client;
NSString *endPoint = @"https://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");
if (ak_env == NULL || sk_env == NULL) {
    NSLog(@"Error: AccessKeyID or SecretAccessKey environment variable not set");
    return;
}
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 * outfilePath = [NSTemporaryDirectory() stringByAppendingString:@"filename"];
// 最大并发数
client.configuration.maxConcurrentDownloadRequestCount = 5;
// 断点续传下载
OBSDownloadFileRequest *request = [[OBSDownloadFileRequest alloc]initWithBucketName:@"bucketname" objectKey:@"objectname" downloadFilePath:outfilePath];
    
// 是否打开强制覆盖
request.enableForceOverwrite = YES;
// 分段大小
request.partSize = [NSNumber numberWithInteger:5*1024*1024];
// 是否开启断点续传
request.enableCheckpoint = YES;
    
request.downloadProgressBlock = ^(int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
    NSLog(@"%0.1f%%",(float)floor((totalBytesExpectedToWrite > 0) ? (totalBytesWritten*10000/totalBytesExpectedToWrite) : 0)/100);
        
};
OBSBFTask  *task = [client downloadFile:request completionHandler:^(OBSDownloadFileResponse *response, NSError *error) {
    NSLog(@"%@",response);
}];
    
[task waitUntilFinished];
if(task.error){
    // 重新下载
}
```
