AWSS3 iOS SDK使用教程

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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<AWSS3TransferUtilityMultiPartUploadTask *> * _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<AWSS3TransferUtilityMultiPartUploadTask *> * _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

分片上传逻辑
  1. 初始化分片上传事件: 调用initiateMultipartUpload方法返回OSS创建的全局唯一的uploadId
  2. 上传分片: 调用uploadPart方法上传分片数据
  3. 完成分片上传: 所有分片上传完成后调用completeMultipartUpload方法将所有分片合并成完整的文件。
  4. 列举正在上传的分片
  5. 取消分片上传: 调用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<AWSS3CompletedPart *> *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

  1. 更改contentSha256,设置为 contentSha256 = [AWSSignatureSignerUtility hexEncode:[[NSString alloc] initWithData:[AWSSignatureSignerUtility hashData:[urlRequest HTTPBody]] encoding:NSASCIIStringEncoding]];
  2. 更改设置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;
}

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6