OSS提供的分片上传(Multipart Upload)功能,将要上传的较大文件(Object)分成多个分片(Part)来分别上传,上传完成后再调用CompleteMultipartUpload接口将这些Part组合成一个Object来达到断点续传的效果。
注意事项
本文以华东1(杭州)外网Endpoint为例。如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见访问域名和数据中心。
本文以OSS域名新建OSSClient为例。如果您希望通过自定义域名、STS等方式新建OSSClient,请参见新建OssClient。
要分片上传,您必须有
oss:PutObject
权限。具体操作,请参见为RAM用户授权自定义的权限策略。
分片上传流程
分片上传(Multipart Upload)分为以下三个步骤:
初始化一个分片上传事件。
调用InitiateMultipartUpload方法返回OSS创建的全局唯一的uploadId。
上传分片。
调用UploadPart方法上传分片数据。
说明对于同一个uploadId,分片号(PartNumber)标识了该分片在整个文件内的相对位置。如果使用同一个分片号上传了新的数据,则OSS上该分片已有的数据将会被覆盖。
OSS将收到的分片数据的MD5值放在ETag头内返回给用户。
OSS计算上传数据的MD5值,并与SDK计算的MD5值比较,如果不一致则返回InvalidDigest错误码。
完成分片上传。
所有分片上传完成后,调用CompleteMultipartUpload方法将所有分片合并成完整的文件。
分片上传完整示例
以下通过一个完整的示例对分片上传的流程进行逐步解析:
#include <alibabacloud/oss/OssClient.h>
#include <fstream>
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 Endpoint = "yourEndpoint";
/* 填写Bucket名称,例如examplebucket */
std::string BucketName = "examplebucket";
/* 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。 */
std::string ObjectName = "exampledir/exampleobject.txt";
/* 初始化网络等资源 */
InitializeSdk();
ClientConfiguration conf;
/* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName);
/*(可选)请参见如下示例设置存储类型 */
//initUploadRequest.MetaData().addHeader("x-oss-storage-class", "Standard");
/* 初始化分片上传事件 */
auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest);
/* 根据UploadId执行取消分片上传事件或者列举已上传分片的操作。*/
/* 如果您需要根据您需要UploadId执行取消分片上传事件的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后获取uploadId。*/
/* 如果您需要根据您需要UploadId执行列举已上传分片的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后,且在调用CompleteMultipartUpload完成分片上传之前获取uploadId。*/
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;
}
}
/* 完成分片上传 */
/* 在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。*/
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;
return -1;
}
/* 释放网络等资源 */
ShutdownSdk();
return 0;
}
获取已经上传的分片过程中调用CompleteMultipartUpload接口时,需提供每个分片的ETag值。您可以通过以下两种方式获取ETag值:
上传每个分片时,返回结果中会包含这个分片的ETag值,供您直接保存使用。上述示例采用的是此种方式。
通过调用ListParts方法获取已经上传的分片的ETag值。
列举已上传的分片
调用ListParts方法列举出指定UploadID下所有已上传成功的分片。
默认情况下,简单列举已上传的分片只能一次列举1000个分片。当分片数量大于1000时,请选择分页列举所有已上传分片。
简单列举已上传的分片
以下代码用于简单列举已上传的分片:
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;
int main(void)
{
/* 初始化OSS账号信息 */
std::string Endpoint = "yourEndpoint";
/* 填写Bucket名称,例如examplebucket */
std::string BucketName = "examplebucket";
/* 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。*/
std::string ObjectName = "exampledir/exampleobject.txt";
/* 填写UploadId。UploadId来源于调用InitiateMultipartUpload完成初始化分片之后,且在调用CompleteMultipartUpload完成分片上传之前的返回结果。*/
std::string UploadId = "yourUploadId";
/* 初始化网络等资源 */
InitializeSdk();
ClientConfiguration conf;
/* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
/* 列举已上传分片,默认列举1000个分片 */
ListPartsRequest listuploadrequest(BucketName, ObjectName);
listuploadrequest.setUploadId(UploadId);
bool IsTruncated = false;
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());
IsTruncated = listUploadResult.result().IsTruncated();
} while (IsTruncated);
/* 释放网络等资源 */
ShutdownSdk();
return 0;
}
分页列举所有已上传的分片
以下代码用于指定每页分片的数量,并分页列举所有分片:
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;
int main(void)
{
/* 初始化OSS账号信息 */
std::string Endpoint = "yourEndpoint";
/* 填写Bucket名称,例如examplebucket */
std::string BucketName = "examplebucket";
/* 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。 */
std::string ObjectName = "exampledir/exampleobject.txt";
/* 填写UploadId。UploadId来源于调用InitiateMultipartUpload完成初始化分片之后,且在调用CompleteMultipartUpload完成分片上传之前的返回结果。*/
std::string UploadId = "yourUploadId";
/* 初始化网络等资源 */
InitializeSdk();
ClientConfiguration conf;
/* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
/* 分页列举全部上传的分片 */
/* 设置每页列举最大上传分片数目 */
ListPartsRequest listuploadrequest(BucketName, ObjectName);
listuploadrequest.setMaxParts(50);
listuploadrequest.setUploadId(UploadId);
bool IsTruncated = false;
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());
IsTruncated = listuploadresult.result().IsTruncated();
} while (IsTruncated);
/* 释放网络等资源 */
ShutdownSdk();
return 0;
}
列举分片上传事件
调用ListMultipartUploads方法列举所有执行中的分片上传事件,即已初始化但尚未完成或已取消的分片上传事件。
默认情况下,简单列举分片上传事件只能一次列举1000个分片上传事件。当分片数量大于1000时,请选择分页列举所有上传事件。
简单列举分片上传事件
以下代码用于简单列举分片上传事件。
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;
int main(void)
{
/* 初始化OSS账号信息 */
std::string Endpoint = "yourEndpoint";
/* 填写Bucket名称,例如examplebucket */
std::string BucketName = "examplebucket";
/* 初始化网络等资源 */
InitializeSdk();
ClientConfiguration conf;
/* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
/* 列举已上传事件,默认列举1000个分片 */
ListMultipartUploadsRequest listmultiuploadrequest(BucketName);
bool IsTruncated = false;
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());
IsTruncated = listresult.result().IsTruncated();
} while (IsTruncated);
/* 释放网络等资源 */
ShutdownSdk();
return 0;
}
分页列举所有上传事件
以下代码用于分页列举所有上传事件:
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;
int main(void)
{
/* 初始化OSS账号信息 */
std::string Endpoint = "yourEndpoint";
/* 填写Bucket名称,例如examplebucket */
std::string BucketName = "examplebucket";
/* 初始化网络等资源 */
InitializeSdk();
ClientConfiguration conf;
/* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
/* 分页列举全部上传事件 */
/* 设置每页列举最大上传事件数目 */
ListMultipartUploadsRequest listmultiuploadrequest(BucketName);
listmultiuploadrequest.setMaxUploads(50);
bool IsTruncated = false;
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());
IsTruncated = listresult.result().IsTruncated();
} while (IsTruncated);
/* 释放网络等资源 */
ShutdownSdk();
return 0;
}
取消分片上传事件
您可以调用client.AbortMultipartUpload方法来取消分片上传事件。当一个分片上传事件被取消后,无法再使用同一个uploadId执行任何操作,已经上传的分片数据会被删除。
以下代码用于取消分片上传事件:
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;
int main(void)
{
/* 初始化OSS账号信息 */
std::string Endpoint = "yourEndpoint";
/* 填写Bucket名称,例如examplebucket */
std::string BucketName = "examplebucket";
/* 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。 */
std::string ObjectName = "exampledir/exampleobject.txt";
/* 填写UploadId。UploadId来源于调用InitiateMultipartUpload完成初始化分片之后的返回结果。*/
std::string UploadId = "yourUploadId";
/* 初始化网络等资源 */
InitializeSdk();
ClientConfiguration conf;
/* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName);
/* 初始化分片上传事件 */
auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest);
auto uploadId = uploadIdResult.result().UploadId();
/* 取消分片上传 */
AbortMultipartUploadRequest abortUploadRequest(BucketName, ObjectName, uploadId);
auto outcome = client.AbortMultipartUpload(abortUploadRequest);
if (!outcome.isSuccess()) {
/* 异常处理 */
std::cout << "AbortMultipartUpload fail" <<
",code:" << outcome.error().Code() <<
",message:" << outcome.error().Message() <<
",requestId:" << outcome.error().RequestId() << std::endl;
return -1;
}
/* 释放网络等资源 */
ShutdownSdk();
return 0;
}
相关文档
关于分片上传的完整示例代码,请参见GitHub示例。
分片上传的完整实现涉及三个API接口,详情如下:
关于初始化分片上传事件的API接口说明,请参见InitiateMultipartUpload。
关于分片上传Part的API接口说明,请参见UploadPart。
关于完成分片上传的API接口说明,请参见CompleteMultipartUpload。
关于取消分片上传事件的API接口说明,请参见AbortMultipartUpload。
关于列举已上传分片的API接口说明,请参见ListParts。
关于列举所有执行中的分片上传事件(即已初始化但尚未完成或已取消的分片上传事件)的API接口说明,请参见ListMultipartUploads。