4. 文件上传¶
在NOS中用户的基本操作单元是对象,亦可以理解为文件,NOS CPP SDK提供了丰富的上传接口,可以通过以下的方式上传文件:
- 流式上传(包含文件上传)
- 分块上传
流式上传、单块上传最大为100M;分块上传除最后一块外,分块不能小于16k,每一个分块不能大于100M,最多能上传10000个分块,即分块上传的文件最大支持1T。
4.1. 流式上传(包含文件上传)¶
您可以使用S3Client.PutObject上传一个一个Stream中的内容,具体实现如下:
//input_data为想要上传的内容的Stream,可以是Aws::StringStream,Aws::FStream,通过使用Aws::MakeShared<<#typename T#>>(<#const char *allocationTag#>, <#ArgTypes &&args...#>)来构建
void putObjectFromContent(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName,const std::shared_ptr<Aws::IOStream>& input_data){
Aws::S3::Model::PutObjectRequest object_request;
object_request.WithBucket(bucketName).WithKey(objectName);
auto input_data = Aws::MakeShared<Aws::StringStream>("PutObjectInputStream",content);
object_request.SetBody(input_data);
auto put_object_outcome = s3Client.PutObject(object_request);
if (put_object_outcome.IsSuccess())
{
std::cout << "Done!" << std::endl;
}
else
{
std::cout << "PutObject error: " <<
put_object_outcome.GetError().GetExceptionName() << " " <<
put_object_outcome.GetError().GetMessage() << std::endl;
}
}
//上传本地文件input_data的构造方式
auto input_file = Aws::MakeShared<Aws::FStream>("test",file_name.c_str(), std::ios_base::in | std::ios_base::binary);
//上传内存对象
auto input_content = Aws::MakeShared<Aws::StringStream>("test","hello world");
Attention
- 上传的流内容不超过100M
4.2. 分块上传¶
除了通过putObject接口上传文件到NOS之外,NOS还提供了另外一种上传模式-分块上传,用户可以在如下应用场景内(但不限于此),使用分块上传模式,如:
- 需支持断点上传
- 上传超过100M的文件
- 网络条件较差,经常和NOS服务器断开连接
- 上传文件之前无法确定文件的大小
分块上传一般流程如下所示:
- 初始化一个分块上传任务(createMultipartRequest)
- 上传分块(UploadPart)
- 完成分块上传(CompleteMultipartUpload)或者取消分块上传(AbortMultipartUpload)
4.2.1. 初始化分块上传¶
您可以使用S3Client.InitiateMultipartUpload初始化分块上传,具体实现如下:
Aws::String initMultipart(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName){
Aws::S3::Model::CreateMultipartUploadRequest createMultipartRequest;
createMultipartRequest.WithBucket(bucketName).WithKey(objectName);
//1. init
auto createMultiPartResult = s3Client.CreateMultipartUpload(createMultipartRequest);
if(createMultiPartResult.IsSuccess()){
std::cout << "createMultipartUpload ok, uploadId = " << createMultiPartResult.GetResult().GetUploadId() << std::endl;
return createMultiPartResult.GetResult().GetUploadId();
} else {
std::cout << "Create MultipartUpload failed" << createMultiPartResult.GetError().GetMessage() << std::endl;
return "";
}
}
4.2.2. 上传分块¶
您可以使用S3Client.UploadPart上传分块,具体实现如下:
Aws::Vector<Aws::S3::Model::CompletedPart> uploadParts(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName,const Aws::String& uploadId,const Aws::String& uploadFilePath){
long partSize = 5 * 1024 * 1024;
std::fstream file(uploadFilePath.c_str(),std::ios::in | std::ios::binary);
file.seekg(0,std::ios::end);
long fileSize = file.tellg();
std::cout << file.tellg() << std::endl;
file.seekg(0, std::ios::beg);
char* buffer = new char[partSize];
long filePosition = 0;
Aws::Vector<Aws::S3::Model::CompletedPart> completeParts;
int partNumber = 1;
while(filePosition < fileSize){
partSize = std::min(partSize,(fileSize - filePosition));
file.read(buffer,partSize);
std::cout << "readSize : " << partSize << std::endl;
Aws::S3::Model::UploadPartRequest uploadPartRequest;
uploadPartRequest.WithBucket(bucketName).WithKey(objectName).WithUploadId(uploadId).WithPartNumber(partNumber).WithContentLength(partSize);
Aws::String str(buffer,partSize);
auto input_data = Aws::MakeShared<Aws::StringStream>("UploadPartStream",str);
uploadPartRequest.SetBody(input_data);
filePosition += partSize;
auto uploadPartResult = s3Client.UploadPart(uploadPartRequest);
std::cout << uploadPartResult.GetResult().GetETag() << std::endl;
completeParts.push_back(Aws::S3::Model::CompletedPart().WithETag(uploadPartResult.GetResult().GetETag()).WithPartNumber(partNumber));
memset(buffer, 0, partSize);
++partNumber;
}
return completeParts;
}
4.2.3. 完成分块上传¶
您可以使用S3Client.CompleteMultipartUpload完成上传分块,具体实现如下:
void completeMultipart(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName,const Aws::String& uploadId,const Aws::Vector<Aws::S3::Model::CompletedPart>& completedParts){
Aws::S3::Model::CompleteMultipartUploadRequest complete;
complete.WithBucket(bucketName).WithKey(objectName).
WithUploadId(uploadId).
WithMultipartUpload(Aws::S3::Model::CompletedMultipartUpload().WithParts(completedParts));
auto completeMultipartUploadResult = s3Client.CompleteMultipartUpload(complete);
if( completeMultipartUploadResult.IsSuccess()){
std::cout << "done" << std::endl;
} else {
Aws::S3::Model::AbortMultipartUploadRequest abortRequest;
abortRequest.WithBucket(bucketName).WithKey(objectName).WithUploadId(uploadId);
s3Client.AbortMultipartUpload(abortRequest);
std::cout << "multipartUpload failed" << std::endl;
}
}
4.2.4. 分块上传示例¶
下面通过一个完整的示例说明了如何进行分片上传操作,可以参考下面代码:
void multipartUpload(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName,const Aws::String& uploadFilePath){
//1. init
const Aws::String uploadId = initMultipart(s3Client, bucketName, objectName);
//2. uploadPart
Aws::Vector<Aws::S3::Model::CompletedPart> completeParts = uploadParts(s3Client, bucketName, objectName, uploadId, uploadFilePath);
//3. complete multipart upload
completeMultipart(s3Client,bucketName,objectName,uploadId,completeParts);
}
Attention
- 上面程序一共分为三个步骤:1. initiate 2. uploadPart 3. complete
- UploadPart 方法要求除最后一个Part以外,其他的Part大小都要大于或等于16K。但是Upload Part接口并不会立即校验上传Part的大小(因为不知道是否为最后一块);只有当Complete Multipart Upload的时候才会校验。
- Part号码的范围是1~10000。如果超出这个范围,NOS将返回InvalidArgument的错误码。
- 分块上传除最后一块外,分块不能小于16k,每一个分块不能大于100M。
- 每次上传Part时都要把流定位到此次上传块开头所对应的位置。
- 分片上传任务初始化或上传部分分片后,可以使用abortMultipartUpload接口中止分片上传事件。当分片上传事件被中止后,就不能再使用这个Upload ID做任何操作,已经上传的分片数据也会被删除。
- 每次上传Part之后,NOS的返回结果会包含一个 PartETag 对象,它是上传块的ETag与块编号(PartNumber)的组合。在后续完成分片上传的步骤中会用到它,因此我们需要将其保存起来,然后在第三步complete的时候使用,具体操作参考上面代码。
4.2.5. 取消分块上传¶
您可以使用S3Client.AbortMultipartUpload取消上传事件,具体实现如下:
void abortMultipartUpload(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName,const Aws::String& uploadId){
Aws::S3::Model::AbortMultipartUploadRequest abortRequest;
abortRequest.WithBucket(bucketName).WithKey(objectName).WithUploadId(uploadId);
auto result = s3Client.AbortMultipartUpload(abortRequest);
if (result.IsSuccess()) {
std::cout << "abort succ" << std::endl;
} else {
std::cout << "abort failed" << std::endl;
}
}
4.2.6. 查看已经上传的分片¶
查看已经上传的分片可以罗列出指定Upload ID(InitiateMultipartUpload时获取)所属的所有已经上传成功的分片,您可以通过S3Client.ListParts接口获取已经上传的分片,可以参考以下代码:
void ListParts(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName,const Aws::String& uploadId){
Aws::S3::Model::ListPartsRequest listPartRequest;
listPartRequest.WithBucket(bucketName).WithKey(objectName).WithUploadId(uploadId).WithMaxParts(10).WithPartNumberMarker(10);
auto result = s3Client.ListParts(listPartRequest);
if (result.IsSuccess()) {
for(auto part : result.GetResult().GetParts()){
std::cout << part.GetPartNumber() << "-->" << part.GetETag() << std::endl;
}
} else {
std::cout << "list part request faield" << std::endl;
}
}
查看已经上传的分片可以指定以下参数:
4.2.7. 查看当前正在进行的分片上传任务¶
查看正在进行的分片上传任务可以罗列出正在进行,还未完成的分片上传任务,您可以通过S3Client.ListMultipartUploads接口当前的上传任务,可以参考以下代码:
void listMultipartUpload(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName){
Aws::S3::Model::ListMultipartUploadsRequest listMultipartUploadRequest;
listMultipartUploadRequest.WithBucket(bucketName);
auto result = s3Client.ListMultipartUploads(listMultipartUploadRequest);
if (result.IsSuccess()) {
for(auto upload : result.GetResult().GetUploads()){
std::cout << upload.GetKey() << std::endl;
}
} else {
std::cout << "failed" << std::endl;
}
}
上述代码中使用的options参数如下:
参数值 | 描述 |
---|---|
KeyMarker | 指定某一uploads key,只有大于该KeyMarker的才会被列出 |
MaxUploads | 最多返回MaxUploads条记录,默认1000 |
4.3. 设置文件元信息¶
文件元数据(object meta),是上传到NOS的文件属性描述信息,CPP SDK只支持设置用户自定义元数据。文件元信息可以在各种上传方式(流式上传、单块上传、分块上传)时进行设置。
4.3.1. 设定自定义http header¶
下面的源代码实现了上传文件时设置Http header:
void putObjectFromContentWithMeta(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName,const Aws::String& content,const Aws::Map<Aws::String, Aws::String>& metas){
Aws::S3::Model::PutObjectRequest object_request;
object_request.WithBucket(bucketName).WithKey(objectName);
// Binary files must also have the std::ios_base::bin flag or'ed in
auto input_data = Aws::MakeShared<Aws::StringStream>("PutObjectInputStream",content);
object_request.SetBody(input_data);
object_request.SetMetadata(metas);
auto put_object_outcome = s3Client.PutObject(object_request);
if (put_object_outcome.IsSuccess())
{
std::cout << "Done!" << std::endl;
}
else
{
std::cout << "PutObject error: " <<
put_object_outcome.GetError().GetExceptionName() << " " <<
put_object_outcome.GetError().GetMessage() << std::endl;
}
}
Attention
- 各种上传方式(包括流式上传、单块上传、分块上传)都可以设置元数据信息。流式上传设置元数据的方式与单块上传相同
- 文件的元信息可以通过S3Client.GetObjectMetadata获取;
- user meta的名称以”x-amz-meta-“开头,例如设置为:’x-amz-meta-name’,读取名字为x-amz-meta-name的参数即可
4.4. 获取文件元信息¶
下面的源代码实现了获取对象的自定义原信息:
void getObjectMeta(const Aws::S3::S3Client& s3Client,const Aws::String& bucketName,const Aws::String& objectName){
Aws::S3::Model::HeadObjectRequest headObjectRequest;
headObjectRequest.WithBucket(bucketName).WithKey(objectName);
auto result = s3Client.HeadObject(headObjectRequest);
if (result.IsSuccess()) {
for(auto entry : result.GetResult().GetMetadata()){
std::cout << entry.first << " -- > " << entry.second << std::endl;
}
} else {
std::cout << "failed" << std::endl;
}
}