diff --git a/AFAmazonS3Manager/AFAmazonS3Manager.h b/AFAmazonS3Manager/AFAmazonS3Manager.h index b93084f..2253ac3 100644 --- a/AFAmazonS3Manager/AFAmazonS3Manager.h +++ b/AFAmazonS3Manager/AFAmazonS3Manager.h @@ -91,12 +91,14 @@ Lists information about the objects in a bucket for a user that has read access to the bucket. @param bucket The S3 bucket to get. Must not be `nil`. + @param parameters The parameters for list objects, you can implement something such directory listing with it. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single argument: the response object from the server. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the `NSError` object describing error that occurred. @return The operation that was enqueued on operationQueue */ - (AFHTTPRequestOperation *)getBucket:(NSString *)bucket + parameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure; @@ -215,6 +217,15 @@ success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure; +/* same as putObjectWithFile but with NSData */ + +- (AFHTTPRequestOperation *)putObjectWithData:(NSData *)data + destinationPath:(NSString *)destinationPath + parameters:(NSDictionary *)parameters + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(void (^)(id responseObject))success + failure:(void (^)(NSError *error))failure; + /** Deletes the specified object. Once deleted, there is no method to restore or undelete an object. diff --git a/AFAmazonS3Manager/AFAmazonS3Manager.m b/AFAmazonS3Manager/AFAmazonS3Manager.m index 8075821..c8b07dc 100644 --- a/AFAmazonS3Manager/AFAmazonS3Manager.m +++ b/AFAmazonS3Manager/AFAmazonS3Manager.m @@ -25,10 +25,6 @@ NSString * const AFAmazonS3ManagerErrorDomain = @"com.alamofire.networking.s3.error"; -static NSString * AFPathByEscapingSpacesWithPlusSigns(NSString *path) { - return [path stringByReplacingOccurrencesOfString:@" " withString:@"+"]; -} - @interface AFAmazonS3Manager () @property (readwrite, nonatomic, strong) NSURL *baseURL; @end @@ -106,12 +102,13 @@ - (AFHTTPRequestOperation *)getServiceWithSuccess:(void (^)(id responseObject))s #pragma mark Bucket Operations - (AFHTTPRequestOperation *)getBucket:(NSString *)bucket + parameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure { NSParameterAssert(bucket); - return [self enqueueS3RequestOperationWithMethod:@"GET" path:bucket parameters:nil success:success failure:failure]; + return [self enqueueS3RequestOperationWithMethod:@"GET" path:bucket parameters:parameters success:success failure:failure]; } - (AFHTTPRequestOperation *)putBucket:(NSString *)bucket @@ -141,8 +138,6 @@ - (AFHTTPRequestOperation *)headObjectWithPath:(NSString *)path { NSParameterAssert(path); - path = AFPathByEscapingSpacesWithPlusSigns(path); - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, __unused id responseObject) { if (success) { @@ -166,8 +161,6 @@ - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path { NSParameterAssert(path); - path = AFPathByEscapingSpacesWithPlusSigns(path); - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { if (success) { @@ -194,8 +187,6 @@ - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path { NSParameterAssert(path); - path = AFPathByEscapingSpacesWithPlusSigns(path); - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) { if (success) { @@ -236,6 +227,42 @@ - (AFHTTPRequestOperation *)putObjectWithFile:(NSString *)path return [self setObjectWithMethod:@"PUT" file:path destinationPath:destinationPath parameters:parameters progress:progress success:success failure:failure]; } +// there is a bug in calcalute authentication in the AFAmazonS3Manager code + +- (AFHTTPRequestOperation *)putObjectWithData:(NSData *)data + destinationPath:(NSString *)destinationPath + parameters:(NSDictionary *)parameters + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(void (^)(id responseObject))success + failure:(void (^)(NSError *error))failure + +{ + NSParameterAssert(data); + NSParameterAssert(destinationPath); + + NSMutableURLRequest *request = nil; + request = [self.requestSerializer putRequestWithURLString:[[self.baseURL URLByAppendingPathComponent:destinationPath] absoluteString] parameters:parameters error:nil]; + + request.HTTPBody = data; + + + AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) { + if (success) { + success(responseObject); + } + } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) { + if (failure) { + failure(error); + } + }]; + + [requestOperation setUploadProgressBlock:progress]; + + [self.operationQueue addOperation:requestOperation]; + + return requestOperation; +} + - (AFHTTPRequestOperation *)setObjectWithMethod:(NSString *)method file:(NSString *)filePath destinationPath:(NSString *)destinationPath @@ -263,8 +290,6 @@ - (AFHTTPRequestOperation *)setObjectWithMethod:(NSString *)method return nil; } - destinationPath = AFPathByEscapingSpacesWithPlusSigns(destinationPath); - NSMutableURLRequest *request = nil; if ([method compare:@"POST" options:NSCaseInsensitiveSearch] == NSOrderedSame) { NSError *requestError = nil; @@ -319,8 +344,6 @@ - (AFHTTPRequestOperation *)deleteObjectWithPath:(NSString *)path { NSParameterAssert(path); - path = AFPathByEscapingSpacesWithPlusSigns(path); - return [self enqueueS3RequestOperationWithMethod:@"DELETE" path:path parameters:nil success:success failure:failure]; } diff --git a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h index 739682b..bace233 100644 --- a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h +++ b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h @@ -95,6 +95,11 @@ expiration:(NSDate *)expiration error:(NSError * __autoreleasing *)error; +// S3 expects parameters as headers for PUT requests for calculating authentication +- (NSMutableURLRequest *)putRequestWithURLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + error:(NSError *__autoreleasing *)error; + @end ///---------------- diff --git a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m index 2fad707..e673247 100644 --- a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m +++ b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m @@ -108,7 +108,9 @@ [mutableCanonicalizedAMZHeaderString appendFormat:@"%@:%@\n", field, value]; } - NSString *canonicalizedResource = bucket ? [NSString stringWithFormat:@"/%@%@", bucket, request.URL.path] : request.URL.path; + // the authentication need use percent encoded path + NSURLComponents *urlComp = [NSURLComponents componentsWithURL:request.URL resolvingAgainstBaseURL:NO]; + NSString *canonicalizedResource = bucket ? [NSString stringWithFormat:@"/%@%@", bucket, urlComp.percentEncodedPath] : urlComp.percentEncodedPath; NSString *method = [request HTTPMethod]; NSString *contentMD5 = [request valueForHTTPHeaderField:@"Content-MD5"]; NSString *contentType = [request valueForHTTPHeaderField:@"Content-Type"]; @@ -235,6 +237,26 @@ - (NSURLRequest *)preSignedRequestWithRequest:(NSURLRequest *)request #pragma mark - AFHTTPRequestSerializer + +// S3 expects parameters as headers for PUT requests for calculating authentication +- (NSMutableURLRequest *)putRequestWithURLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + error:(NSError *__autoreleasing *)error +{ + NSMutableURLRequest *request = [super requestWithMethod:@"PUT" URLString:URLString parameters:nil error:error]; + + if (self.sessionToken) { + [request setValue:self.sessionToken forHTTPHeaderField:@"x-amz-security-token"]; + } + if (parameters != nil) { + for (id key in parameters) { + [request setValue:[parameters objectForKey:key] forHTTPHeaderField:key]; + } + } + + return [[self requestBySettingAuthorizationHeadersForRequest:request error:error] mutableCopy]; +} + - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters