AWSS3 iOS SDK 使用教程

创建AWSServiceConfiguration

根据文档注释,有两种创建方式

方式一:自定义创建ServiceConfiguration

根据AK/SK/token创建

AWSBasicSessionCredentialsProvider *provider = [[AWSBasicSessionCredentialsProvider alloc]

initWithAccessKey:access_key secretKey:secret_key sessionToken:token];

AWSRegionType regionType = [AWSEndpoint regionTypeFromName:config.region];

AWSEndpoint *endpoint = [[AWSEndpoint alloc] initWithRegion:regionType

service:AWSServiceS3 URL:[NSURL URLWithString:config.endpoint]];

AWSServiceConfiguration *serviceConfig = [[AWSServiceConfiguration alloc]

initWithRegion:regionType endpoint:endpoint credentialsProvider:provider];

方式二: 根据IdentityPoolId创建

AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc]

initWithRegionType:AWSRegionUSEast1 identityPoolId:@"YourIdentityPoolId"];

AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc]

initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];

获取上传对象

两种方式获取 AWSS3TransferUtility

方式1: 获取默认服务

[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;

AWSS3TransferUtility *S3TransferUtility = [AWSS3TransferUtility

defaultS3TransferUtility:^(NSError * _Nullable error) { }];

// 或

AWSS3TransferUtility *S3TransferUtility = [AWSS3TransferUtility defaultS3TransferUtility];

方式2:

[AWSS3TransferUtility registerS3TransferUtilityWithConfiguration:configuration

forKey:@"USWest2S3TransferUtility"];

// 获取注册结果

[AWSS3TransferUtility registerS3TransferUtilityWithConfiguration:configuration

forKey:@"USWest2S3TransferUtility" completionHandler:^(NSError * _Nullable error) {

}];

AWSS3TransferUtility *S3TransferUtility = [AWSS3TransferUtility

S3TransferUtilityForKey:@"USWest2S3TransferUtility"];

移除上传对象 AWSS3TransferUtility

+ (void)removeS3TransferUtilityForKey:(NSString *)key; 在上传任务上传完毕后,进行移除. 可以监听AWSS3TransferUtilityURLSessionDidBecomeInvalidNotification后,进行移除.

使用AWSS3TransferUtility进行单文件上传

支持NSData、NSUrl两种方式上传

// 上传进度

AWSS3TransferUtilityUploadExpression *expression =

[AWSS3TransferUtilityUploadExpression new];

expression.progressBlock = ^(AWSS3TransferUtilityTask *task, NSProgress *progress) {

!progressBlock ?: progressBlock(progress);

};

// AWSS3TransferUtility对象

AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility

S3TransferUtilityForKey:key];

// 上传NSData

[[transferUtility uploadData:data bucket:bucket key:key contentType:contentType

expression:expression completionHandler:^(AWSS3TransferUtilityUploadTask * _Nonnull task, NSError * _Nullable error) {

// 完成回调

!completionHandler ?: completionHandler(error);

}] continueWithBlock:^id(AWSTask *task) {

if (task.error) {

NSLog(@"AWSTask.error: %@", task.error);

}

if (task.result) {

NSLog(@"AWSTask.result: %@", task.result);

}

return nil;

}];

// 上传URL

[[transferUtility uploadFile:fileUrl bucket:bucket key:key contentType:contentType

expression:expression completionHandler:^(AWSS3TransferUtilityUploadTask * _Nonnull task, NSError * _Nullable error) {

// 完成回调

!completionHandler ?: completionHandler(error);

}] continueWithBlock:^id(AWSTask *task) {

if (task.error) {

DLog(@"AWSTask.error: %@", task.error);

}

if (task.result) {

DLog(@"AWSTask.result: %@", task.result);

}

return nil;

}];

使用AWSS3TransferUtility进行大文件分片上传

支持NSData、NSUrl两种方式上传

// 上传进度

AWSS3TransferUtilityMultiPartUploadExpression *multipartExpression =

[AWSS3TransferUtilityMultiPartUploadExpression new];

multipartExpression.progressBlock = ^(AWSS3TransferUtilityMultiPartUploadTask * _Nonnull task, NSProgress * _Nonnull progress){

!progressBlock ?: progressBlock(progress);

};

// 上传对象

AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility

S3TransferUtilityForKey:key];

// NSData 上传

[[transferUtility uploadDataUsingMultiPart:data bucket:bucket key:key contentType:contentType

expression:multipartExpression completionHandler:^(AWSS3TransferUtilityMultiPartUploadTask * _Nonnull task, NSError * _Nullable error) {

!completionHandler ?: completionHandler(error);

}] continueWithBlock:^id _Nullable(AWSTask * _Nonnull task) {

if (task.error) {

NSLog(@"AWSTask.error: %@", task.error);

}

if (task.result) {

NSLog(@"AWSTask.result: %@", task.result);

}

return nil;

}];

// NSUrl上传

[[transferUtility uploadFileUsingMultiPart:fileUrl bucket:bucket key:key contentType:contentType

expression:multipartExpression completionHandler:^(AWSS3TransferUtilityMultiPartUploadTask * _Nonnull task, NSError * _Nullable error) {

!completionHandler ?: completionHandler(error);

}] continueWithBlock:^id _Nullable(AWSTask * _Nonnull task) {

if (task.error) {

NSLog(@"AWSTask.error: %@", task.error);

}

if (task.result) {

NSLog(@"AWSTask.result: %@", task.result);

}

return nil;

}];

以上方法支持:七牛/腾讯云,不支持阿里云

使用AWSS3

获取AWSS3

[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;

AWSS3 *S3 = [AWSS3 defaultS3];

// 或

[AWSS3 registerS3WithConfiguration:configuration forKey:@"USWest2S3"

AWSS3 *S3 = [AWSS3 S3ForKey:@"USWest2S3"];

移除AWSS3

+ (void)removeS3ForKey:(NSString *)key; 全部上传任务完成后,才可进行移除.

使用AWSS3 单文件上传

AWSS3PutObjectRequest *request = [[AWSS3PutObjectRequest alloc] init];

request.key = key;

request.body = data;

request.bucket = bucket;

// 上传进度

request.uploadProgress = ^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend){

// totalBytes: data.length 数据的总长度

// totalBytesSent: 已完成的长度

NSProgress *progress = [[NSProgress alloc] init];

progress.totalUnitCount = (int64_t)data.length;

progress.completedUnitCount = totalBytesSent;

!progressBlock ?: progressBlock(progress);

};

[S3 putObject:request completionHandler:^(AWSS3PutObjectOutput * _Nullable response, NSError * _Nullable error) {

!completionHandler ?: completionHandler(error);

}];

此方法支持:七牛/腾讯云,支持阿里云,需更改认证方式

使用AWSS3 大文件分片上传

AWSS3TransferUtility 大文件分片上传底层同此方法. 系统默认每片大小: 5 x 1024 x 1024

分片上传逻辑

初始化分片上传事件: 调用initiateMultipartUpload方法返回OSS创建的全局唯一的uploadId上传分片: 调用uploadPart方法上传分片数据完成分片上传: 所有分片上传完成后,调用completeMultipartUpload方法将所有分片合并成完整的文件。列举正在上传的分片取消分片上传: 调用abortMultipartUpload方法来取消分片上传事件。 当一个分片上传事件被取消后,无法再使用这个uploadId做任何操作,已经上传的分片数据会被删除。

详细示例

// AWSUploadConfig: 自定义对象

- (void)AWSS3UploadMultipartData:(NSData *)data config:(AWSUploadConfig *)config progressBlock:(void(^)(NSProgress *progress))progressBlock completionHandler:(void (^)(NSError *error))completionHandler {

// 获取serviceConfig/注册/获取AWSS3

AWSServiceConfiguration *serviceConfig = [self createAWSServiceConfigByUploadConfig:config];

[AWSS3 registerS3WithConfiguration:serviceConfig forKey:config.key];

AWSS3 *S3 = [AWSS3 S3ForKey:config.key];

// 1.初始化分片上传事件: 调用[[AWSS3CreateMultipartUploadRequest alloc] init]方法返回OSS创建的全局唯一的uploadId

AWSS3CreateMultipartUploadRequest *createRequest = [[AWSS3CreateMultipartUploadRequest alloc] init];

createRequest.bucket = config.bucket;

createRequest.key = config.key;

WS(weakSelf);

[S3 createMultipartUpload:createRequest completionHandler:^(AWSS3CreateMultipartUploadOutput * _Nullable response, NSError * _Nullable error) {

if (error) {

NSLog(@"createMultipartUpload 创建失败: %@", error);

} else {

config.uploadId = response.uploadId;

NSLog(@"createMultipartUpload 创建成功: %@", config.uploadId);

[weakSelf AWSS3:S3 uploadPartData:data config:config progressBlock:^(NSProgress *progress) {

!progressBlock ?: progressBlock(progress);

} complete:^(NSArray *parts) {

if (parts.count > 0) {

[weakSelf AWSS3:S3 multipartUploadParts:parts config:config complete:^(NSError *error) {

!completionHandler ?: completionHandler(error);

}];

}

}];

}

}];

}

/// 2.上传分片: 调用uploadPart方法上传分片数据

- (void)AWSS3:(AWSS3 *)S3 uploadPartData:(NSData *)data config:(AWSUploadConfig *)config progressBlock:(void(^)(NSProgress *progress))progressBlock complete:(void(^)(NSArray *parts))complete {

// 默认每片5M

NSInteger partSize = 5 * 1024 * 1024;

NSUInteger chunkSize = ceil((float)data.length /(unsigned long) partSize);

__block NSInteger location = 0;

__block NSMutableArray *parts = [NSMutableArray array];

int64_t totalBytes = data.length;

__block int64_t completeBytes = 0;

for (int i = 1; location < data.length; i++) {

partSize = MIN(partSize, data.length - location);

NSData *partData = [data subdataWithRange:NSMakeRange(location, partSize)];

AWSS3UploadPartRequest *uploadRequest = [[AWSS3UploadPartRequest alloc] init];

uploadRequest.bucket = config.bucket;

uploadRequest.key = config.key;

uploadRequest.uploadId = config.uploadId;

uploadRequest.partNumber = @(i);

uploadRequest.body = partData;

uploadRequest.uploadProgress = ^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {

completeBytes += bytesSent;

NSProgress *progress = [[NSProgress alloc] init];

progress.totalUnitCount = totalBytes;

progress.completedUnitCount = completeBytes;

!progressBlock ?: progressBlock(progress);

};

[S3 uploadPart:uploadRequest completionHandler:^(AWSS3UploadPartOutput * _Nullable response, NSError * _Nullable error) {

if (error) {

NSLog(@"uploadPart 失败: %@", error);

} else {

AWSS3CompletedPart *part = [[AWSS3CompletedPart alloc] init];

part.ETag = response.ETag;

part.partNumber = @(i);

[parts addObject:part];

if (parts.count == chunkSize) {

// 分片块 要求升序排列

[parts sortUsingComparator:^NSComparisonResult(AWSS3CompletedPart *obj1, AWSS3CompletedPart *obj2) {

if ([obj1.partNumber integerValue] < [obj2.partNumber integerValue]) {

return NSOrderedAscending;

} else {

return NSOrderedDescending;

}

}];

!complete ?: complete(parts);

}

}

}];

location += partSize;

}

}

/// 3.完成分片上传: 所有分片上传完成后,调用completeMultipartUpload方法将所有分片合并成完整的文件

- (void)AWSS3:(AWSS3 *)S3 multipartUploadParts:(NSArray *)parts config:(AWSUploadConfig *)config complete:(void (^)(NSError *error))complete {

AWSS3CompletedMultipartUpload *multipartUpload = [[AWSS3CompletedMultipartUpload alloc] init];

multipartUpload.parts = parts;

AWSS3CompleteMultipartUploadRequest *completeRequest = [[AWSS3CompleteMultipartUploadRequest alloc] init];

completeRequest.bucket = config.bucket;

completeRequest.key = config.key;

completeRequest.uploadId = config.uploadId;

completeRequest.multipartUpload = multipartUpload;

[S3 completeMultipartUpload:completeRequest completionHandler:^(AWSS3CompleteMultipartUploadOutput * _Nullable response, NSError * _Nullable error) {

if (error) {

NSLog(@"completeMultipartUpload 失败: %@", error);

} else {

// 移除AWSS3

[S3 removeS3ForKey:config.key];

NSLog(@"completeMultipartUpload 成功: %@", response.location);

!complete ?: complete(error);

}

}];

}

/// 4.列举正在上传的分片

- (void)AWSS3:(AWSS3 *)S3 abortMultipartUploadConfig:(AWSUploadConfig *)config {

AWSS3ListMultipartUploadsRequest *listRequest = [[AWSS3ListMultipartUploadsRequest alloc] init];

listRequest.bucket = config.bucket;

[S3 listMultipartUploads:listRequest completionHandler:^(AWSS3ListMultipartUploadsOutput * _Nullable response, NSError * _Nullable error) {

if (error) {

NSLog(@"listMultipartUploads 上传失败: %@", error);

} else {

NSInteger count = response.uploads.count;

NSLog(@"listMultipartUploads: %ld", count);

/// 5. 取消分片上传

for (AWSS3MultipartUpload *upload in response.uploads) {

AWSS3AbortMultipartUploadRequest *abortRequest = [[AWSS3AbortMultipartUploadRequest alloc] init];

abortRequest.bucket = config.bucket;

abortRequest.key = config.key;

abortRequest.uploadId = upload.uploadId;

[S3 abortMultipartUpload:abortRequest completionHandler:^(AWSS3AbortMultipartUploadOutput * _Nullable response, NSError * _Nullable error) {

if (error) {

NSLog(@"abortMultipartUpload 失败: %@", error);

} else {

NSLog(@"abortMultipartUpload 成功");

}

}];

}

}

}];

}

此方法支持七牛、阿里云(需更改认证方式),不支持腾讯云

下载文件

/// 下载文件

- (void)AWSS3:(AWSS3 *)S3 getObjectWithFilePath:(NSString *)filePath config:(AWSUploadConfig *)config {

AWSS3GetObjectRequest *request = [[AWSS3GetObjectRequest alloc] init];

request.key = config.key;

request.bucket = config.bucket;

request.downloadingFileURL = [NSURL fileURLWithPath:filePath];

[S3 getObject:request completionHandler:^(AWSS3GetObjectOutput * _Nullable response, NSError * _Nullable error) {

}];

}

更改AWSS3签名认证支持阿里云上传

在AWSSignature.m文件中, 更改- (NSString *)signS3RequestV4:(NSMutableURLRequest *)urlRequest credentials:(AWSCredentials *)credentials方法中的生成的authorization

更改contentSha256,设置为 contentSha256 = [AWSSignatureSignerUtility hexEncode:[[NSString alloc] initWithData:[AWSSignatureSignerUtility hashData:[urlRequest HTTPBody]] encoding:NSASCIIStringEncoding]];更改设置HTTPBodyStream: [urlRequest setHTTPBodyStream:stream];

具体如下:

- (NSString *)aliyun_signS3RequestV4:(NSMutableURLRequest *)urlRequest

credentials:(AWSCredentials *)credentials {

if ([urlRequest valueForHTTPHeaderField:@"Content-Type"] == nil) {

[urlRequest addValue:@"binary/octet-stream" forHTTPHeaderField:@"Content-Type"];

}

NSDate *date = [NSDate aws_clockSkewFixedDate];

NSString *dateStamp = [date aws_stringValue:AWSDateShortDateFormat1];

NSString *scope = [NSString stringWithFormat:@"%@/%@/%@/%@", dateStamp, self.endpoint.regionName, self.endpoint.serviceName, AWSSignatureV4Terminator];

NSString *signingCredentials = [NSString stringWithFormat:@"%@/%@", credentials.accessKey, scope];

// compute canonical request

NSString *httpMethod = urlRequest.HTTPMethod;

// URL.path returns unescaped path

// For S3, url-encoded URI need to be decoded before generate CanonicalURI, otherwise, signature doesn't match occurs. (I.e. CanonicalURI for "/ios-v2-test-445901470/name%3A" will still be "/ios-v2-test-445901470/name%3A". "%3A" -> ":" -> "%3A")

NSString *cfPath = (NSString*)CFBridgingRelease(CFURLCopyPath((CFURLRef)urlRequest.URL));

NSString *path = [cfPath aws_stringWithURLEncodingPath];

if (path.length == 0) {

path = [NSString stringWithFormat:@"/"];

}

NSString *query = urlRequest.URL.query;

if (query == nil) {

query = [NSString stringWithFormat:@""];

}

NSUInteger contentLength = [[urlRequest allHTTPHeaderFields][@"Content-Length"] integerValue];

if (contentLength == 0) {

[urlRequest setValue:nil forHTTPHeaderField:@"Content-Length"];

} else {

[urlRequest setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[[urlRequest HTTPBody] length]] forHTTPHeaderField:@"Content-Length"];

}

// aliyun 上传更改

// Compute contentSha256

NSString *contentSha256 = [AWSSignatureSignerUtility hexEncode:[[NSString alloc] initWithData:[AWSSignatureSignerUtility hashData:[urlRequest HTTPBody]] encoding:NSASCIIStringEncoding]];

//[request.urlRequest setValue:dateTime forHTTPHeaderField:@"X-Amz-Date"];

[urlRequest setValue:contentSha256 forHTTPHeaderField:@"x-amz-content-sha256"];

//Set Content-MD5 header field if required by server.

if (([ urlRequest.HTTPMethod isEqualToString:@"PUT"] && ([[[urlRequest URL] query] hasPrefix:@"tagging"] ||

[[[urlRequest URL] query] hasPrefix:@"lifecycle"] ||

[[[urlRequest URL] query] hasPrefix:@"cors"]))

|| ([urlRequest.HTTPMethod isEqualToString:@"POST"] && [[[urlRequest URL] query] hasPrefix:@"delete"])

) {

if (![urlRequest valueForHTTPHeaderField:@"Content-MD5"]) {

[urlRequest setValue:[NSString aws_base64md5FromData:urlRequest.HTTPBody] forHTTPHeaderField:@"Content-MD5"];

}

}

NSMutableDictionary *headers = [[urlRequest allHTTPHeaderFields] mutableCopy];

NSString *canonicalRequest = [AWSSignatureV4Signer getCanonicalizedRequest:httpMethod

path:path

query:query

headers:headers

contentSha256:contentSha256];

AWSDDLogVerbose(@"Canonical request: [%@]", canonicalRequest);

NSString *stringToSign = [NSString stringWithFormat:@"%@\n%@\n%@\n%@",

AWSSignatureV4Algorithm,

[urlRequest valueForHTTPHeaderField:@"X-Amz-Date"],

scope,

[AWSSignatureSignerUtility hexEncode:[AWSSignatureSignerUtility hashString:canonicalRequest]]];

AWSDDLogVerbose(@"AWS4 String to Sign: [%@]", stringToSign);

NSData *kSigning = [AWSSignatureV4Signer getV4DerivedKey:credentials.secretKey

date:dateStamp

region:self.endpoint.regionName

service:self.endpoint.serviceName];

NSData *signature = [AWSSignatureSignerUtility sha256HMacWithData:[stringToSign dataUsingEncoding:NSUTF8StringEncoding] withKey:kSigning];

NSString *signatureString = [AWSSignatureSignerUtility hexEncode:[[NSString alloc] initWithData:signature encoding:NSASCIIStringEncoding]];

NSString *authorization = [NSString stringWithFormat:@"%@ Credential=%@, SignedHeaders=%@, Signature=%@",

AWSSignatureV4Algorithm,

signingCredentials,

[AWSSignatureV4Signer getSignedHeadersString:headers],

signatureString];

NSInputStream *stream = [urlRequest HTTPBodyStream];

if (nil != stream) {

// 阿里云更改:取消分块上传

[urlRequest setHTTPBodyStream:stream];

}

return authorization;

}

文档参考

AWS源码地址 AWS示例代码地址 官方中文文档 阿里云关于AWSS3的开发指南 七牛关于AWSS3的开发指南 紫光云文档 这里面有AWS的一些说明和API介绍。

文章来源

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: