
# 分段上传
![](https://support.huaweicloud.com/sdk-dotnet-devg-obs/public_sys-resources/notice_3.0-zh-cn.png)
开发过程中，您有任何问题可以在github上[提交issue](https://github.com/huaweicloud/huaweicloud-sdk-dotnet-obs/issues)，或者在[华为云对象存储服务论坛](https://bbs.huaweicloud.com/forum/forum-620-1.html)中发帖求助。[接口参考文档](https://obssdk.obs.cn-north-1.myhuaweicloud.com/apidoc/cn/dotnet/html/G_OBS.htm)详细介绍了每个接口的参数和使用方法。
对于较大文件上传，可以切分成段上传。用户可以在如下的应用场景内（但不仅限于此），使用分段上传的模式：
- 上传超过100MB大小的文件。
- 网络条件较差，和OBS服务端之间的链接经常断开。
- 上传前无法确定将要上传文件的大小。
分段上传分为如下3个步骤：
1. 初始化分段上传任务（ObsClient.InitiateMultipartUpload）。
2. 逐个或并行上传段（ObsClient.UploadPart）。
3. 合并段（ObsClient.CompleteMultipartUpload）或取消分段上传任务(ObsClient.AbortMultipartUpload)。
#### 初始化分段上传任务
使用分段上传方式传输数据前，必须先通知OBS初始化一个分段上传任务。该操作会返回一个OBS服务端创建的全局唯一标识（UploadId），用于标识本次分段上传任务。您可以根据这个唯一标识来发起相关的操作，如取消分段上传任务、列举分段上传任务、列举已上传的段等。
您可以通过ObsClient.InitiateMultipartUpload初始化一个分段上传任务。
![](https://support.huaweicloud.com/sdk-dotnet-devg-obs/public_sys-resources/note_3.0-zh-cn.png)
- 用InitiateMultipartUploadRequest指定上传对象的名称和所属桶。
- 在InitiateMultipartUploadRequest中，您可以设置对象MIME类型、对象存储类型、对象自定义元数据等对象属性。
- InitiateMultipartUploadResponse.UploadId返回分段上传任务的全局唯一标识（UploadId），在后面的操作中将用到它。
 
#### 上传段
初始化一个分段上传任务之后，可以根据指定的对象名和UploadId来分段上传数据。每一个上传的段都有一个标识它的号码------分段号（PartNumber，范围是1\~10000）。对于同一个UploadId，该分段号不但唯一标识这一段数据，也标识了这段数据在整个对象内的相对位置。如果您用同一个分段号上传了新的数据，那么OBS上已有的这个段号的数据将被覆盖。除了最后一段以外，其他段的大小范围是100KB\~5GB；最后段大小范围是0\~5GB。每个段不需要按顺序上传，甚至可以在不同进程、不同机器上上传，OBS会按照分段号排序组成最终对象。
您可以通过ObsClient.UploadPart上传段。
![](https://support.huaweicloud.com/sdk-dotnet-devg-obs/public_sys-resources/note_3.0-zh-cn.png)
- 上传段接口要求除最后一段以外，其他的段大小都要大于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将把这些分段组合成最终的对象。
您可以通过ObsClient.CompleteMultipartUpload合并段。
![](https://support.huaweicloud.com/sdk-dotnet-devg-obs/public_sys-resources/note_3.0-zh-cn.png)
分段可以是不连续的。
示例代码如下：
```
// 初始化配置参数
ObsConfig config = new ObsConfig();
config.Endpoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
string accessKey= Environment.GetEnvironmentVariable("AccessKeyID", EnvironmentVariableTarget.Machine);
string secretKey= Environment.GetEnvironmentVariable("SecretAccessKey", EnvironmentVariableTarget.Machine);
// 创建ObsClient实例
ObsClient client = new ObsClient(accessKey, secretKey, config);
try
{
    // 1. 初始化分段上传任务
    InitiateMultipartUploadRequest initiateRequest = new InitiateMultipartUploadRequest
    {
        BucketName = "bucketname",
        ObjectKey = "objectname"
    };
    InitiateMultipartUploadResponse initResponse = client.InitiateMultipartUpload(initiateRequest);
    Console.WriteLine("InitiateMultipartUpload status: {0}", initResponse.StatusCode);
    Console.WriteLine("InitiateMultipartUpload UploadId: {0}", initResponse.UploadId);
    // 2. 上传段
    string filePath = "localfile";//上传的本地文件路径，需要指定到具体的文件名
    long contentLength = new FileInfo(filePath).Length;
    long partSize = 15 * (long)Math.Pow(2, 20); // 每个段大小15 MB
    List<UploadPartResponse> uploadResponses = new List<UploadPartResponse>();
    long filePosition = 0;
    for (int i = 1; filePosition < contentLength; i++)
    {
        UploadPartRequest uploadRequest = new UploadPartRequest
        {
            BucketName = "bucketname",
            ObjectKey = "objectname",
            UploadId = initResponse.UploadId,
            PartNumber = i,
            PartSize = partSize,
            Offset = filePosition,
            FilePath = filePath
        };
        uploadResponses.Add(client.UploadPart(uploadRequest));
        Console.WriteLine("UploadPart count: {0}", uploadResponses.Count);
        filePosition += partSize;
    }
    // 3.合并段
    CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest()
    {
        BucketName = "bucketname",
        ObjectKey = "objectname",
        UploadId = initResponse.UploadId,
    };
    completeRequest.AddPartETags(uploadResponses);
    CompleteMultipartUploadResponse completeUploadResponse = client.CompleteMultipartUpload(completeRequest);
    Console.WriteLine("CompleteMultipartUpload status: {0}", completeUploadResponse.StatusCode);
}
catch (ObsException ex)
{
    Console.WriteLine("Exception:{0}", ex.ErrorCode);
    Console.WriteLine("Exception Message:{0}", ex.ErrorMessage);
}
```
#### 取消分段上传任务
分段上传任务可以被取消，当一个分段上传任务被取消后，就不能再使用其UploadId做任何操作，已经上传段也会被OBS服务端删除。
采用分段上传方式上传对象过程中或上传对象失败后会在桶内产生段，这些段会占用您的存储空间，您可以通过取消该分段上传任务来清理掉不需要的段，节约存储空间。
您可以通过ObsClient.AbortMultipartUpload取消分段上传任务，示例代码如下：
```
// 初始化配置参数
ObsConfig config = new ObsConfig();
config.Endpoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
string accessKey= Environment.GetEnvironmentVariable("AccessKeyID", EnvironmentVariableTarget.Machine);
string secretKey= Environment.GetEnvironmentVariable("SecretAccessKey", EnvironmentVariableTarget.Machine);
// 创建ObsClient实例
ObsClient client = new ObsClient(accessKey, secretKey, config);
//取消分段上传任务
try
{
    AbortMultipartUploadRequest request = new AbortMultipartUploadRequest
    {
        BucketName = "bucketname",
        ObjectKey = "objectname",
        UploadId = "uploadId",//待取消的分段上传任务的Id
    };
    AbortMultipartUploadResponse response = client.AbortMultipartUpload(request);
    Console.WriteLine("Abort multipart upload response: {0}", response.StatusCode);
}
catch (ObsException ex)
{
    Console.WriteLine("ErrorCode: {0}", ex.ErrorCode);
    Console.WriteLine("ErrorMessage: {0}", ex.ErrorMessage);
}
```
#### 列举已上传的段
您可使用ObsClient.ListParts列举出某一分段上传任务所有已经上传成功的段。
该接口可设置的参数如下：
| **参数**           | **作用**                                        | **OBS .NET SDK对应属性**              |
|:---|:---|:---|
| BucketName       | 分段上传任务所属的桶名。                                  | ListPartsRequest.BucketName       |
| ObjectKey        | 分段上传任务所属的对象名。                                 | ListPartsRequest.ObjectKey        |
| UploadId         | 分段上传任务全局唯一标识，从InitiateMultipartUpload返回的结果获取。 | ListPartsRequest.UploadId         |
| MaxParts         | 表示列举已上传的段返回结果最大段数目，即分页时每一页中段数目。               | ListPartsRequest.MaxParts         |
| PartNumberMarker | 表示待列出段的起始位置，只有Part Number大于该参数的段会被列出。         | ListPartsRequest.PartNumberMarker |
   
- 简单列举
```
// 初始化配置参数
ObsConfig config = new ObsConfig();
config.Endpoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
string accessKey= Environment.GetEnvironmentVariable("AccessKeyID", EnvironmentVariableTarget.Machine);
string secretKey= Environment.GetEnvironmentVariable("SecretAccessKey", EnvironmentVariableTarget.Machine);
// 创建ObsClient实例
ObsClient client = new ObsClient(accessKey, secretKey, config);
// 简单列举
try
{
    ListPartsRequest request = new ListPartsRequest();
    request.BucketName = "bucketname";
    request.ObjectKey = "objectname";
    request.UploadId = "uploadId";
    ListPartsResponse response = client.ListParts(request);
    Console.WriteLine("List parts response: {0}", response.StatusCode);
    foreach (PartDetail part in response.Parts)
    {
        Console.WriteLine("PartNumber: " + part.PartNumber);
        Console.WriteLine("Size: " + part.Size);
        Console.WriteLine("ETag: " + part.ETag);
        Console.WriteLine("LastModified: " + part.LastModified);
    }
}
catch (ObsException ex)
{
    Console.WriteLine("ErrorCode: {0}", ex.ErrorCode);
    Console.WriteLine("ErrorMessage: {0}", ex.ErrorMessage);
}
```
![](https://support.huaweicloud.com/sdk-dotnet-devg-obs/public_sys-resources/note_3.0-zh-cn.png)
列举段至多返回1000个段信息，如果指定的Upload ID包含的段数量大于1000，则返回结果中ListPartsResult.isTruncated为true表明本次没有返回全部段，并可通过ListPartsRespon.NextPartNumberMarker获取下次列举的起始位置。
- 列举所有段
由于ObsClient.ListParts只能列举至多1000个段，如果段数量大于1000，列举所有分段请参考如下示例：
```
// 初始化配置参数
ObsConfig config = new ObsConfig();
config.Endpoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
string accessKey= Environment.GetEnvironmentVariable("AccessKeyID", EnvironmentVariableTarget.Machine);
string secretKey= Environment.GetEnvironmentVariable("SecretAccessKey", EnvironmentVariableTarget.Machine);
// 创建ObsClient实例
ObsClient client = new ObsClient(accessKey, secretKey, config);
// 列举所有段
try
{
    ListPartsRequest request = new ListPartsRequest();
    request.BucketName = "bucketname";
    request.ObjectKey = "objectname";
    request.UploadId = "uploadId";
    ListPartsResponse response;
    do
    {
        response = client.ListParts(request);
        Console.WriteLine("List parts response: {0}", response.StatusCode);
        foreach (PartDetail part in response.Parts)
        {
            Console.WriteLine("PartNumber: " + part.PartNumber);
            Console.WriteLine("Size: " + part.Size);
            Console.WriteLine("ETag: " + part.ETag);
            Console.WriteLine("LastModified: " + part.LastModified);
        }
        request.PartNumberMarker = response.NextPartNumberMarker;
    }
    while (response.IsTruncated);
}
catch (ObsException ex)
{
    Console.WriteLine("ErrorCode: {0}", ex.ErrorCode);
    Console.WriteLine("ErrorMessage: {0}", ex.ErrorMessage);
}
```
#### 列举分段上传任务
您可以通过ObsClient.ListMultipartUploads列举分段上传任务。列举分段上传任务可设置的参数如下：
| **参数**         | **作用**                                                                                                                             | **OBS .NET SDK对应属性**                       |
|:---|:---|:---|
| BucketName     | 桶名。                                                                                                                                | ListMultipartUploadsRequest.BucketName     |
| Prefix         | 限定返回的分段上传任务中的对象名必须带有prefix前缀。                                                                                                      | ListMultipartUploadsRequest.Prefix         |
| Delimiter      | 用于对分段上传任务中的对象名进行分组的字符。对于对象名中包含Delimiter的任务，其对象名（如果请求中指定了Prefix，则此处的对象名需要去掉Prefix）中从首字符至第一个Delimiter之间的字符串将作为一个分组并作为CommonPrefix返回。 | ListMultipartUploadsRequest.Delimiter      |
| MaxUploads     | 列举分段上传任务的最大数目，取值范围为1\~1000，当超出范围时，按照默认的1000进行处理。                                                                                   | ListMultipartUploadsRequest.MaxUploads     |
| KeyMarker      | 表示列举时返回指定的keyMarker之后的分段上传任务。                                                                                                      | ListMultipartUploadsRequest.KeyMarker      |
| UploadIdMarker | 只有与KeyMarker参数一起使用时才有意义，用于指定返回结果的起始位置，即列举时返回指定KeyMarker的UploadIdMarker之后的分段上传任务。                                                   | ListMultipartUploadsRequest.UploadIdMarker |
   
- 简单列举分段上传任务
```
// 初始化配置参数
ObsConfig config = new ObsConfig();
config.Endpoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
string accessKey= Environment.GetEnvironmentVariable("AccessKeyID", EnvironmentVariableTarget.Machine);
string secretKey= Environment.GetEnvironmentVariable("SecretAccessKey", EnvironmentVariableTarget.Machine);
// 创建ObsClient实例
ObsClient client = new ObsClient(accessKey, secretKey, config);
//简单列举分段上传任务
try
{
    ListMultipartUploadsRequest listMultipartUploadsRequest = new ListMultipartUploadsRequest();
    listMultipartUploadsRequest.BucketName = "bucketname";
    ListMultipartUploadsResponse listMultipartUploadsResponse = client.ListMultipartUploads(listMultipartUploadsRequest);
    Console.WriteLine("ListMultipartUploadsResponse status code: " + listMultipartUploadsResponse.StatusCode);
    foreach (MultipartUpload upload in listMultipartUploadsResponse.MultipartUploads)
    {
        Console.WriteLine("ObjectKey {0}: " , upload.ObjectKey);
        Console.WriteLine("Initiated {0}: " , upload.Initiated);
        Console.WriteLine("\n");
    }
}
catch (ObsException ex)
{
    Console.WriteLine("ErrorCode: {0}", ex.ErrorCode);
    Console.WriteLine("ErrorMessage: {0}", ex.ErrorMessage);
}
```
![](https://support.huaweicloud.com/sdk-dotnet-devg-obs/public_sys-resources/note_3.0-zh-cn.png)
- 列举分段上传任务至多返回1000个任务信息，如果指定的桶包含的分段上传任务数量大于1000，则ListMultipartUploadsResponse.isTruncated为true表明本次没有返回全部结果，并可通过ListMultipartUploadsResponse.NextKeyMarker和ListMultipartUploadsResponse.NextUploadIdMarker获取下次列举的起点。
- 如果想获取指定桶包含的所有分段上传任务，可以采用分页列举的方式。
 
- 分页列举全部分段上传任务
```
// 初始化配置参数
ObsConfig config = new ObsConfig();
config.Endpoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
string accessKey= Environment.GetEnvironmentVariable("AccessKeyID", EnvironmentVariableTarget.Machine);
string secretKey= Environment.GetEnvironmentVariable("SecretAccessKey", EnvironmentVariableTarget.Machine);
// 创建ObsClient实例
ObsClient client = new ObsClient(accessKey, secretKey, config);
//列举全部分段上传任务
try
{
    ListMultipartUploadsRequest request = new ListMultipartUploadsRequest();
    request.BucketName = "bucketname";
    ListMultipartUploadsResponse response;
    do
    {
        response = client.ListMultipartUploads(request);
        Console.WriteLine("ListMultipartUploadsResponse status code: " + response.StatusCode);
        foreach (MultipartUpload upload in response.MultipartUploads)
        {
            Console.WriteLine("ObjectKey {0}: " , upload.ObjectKey);
            Console.WriteLine("Initiated {0}: " , upload.Initiated);
            Console.WriteLine("\n");
        }
        request.KeyMarker = response.NextKeyMarker;
        request.UploadIdMarker = response.NextUploadIdMarker;
    }
    while (response.IsTruncated);
}
catch (ObsException ex)
{
    Console.WriteLine("ErrorCode: {0}", ex.ErrorCode);
    Console.WriteLine("ErrorMessage: {0}", ex.ErrorMessage);
}
```
