OSS提供的分片上传(Multipart Upload)功能,将要上传的较大文件(Object)分成多个数据块(Part)来分别上传,上传完成后再调用CompleteMultipartUpload接口将这些Part组合成一个Object来达到断点续传的效果。
分片上传流程
分片上传(Multipart Upload)分为以下三个步骤:
- 初始化一个分片上传事件。
调用InitiateMultipartUpload方法返回OSS创建的全局唯一的uploadId。
- 上传分片。
调用UploadPart方法上传分片数据。
说明- 对于同一个UploadId,分片号(PartNumber)标识了该分片在整个文件内的相对位置。如果使用同一个分片号上传了新的数据,那么OSS上这个分片已有的数据将会被覆盖。
- OSS将收到的分片数据的MD5值放在ETag头内返回给用户。
- OSS计算上传数据的MD5值,并与SDK计算的MD5值比较,如果不一致则返回InvalidDigest错误码。
- 完成分片上传。
所有分片上传完成后,调用CompleteMultipartUpload方法将所有分片合并成完整的文件。
分片上传完整示例
以下通过一个完整的示例对分片上传的流程进行逐步解析:
#include <alibabacloud/oss/OssClient.h> int64_t getFileSize(const std::string& file) { std::fstream f(file, std::ios::in | std::ios::binary); f.seekg(0, f.end); int64_t size = f.tellg(); f.close(); return size; } using namespace AlibabaCloud::OSS; int main(void) { /* 初始化OSS账号信息 */ std::string AccessKeyId = "yourAccessKeyId"; std::string AccessKeySecret = "yourAccessKeySecret"; std::string Endpoint = "yourEndpoint"; std::string BucketName = "yourBucketName"; std::string ObjectName = "yourObjectName"; /* 初始化网络等资源 */ InitializeSdk(); ClientConfiguration conf; OssClient client(Endpoint, AccessKeyId, AccessKeySecret, conf); InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName); /*(可选)请参见如下示例设置存储类型 */ //initUploadRequest.MetaData().addHeader("x-oss-storage-class", "Standard"); /* 初始化分片上传事件 */ auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest); auto uploadId = uploadIdResult.result().UploadId(); std::string fileToUpload = "yourLocalFilename"; int64_t partSize = 100 * 1024; PartList partETagList; auto fileSize = getFileSize(fileToUpload); int partCount = static_cast<int>(fileSize / partSize); /* 计算分片个数 */ if (fileSize % partSize != 0) { partCount++; } /* 对每一个分片进行上传 */ for (int i = 1; i <= partCount; i++) { auto skipBytes = partSize * (i - 1); auto size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes); std::shared_ptr<std::iostream> content = std::make_shared<std::fstream>(fileToUpload, std::ios::in|std::ios::binary); content->seekg(skipBytes, std::ios::beg); UploadPartRequest uploadPartRequest(BucketName, ObjectName, content); uploadPartRequest.setContentLength(size); uploadPartRequest.setUploadId(uploadId); uploadPartRequest.setPartNumber(i); auto uploadPartOutcome = client.UploadPart(uploadPartRequest); if (uploadPartOutcome.isSuccess()) { Part part(i, uploadPartOutcome.result().ETag()); partETagList.push_back(part); } else { std::cout << "uploadPart fail" << ",code:" << uploadPartOutcome.error().Code() << ",message:" << uploadPartOutcome.error().Message() << ",requestId:" << uploadPartOutcome.error().RequestId() << std::endl; } } /* 完成分片上传 */ CompleteMultipartUploadRequest request(BucketName, ObjectName); request.setUploadId(uploadId); request.setPartList(partETagList); /*(可选)请参见如下示例设置读写权限ACL */ //request.setAcl(CannedAccessControlList::Private); auto outcome = client.CompleteMultipartUpload(request); if (!outcome.isSuccess()) { /* 异常处理 */ std::cout << "CompleteMultipartUpload fail" << ",code:" << outcome.error().Code() << ",message:" << outcome.error().Message() << ",requestId:" << outcome.error().RequestId() << std::endl; ShutdownSdk(); return -1; } /* 释放网络等资源 */ ShutdownSdk(); return 0; }
获取已经上传的分片过程中调用CompleteMultipartUpload接口时,需提供每个分片的ETag值。您可以通过以下两种方式获取ETag值:
- 上传每个分片时,返回结果中会包含这个分片的ETag值,供您直接保存使用。上述示例采用的是此种方式。
- 通过调用ListParts方法获取已经上传的分片的ETag值。
列举已上传的分片
调用ListParts方法列举出指定UploadID下所有已上传成功的分片。
- 列举所有已上传的分片
说明 默认情况下,ListParts只能一次列举1000个分片。当分片数量大于1000时,请分页列举所有已上传分片。以下代码用于列举所有已上传的分片:
#include <alibabacloud/oss/OssClient.h> using namespace AlibabaCloud::OSS; int main(void) { /* 初始化OSS账号信息 */ std::string AccessKeyId = "yourAccessKeyId"; std::string AccessKeySecret = "yourAccessKeySecret"; std::string Endpoint = "yourEndpoint"; std::string BucketName = "yourBucketName"; std::string ObjectName = "yourObjectName"; /* 初始化网络等资源 */ InitializeSdk(); ClientConfiguration conf; OssClient client(Endpoint, AccessKeyId, AccessKeySecret, conf); /* 列举已上传分片,默认列举1000个分片 */ ListPartsRequest listuploadrequest(BucketName, ObjectName); listuploadrequest.setUploadId(uploadId); do { auto listuploadresult = client.ListParts(listuploadrequest); if (!listUploadResult.isSuccess()) { /* 异常处理 */ std::cout << "ListParts fail" << ",code:" << listuploadresult.error().Code() << ",message:" << listuploadresult.error().Message() << ",requestId:" << listuploadresult.error().RequestId() << std::endl; break; } else { for (const auto& part : listuploadresult.result().PartList()) { std::cout << "part"<< ",name:" << part.PartNumber() << ",size:" << part.Size() << ",etag:" << part.ETag() << ",lastmodify time:" << part.LastModified() << std::endl; } } listuploadrequest.setPartNumberMarker(listuploadresult.result().NextPartNumberMarker()); } while (listuploadresult.result().IsTruncated()); /* 释放网络等资源 */ ShutdownSdk(); return 0; }
- 分页列举所有已上传的分片
以下代码用于指定每页分片的数量,并分页列举所有分片:
#include <alibabacloud/oss/OssClient.h> using namespace AlibabaCloud::OSS; int main(void) { /* 初始化OSS账号信息 */ std::string AccessKeyId = "yourAccessKeyId"; std::string AccessKeySecret = "yourAccessKeySecret"; std::string Endpoint = "yourEndpoint"; std::string BucketName = "yourBucketName"; std::string ObjectName = "yourObjectName"; /* 初始化网络等资源 */ InitializeSdk(); ClientConfiguration conf; OssClient client(Endpoint, AccessKeyId, AccessKeySecret, conf); /* 分页列举全部上传的分片 */ /* 设置每页列举最大上传分片数目 */ ListPartsRequest listuploadrequest(BucketName, ObjectName); listuploadrequest.setMaxParts(50); listuploadrequest.setUploadId(uploadId); do { listuploadresult = client.ListParts(listuploadrequest); if (!listUploadResult.isSuccess()) { /* 异常处理 */ std::cout << "ListParts fail" << ",code:" << listuploadresult.error().Code() << ",message:" << listuploadresult.error().Message() << ",requestId:" << listuploadresult.error().RequestId() << std::endl; break; } else { for (const auto& part : listuploadresult.result().PartList()) { std::cout << "part"<< ",name:" << part.PartNumber() << ",size:" << part.Size() << ",etag:" << part.ETag() << ",lastmodify time:" << part.LastModified() << std::endl; } } listuploadrequest.setPartNumberMarker(listuploadresult.result().NextPartNumberMarker()); } while (listuploadresult.result().IsTruncated()); /* 释放网络等资源 */ ShutdownSdk(); return 0; }
列举分片上传事件
调用ListMultipartUploads方法列举出所有执行中的分片上传事件,即已初始化但尚未完成或已取消的分片上传事件。
- 列举全部分片上传事件
说明 默认情况下,ListMultipartUploads只能一次列举1000个分片上传事件。当分片数量大于1000时,请分页列举所有上传事件。
以下代码用于列举所有已上传事件:
#include <alibabacloud/oss/OssClient.h> using namespace AlibabaCloud::OSS; int main(void) { /* 初始化OSS账号信息 */ std::string AccessKeyId = "yourAccessKeyId"; std::string AccessKeySecret = "yourAccessKeySecret"; std::string Endpoint = "yourEndpoint"; std::string BucketName = "yourBucketName"; /* 初始化网络等资源 */ InitializeSdk(); ClientConfiguration conf; OssClient client(Endpoint, AccessKeyId, AccessKeySecret, conf); /* 列举已上传事件,默认列举1000个分片 */ ListMultipartUploadsRequest listmultiuploadrequest(BucketName); do { auto listresult = client.ListMultipartUploads(listmultiuploadrequest); if (!listresult.isSuccess()) { /* 异常处理 */ std::cout << "ListMultipartUploads fail" << ",code:" << listresult.error().Code() << ",message:" << listresult.error().Message() << ",requestId:" << listresult.error().RequestId() << std::endl; break; } else { for (const auto& part : listresult.result().MultipartUploadList()) { std::cout << "part"<< ",name:" << part.Key << ",uploadid:" << part.UploadId << ",initiated time:" << part.Initiated << std::endl; } } listmultiuploadrequest.setKeyMarker(listresult.result().NextKeyMarker()); listmultiuploadrequest.setUploadIdMarker(listresult.result().NextUploadIdMarker()); } while (listresult.result().IsTruncated()); /* 释放网络等资源 */ ShutdownSdk(); return 0; }
- 分页列举所有上传事件
以下代码用于分页列举所有上传事件:
#include <alibabacloud/oss/OssClient.h> using namespace AlibabaCloud::OSS; int main(void) { /* 初始化OSS账号信息 */ std::string AccessKeyId = "yourAccessKeyId"; std::string AccessKeySecret = "yourAccessKeySecret"; std::string Endpoint = "yourEndpoint"; std::string BucketName = "yourBucketName"; /* 初始化网络等资源 */ InitializeSdk(); ClientConfiguration conf; OssClient client(Endpoint, AccessKeyId, AccessKeySecret, conf); /* 分页列举全部上传事件 */ /* 设置每页列举最大上传事件数目 */ ListMultipartUploadsRequest listmultiuploadrequest(BucketName); listmultiuploadrequest.setMaxUploads(50); do { listresult = client.ListMultipartUploads(listmultiuploadrequest); if (!listresult.isSuccess()) { /* 异常处理 */ std::cout << "ListMultipartUploads fail" << ",code:" << listresult.error().Code() << ",message:" << listresult.error().Message() << ",requestId:" << listresult.error().RequestId() << std::endl; break; } else { for (const auto& part : listresult.result().MultipartUploadList()) { std::cout << "part"<< ",name:" << part.Key << ",uploadid:" << part.UploadId << ",initiated time:" << part.Initiated << std::endl; } } listmultiuploadrequest.setKeyMarker(listresult.result().NextKeyMarker()); listmultiuploadrequest.setUploadIdMarker(listresult.result().NextUploadIdMarker()); } while (listresult.result().IsTruncated()); /* 释放网络等资源 */ ShutdownSdk(); return 0; }
取消分片上传事件
您可以调用client.AbortMultipartUpload方法来取消分片上传事件。当一个分片上传事件被取消后,无法再使用同一个uploadId执行任何操作,已经上传的分片数据会被删除。
以下代码用于取消分片上传事件:
#include <alibabacloud/oss/OssClient.h> using namespace AlibabaCloud::OSS; int main(void) { /* 初始化OSS账号信息 */ std::string AccessKeyId = "yourAccessKeyId"; std::string AccessKeySecret = "yourAccessKeySecret"; std::string Endpoint = "yourEndpoint"; std::string BucketName = "yourBucketName"; std::string ObjectName = "yourObjectName"; /* 初始化网络等资源 */ InitializeSdk(); ClientConfiguration conf; OssClient client(Endpoint, AccessKeyId, AccessKeySecret, conf); InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName); /* 初始化分片上传事件 */ auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest); auto uploadId = uploadIdResult.result().UploadId(); /* 取消分片上传 */ AbortMultipartUploadRequest abortUploadRequest(BucketName, ObjectName, uploadId); auto abortUploadIdResult = client.AbortMultipartUpload(abortUploadRequest); if (!abortUploadIdResult.isSuccess()) { /* 异常处理 */ std::cout << "AbortMultipartUpload fail" << ",code:" << outcome.error().Code() << ",message:" << outcome.error().Message() << ",requestId:" << outcome.error().RequestId() << std::endl; ShutdownSdk(); return -1; } /* 释放网络等资源 */ ShutdownSdk(); return 0; }