Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
4 changes: 2 additions & 2 deletions AFAmazonS3Client/AFAmazonS3Manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@
@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.
*/
- (void)postObjectWithFile:(NSString *)path
- (void)postObjectWithFile:(NSURL *)path
destinationPath:(NSString *)destinationPath
parameters:(NSDictionary *)parameters
progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
Expand All @@ -188,7 +188,7 @@
@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.
*/
- (void)putObjectWithFile:(NSString *)path
- (void)putObjectWithFile:(NSURL *)path
destinationPath:(NSString *)destinationPath
parameters:(NSDictionary *)parameters
progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
Expand Down
74 changes: 35 additions & 39 deletions AFAmazonS3Client/AFAmazonS3Manager.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

@interface AFAmazonS3Manager ()
@property (readwrite, nonatomic, strong) NSURL *baseURL;
@property (readwrite, nonatomic) NSTimeInterval sendTimeout;
@end

@implementation AFAmazonS3Manager
Expand All @@ -39,6 +40,7 @@ - (instancetype)initWithBaseURL:(NSURL *)url {

self.requestSerializer = [AFAmazonS3RequestSerializer serializer];
self.responseSerializer = [AFXMLParserResponseSerializer serializer];
self.sendTimeout = 30;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this while debugging problems I was having with operations seemingly starting but never completing. Some of these problems were caused by the code not calling the failure block in all failure cases, so it appeared like an upload had commenced when it had not, for example.


return self;
}
Expand All @@ -52,7 +54,6 @@ - (id)initWithAccessKeyID:(NSString *)accessKey
}

[self.requestSerializer setAccessKeyID:accessKey secret:secret];

return self;
}

Expand Down Expand Up @@ -184,7 +185,7 @@ - (void)getObjectWithPath:(NSString *)path
[self.operationQueue addOperation:requestOperation];
}

- (void)postObjectWithFile:(NSString *)path
- (void)postObjectWithFile:(NSURL *)path
destinationPath:(NSString *)destinationPath
parameters:(NSDictionary *)parameters
progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
Expand All @@ -194,7 +195,7 @@ - (void)postObjectWithFile:(NSString *)path
[self setObjectWithMethod:@"POST" file:path destinationPath:destinationPath parameters:parameters progress:progress success:success failure:failure];
}

- (void)putObjectWithFile:(NSString *)path
- (void)putObjectWithFile:(NSURL *)path
destinationPath:(NSString *)destinationPath
parameters:(NSDictionary *)parameters
progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
Expand All @@ -212,49 +213,44 @@ - (void)deleteObjectWithPath:(NSString *)path
}

- (void)setObjectWithMethod:(NSString *)method
file:(NSString *)filePath
file:(NSURL *)fileUrl
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
{
NSMutableURLRequest *fileRequest = [NSMutableURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]];
[fileRequest setCachePolicy:NSURLCacheStorageNotAllowed];

NSURLResponse *response = nil;
NSError *fileError = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:fileRequest returningResponse:&response error:&fileError];

if (data && response) {
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:method URLString:[[self.baseURL URLByAppendingPathComponent:destinationPath] absoluteString] parameters:parameters constructingBodyWithBlock:^(id <AFMultipartFormData> formData) {
if (![parameters valueForKey:@"key"]) {
[formData appendPartWithFormData:[[filePath lastPathComponent] dataUsingEncoding:NSUTF8StringEncoding] name:@"key"];
}
[formData appendPartWithFileData:data name:@"file" fileName:[filePath lastPathComponent] mimeType:[response MIMEType]];
} error:nil];

// NSURL *temporaryFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]]];
// request = [self.requestSerializer requestWithMultipartFormRequest:request writingStreamContentsToFile:temporaryFileURL completionHandler:^(NSError *error) {
// if (!error) {
// [request setHTTPBody:[NSData dataWithContentsOfFile:[temporaryFileURL absoluteString]]];
// }
// }];

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];

NSError* requestError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method
URLString:[[self.baseURL URLByAppendingPathComponent:destinationPath] absoluteString]
parameters:parameters
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that this call to sendSynchronousRequest was blocking indefinitely when called from a thread with with IOPOL_THROTTLE set (https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man3/getiopolicy_np.3.html). I also had other uploads in flight at the same time. I didn't take too long to investigate why, but replacing the call with NSData dataWithContentsOfURL fixed the problem for me.

error:&requestError];

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user passes a path does not exist locally here, then data will be null, and the PUT operation will not take place. This was causing my problems because setObjectWithMethod does not return anything to indicate failure, nor does it call the block passed as failure. The code was just failing silently. This was the first thing that I changed.

if (requestError && failure) {
failure(requestError);
return;
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the uploads completed successfully, the contents on S3 were multi-part MIME documents, rather than just the content of the file that I wanted to uploaded. I was uploading images, and there was a few lines of plain test at the start of each image referring to multipart-mime, mime-type image/jpeg, etc.

[request setHTTPBody:[NSData dataWithContentsOfURL:fileUrl]];
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error was never being set of checked here.

[request setTimeoutInterval:self.sendTimeout];

AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success(responseObject);
}
} failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nuked commented out code.

if (failure) {
NSLog(@"Operation: %@", operation);
NSLog(@"Response String: %@", operation.responseString);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an S3 operation failed, the response contains an XML description of the problem, but I didn't see a way to get this data from the NSError directly.

failure(error);
return;
}
}];

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the call to HTTPRequestOperationWithRequest:request fails, there is no return statement in this failure block, so the subsequent calls to

[requestOperation setUploadProgressBlock:progress];
[self.operationQueue addOperation:requestOperation];

still take place, with an invalid requestOperation object.

[requestOperation setUploadProgressBlock:progress];

[self.operationQueue addOperation:requestOperation];
}

#pragma mark - NSKeyValueObserving
Expand Down