├── AFAmazonS3Manager.podspec ├── AFAmazonS3Manager.xcworkspace └── contents.xcworkspacedata ├── AFAmazonS3Manager ├── AFAmazonS3Manager.h ├── AFAmazonS3Manager.m ├── AFAmazonS3RequestSerializer.h ├── AFAmazonS3RequestSerializer.m ├── AFAmazonS3ResponseSerializer.h └── AFAmazonS3ResponseSerializer.m ├── LICENSE └── README.md /AFAmazonS3Manager.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "AFAmazonS3Manager" 3 | s.version = "3.2.1" 4 | s.summary = "AFNetworking extension for the Amazon S3 API." 5 | s.homepage = "https://github.com/AFNetworking/AFAmazonS3Manager" 6 | s.social_media_url = "https://twitter.com/AFNetworking" 7 | s.license = 'MIT' 8 | s.author = { "Mattt Thompson" => "m@mattt.me" } 9 | s.source = { :git => "https://github.com/AFNetworking/AFAmazonS3Manager.git", 10 | :tag => s.version } 11 | 12 | s.ios.deployment_target = '6.0' 13 | s.osx.deployment_target = '10.8' 14 | 15 | s.source_files = 'AFAmazonS3Manager' 16 | s.requires_arc = true 17 | 18 | s.dependency 'AFNetworking/NSURLConnection', '~>2.4' 19 | end 20 | -------------------------------------------------------------------------------- /AFAmazonS3Manager.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /AFAmazonS3Manager/AFAmazonS3Manager.h: -------------------------------------------------------------------------------- 1 | // AFAmazonS3Manager.h 2 | // 3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AFHTTPRequestOperationManager.h" 24 | #import "AFAmazonS3RequestSerializer.h" 25 | 26 | /** 27 | AFAmazonS3Manager` is an `AFHTTPRequestOperationManager` subclass for interacting with the Amazon S3 webservice API (http://aws.amazon.com/s3/). 28 | */ 29 | @interface AFAmazonS3Manager : AFHTTPRequestOperationManager 30 | 31 | /** 32 | The base URL for the S3 manager. 33 | 34 | @discussion By default, the `baseURL` of `AFAmazonS3Manager` is derived from the `bucket` and `region` values. If `baseURL` is set directly, it will override the default `baseURL` and disregard values set for the `bucket`, `region`, and `useSSL` properties. 35 | */ 36 | @property (readonly, nonatomic, strong) NSURL *baseURL; 37 | 38 | /** 39 | Requests created by `AFAmazonS3Manager` are serialized by a subclass of `AFAmazonS3RequestSerializer`, which is responsible for encoding credentials, and any specified region and bucket. 40 | */ 41 | @property (nonatomic, strong) AFAmazonS3RequestSerializer * requestSerializer; 42 | 43 | /** 44 | Initializes and returns a newly allocated Amazon S3 client with specified credentials. 45 | 46 | This is the designated initializer. 47 | 48 | @param accessKey The AWS access key. 49 | @param secret The AWS secret. 50 | */ 51 | - (id)initWithAccessKeyID:(NSString *)accessKey 52 | secret:(NSString *)secret; 53 | 54 | /** 55 | Creates and enqueues a request operation to the client's operation queue. 56 | 57 | @param method The HTTP method for the request. 58 | @param path The path to be appended to the HTTP client's base URL and used as the request URL. 59 | @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. 60 | @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. 61 | 62 | @return The operation that was enqueued on operationQueue 63 | */ 64 | - (AFHTTPRequestOperation *)enqueueS3RequestOperationWithMethod:(NSString *)method 65 | path:(NSString *)path 66 | parameters:(NSDictionary *)parameters 67 | success:(void (^)(id responseObject))success 68 | failure:(void (^)(NSError *error))failure; 69 | 70 | ///------------------------- 71 | /// @name Service Operations 72 | ///------------------------- 73 | 74 | /** 75 | Returns a list of all buckets owned by the authenticated request sender. 76 | 77 | @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. 78 | @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. 79 | 80 | @return The operation that was enqueued on operationQueue 81 | */ 82 | - (AFHTTPRequestOperation *)getServiceWithSuccess:(void (^)(id responseObject))success 83 | failure:(void (^)(NSError *error))failure; 84 | 85 | 86 | ///------------------------ 87 | /// @name Bucket Operations 88 | ///------------------------ 89 | 90 | /** 91 | Lists information about the objects in a bucket for a user that has read access to the bucket. 92 | 93 | @param bucket The S3 bucket to get. Must not be `nil`. 94 | @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. 95 | @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. 96 | 97 | @return The operation that was enqueued on operationQueue 98 | */ 99 | - (AFHTTPRequestOperation *)getBucket:(NSString *)bucket 100 | success:(void (^)(id responseObject))success 101 | failure:(void (^)(NSError *error))failure; 102 | 103 | /** 104 | Creates a new bucket belonging to the account of the authenticated request sender. Optionally, you can specify a EU (Ireland) or US-West (N. California) location constraint. 105 | 106 | @param bucket The S3 bucket to create. Must not be `nil`. 107 | @param parameters The parameters to be encoded and set in the request HTTP body. 108 | @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. 109 | @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. 110 | 111 | @return The operation that was enqueued on operationQueue 112 | */ 113 | - (AFHTTPRequestOperation *)putBucket:(NSString *)bucket 114 | parameters:(NSDictionary *)parameters 115 | success:(void (^)(id responseObject))success 116 | failure:(void (^)(NSError *error))failure; 117 | 118 | /** 119 | Deletes the specified bucket. All objects in the bucket must be deleted before the bucket itself can be deleted. 120 | 121 | @param bucket The S3 bucket to be delete. Must not be `nil`. 122 | @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. 123 | @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. 124 | 125 | @return The operation that was enqueued on operationQueue 126 | */ 127 | - (AFHTTPRequestOperation *)deleteBucket:(NSString *)bucket 128 | success:(void (^)(id responseObject))success 129 | failure:(void (^)(NSError *error))failure; 130 | 131 | ///---------------------------------------------- 132 | /// @name Object Operations 133 | ///---------------------------------------------- 134 | 135 | /** 136 | Retrieves information about an object for a user with read access without fetching the object. 137 | 138 | @param path The object path. Must not be `nil`. 139 | @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. 140 | @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. 141 | */ 142 | - (AFHTTPRequestOperation *)headObjectWithPath:(NSString *)path 143 | success:(void (^)(NSHTTPURLResponse *response))success 144 | failure:(void (^)(NSError *error))failure; 145 | 146 | /** 147 | Gets an object for a user that has read access to the object. 148 | 149 | @param path The object path. Must not be `nil`. 150 | @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. 151 | @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. 152 | @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. 153 | 154 | @return The operation that was enqueued 155 | */ 156 | - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path 157 | progress:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progress 158 | success:(void (^)(id responseObject, NSData *responseData))success 159 | failure:(void (^)(NSError *error))failure; 160 | 161 | /** 162 | Gets an object for a user that has read access to the object. 163 | 164 | @param path The object path. Must not be `nil`. 165 | @param outputStream The `NSOutputStream` object receiving data from the request. 166 | @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. 167 | @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. 168 | @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. 169 | 170 | @return The operation that was enqueued 171 | */ 172 | - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path 173 | outputStream:(NSOutputStream *)outputStream 174 | progress:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progress 175 | success:(void (^)(id responseObject))success 176 | failure:(void (^)(NSError *error))failure; 177 | 178 | /** 179 | Adds an object to a bucket using forms. 180 | 181 | @param path The path to the local file. Must not be `nil`. 182 | @param destinationPath The destination path for the remote file. Must not be `nil`. 183 | @param parameters The parameters to be encoded and set in the request HTTP body. 184 | @param progress A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. 185 | @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. 186 | @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. 187 | 188 | @discussion `destinationPath` is relative to the bucket's root, and will create any intermediary directories as necessary. For example, specifying "/a/b/c.txt" will create the "/a" and / or "/a/b" directories within the bucket, if they do not already exist, and upload the source file as "c.txt" into "/a/b". 189 | */ 190 | - (AFHTTPRequestOperation *)postObjectWithFile:(NSString *)path 191 | destinationPath:(NSString *)destinationPath 192 | parameters:(NSDictionary *)parameters 193 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress 194 | success:(void (^)(id responseObject))success 195 | failure:(void (^)(NSError *error))failure; 196 | 197 | /** 198 | Adds an object to a bucket for a user that has write access to the bucket. A success response indicates the object was successfully stored; if the object already exists, it will be overwritten. 199 | 200 | @param path The path to the local file. Must not be `nil`. 201 | @param destinationPath The destination path for the remote file, including its name. Must not be `nil`. 202 | @param parameters The parameters to be encoded and set in the request HTTP body. 203 | @param progress A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. 204 | @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. 205 | @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. 206 | 207 | @return The operation that was enqueued on operationQueue 208 | 209 | @discussion `destinationPath` is relative to the bucket's root, and will create any intermediary directories as necessary. For example, specifying "/a/b/c.txt" will create the "/a" and / or "/a/b" directories within the bucket, if they do not already exist, and upload the source file as "c.txt" into "/a/b". 210 | */ 211 | - (AFHTTPRequestOperation *)putObjectWithFile:(NSString *)path 212 | destinationPath:(NSString *)destinationPath 213 | parameters:(NSDictionary *)parameters 214 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress 215 | success:(void (^)(id responseObject))success 216 | failure:(void (^)(NSError *error))failure; 217 | 218 | /** 219 | Deletes the specified object. Once deleted, there is no method to restore or undelete an object. 220 | 221 | @param path The path for the remote file to be deleted. Must not be `nil`. 222 | @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. 223 | @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. 224 | 225 | @return The operation that was enqueued on operationQueue 226 | */ 227 | - (AFHTTPRequestOperation *)deleteObjectWithPath:(NSString *)path 228 | success:(void (^)(id responseObject))success 229 | failure:(void (^)(NSError *error))failure; 230 | 231 | @end 232 | 233 | ///---------------- 234 | /// @name Constants 235 | ///---------------- 236 | 237 | /** 238 | ## Error Domain 239 | 240 | `AFAmazonS3ManagerErrorDomain` 241 | AFAmazonS3Manager errors. Error codes for `AFAmazonS3ManagerErrorDomain` correspond to codes in `NSURLErrorDomain`. 242 | */ 243 | extern NSString * const AFAmazonS3ManagerErrorDomain; 244 | 245 | @compatibility_alias AFAmazonS3Client AFAmazonS3Manager; 246 | -------------------------------------------------------------------------------- /AFAmazonS3Manager/AFAmazonS3Manager.m: -------------------------------------------------------------------------------- 1 | // AFAmazonS3Manager.m 2 | // 3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AFAmazonS3Manager.h" 24 | #import "AFAmazonS3ResponseSerializer.h" 25 | 26 | NSString * const AFAmazonS3ManagerErrorDomain = @"com.alamofire.networking.s3.error"; 27 | 28 | static NSString * AFPathByEscapingSpacesWithPlusSigns(NSString *path) { 29 | return [path stringByReplacingOccurrencesOfString:@" " withString:@"+"]; 30 | } 31 | 32 | @interface AFAmazonS3Manager () 33 | @property (readwrite, nonatomic, strong) NSURL *baseURL; 34 | @end 35 | 36 | @implementation AFAmazonS3Manager 37 | @synthesize baseURL = _s3_baseURL; 38 | @dynamic requestSerializer; 39 | 40 | - (instancetype)initWithBaseURL:(NSURL *)url { 41 | self = [super initWithBaseURL:url]; 42 | if (!self) { 43 | return nil; 44 | } 45 | 46 | self.requestSerializer = [AFAmazonS3RequestSerializer serializer]; 47 | self.responseSerializer = [AFAmazonS3ResponseSerializer serializer]; 48 | 49 | return self; 50 | } 51 | 52 | - (id)initWithAccessKeyID:(NSString *)accessKey 53 | secret:(NSString *)secret 54 | { 55 | self = [self initWithBaseURL:nil]; 56 | if (!self) { 57 | return nil; 58 | } 59 | 60 | [self.requestSerializer setAccessKeyID:accessKey secret:secret]; 61 | 62 | return self; 63 | } 64 | 65 | - (NSURL *)baseURL { 66 | if (!_s3_baseURL) { 67 | return self.requestSerializer.endpointURL; 68 | } 69 | 70 | return _s3_baseURL; 71 | } 72 | 73 | #pragma mark - 74 | 75 | - (AFHTTPRequestOperation *)enqueueS3RequestOperationWithMethod:(NSString *)method 76 | path:(NSString *)path 77 | parameters:(NSDictionary *)parameters 78 | success:(void (^)(id responseObject))success 79 | failure:(void (^)(NSError *error))failure 80 | { 81 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:parameters error:nil]; 82 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) { 83 | if (success) { 84 | success(responseObject); 85 | } 86 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) { 87 | if (failure) { 88 | failure(error); 89 | } 90 | }]; 91 | 92 | [self.operationQueue addOperation:requestOperation]; 93 | 94 | return requestOperation; 95 | } 96 | 97 | 98 | #pragma mark Service Operations 99 | 100 | - (AFHTTPRequestOperation *)getServiceWithSuccess:(void (^)(id responseObject))success 101 | failure:(void (^)(NSError *error))failure 102 | { 103 | return [self enqueueS3RequestOperationWithMethod:@"GET" path:@"/" parameters:nil success:success failure:failure]; 104 | } 105 | 106 | #pragma mark Bucket Operations 107 | 108 | - (AFHTTPRequestOperation *)getBucket:(NSString *)bucket 109 | success:(void (^)(id responseObject))success 110 | failure:(void (^)(NSError *error))failure 111 | { 112 | NSParameterAssert(bucket); 113 | 114 | return [self enqueueS3RequestOperationWithMethod:@"GET" path:bucket parameters:nil success:success failure:failure]; 115 | } 116 | 117 | - (AFHTTPRequestOperation *)putBucket:(NSString *)bucket 118 | parameters:(NSDictionary *)parameters 119 | success:(void (^)(id responseObject))success 120 | failure:(void (^)(NSError *error))failure 121 | { 122 | NSParameterAssert(bucket); 123 | 124 | return [self enqueueS3RequestOperationWithMethod:@"PUT" path:bucket parameters:parameters success:success failure:failure]; 125 | } 126 | 127 | - (AFHTTPRequestOperation *)deleteBucket:(NSString *)bucket 128 | success:(void (^)(id responseObject))success 129 | failure:(void (^)(NSError *error))failure 130 | { 131 | NSParameterAssert(bucket); 132 | 133 | return [self enqueueS3RequestOperationWithMethod:@"DELETE" path:bucket parameters:nil success:success failure:failure]; 134 | } 135 | 136 | #pragma mark Object Operations 137 | 138 | - (AFHTTPRequestOperation *)headObjectWithPath:(NSString *)path 139 | success:(void (^)(NSHTTPURLResponse *response))success 140 | failure:(void (^)(NSError *error))failure 141 | { 142 | NSParameterAssert(path); 143 | 144 | path = AFPathByEscapingSpacesWithPlusSigns(path); 145 | 146 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; 147 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, __unused id responseObject) { 148 | if (success) { 149 | success(operation.response); 150 | } 151 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) { 152 | if (failure) { 153 | failure(error); 154 | } 155 | }]; 156 | 157 | [self.operationQueue addOperation:requestOperation]; 158 | 159 | return requestOperation; 160 | } 161 | 162 | - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path 163 | progress:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progress 164 | success:(void (^)(id responseObject, NSData *responseData))success 165 | failure:(void (^)(NSError *error))failure 166 | { 167 | NSParameterAssert(path); 168 | 169 | path = AFPathByEscapingSpacesWithPlusSigns(path); 170 | 171 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; 172 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { 173 | if (success) { 174 | success(responseObject, operation.responseData); 175 | } 176 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) { 177 | if (failure) { 178 | failure(error); 179 | } 180 | }]; 181 | 182 | [requestOperation setDownloadProgressBlock:progress]; 183 | 184 | [self.operationQueue addOperation:requestOperation]; 185 | 186 | return requestOperation; 187 | } 188 | 189 | - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path 190 | outputStream:(NSOutputStream *)outputStream 191 | progress:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progress 192 | success:(void (^)(id responseObject))success 193 | failure:(void (^)(NSError *error))failure 194 | { 195 | NSParameterAssert(path); 196 | 197 | path = AFPathByEscapingSpacesWithPlusSigns(path); 198 | 199 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; 200 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) { 201 | if (success) { 202 | success(responseObject); 203 | } 204 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) { 205 | if (failure) { 206 | failure(error); 207 | } 208 | }]; 209 | 210 | requestOperation.outputStream = outputStream; 211 | 212 | [requestOperation setDownloadProgressBlock:progress]; 213 | 214 | [self.operationQueue addOperation:requestOperation]; 215 | 216 | return requestOperation; 217 | } 218 | 219 | - (AFHTTPRequestOperation *)postObjectWithFile:(NSString *)path 220 | destinationPath:(NSString *)destinationPath 221 | parameters:(NSDictionary *)parameters 222 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress 223 | success:(void (^)(id responseObject))success 224 | failure:(void (^)(NSError *error))failure 225 | { 226 | return [self setObjectWithMethod:@"POST" file:path destinationPath:destinationPath parameters:parameters progress:progress success:success failure:failure]; 227 | } 228 | 229 | - (AFHTTPRequestOperation *)putObjectWithFile:(NSString *)path 230 | destinationPath:(NSString *)destinationPath 231 | parameters:(NSDictionary *)parameters 232 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress 233 | success:(void (^)(id responseObject))success 234 | failure:(void (^)(NSError *error))failure 235 | { 236 | return [self setObjectWithMethod:@"PUT" file:path destinationPath:destinationPath parameters:parameters progress:progress success:success failure:failure]; 237 | } 238 | 239 | - (AFHTTPRequestOperation *)setObjectWithMethod:(NSString *)method 240 | file:(NSString *)filePath 241 | destinationPath:(NSString *)destinationPath 242 | parameters:(NSDictionary *)parameters 243 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress 244 | success:(void (^)(id responseObject))success 245 | failure:(void (^)(NSError *error))failure 246 | { 247 | NSParameterAssert(method); 248 | NSParameterAssert(filePath); 249 | NSParameterAssert(destinationPath); 250 | 251 | NSMutableURLRequest *fileRequest = [NSMutableURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]]; 252 | fileRequest.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; 253 | 254 | NSURLResponse *response = nil; 255 | NSError *fileError = nil; 256 | NSData *data = [NSURLConnection sendSynchronousRequest:fileRequest returningResponse:&response error:&fileError]; 257 | 258 | if (fileError || !response || !data) { 259 | if (failure) { 260 | failure(fileError); 261 | } 262 | 263 | return nil; 264 | } 265 | 266 | destinationPath = AFPathByEscapingSpacesWithPlusSigns(destinationPath); 267 | 268 | NSMutableURLRequest *request = nil; 269 | if ([method compare:@"POST" options:NSCaseInsensitiveSearch] == NSOrderedSame) { 270 | NSError *requestError = nil; 271 | request = [self.requestSerializer multipartFormRequestWithMethod:method URLString:[[self.baseURL URLByAppendingPathComponent:destinationPath] absoluteString] parameters:parameters constructingBodyWithBlock:^(id formData) { 272 | if (![parameters valueForKey:@"key"]) { 273 | [formData appendPartWithFormData:[[filePath lastPathComponent] dataUsingEncoding:NSUTF8StringEncoding] name:@"key"]; 274 | } 275 | 276 | [formData appendPartWithFileData:data name:@"file" fileName:[filePath lastPathComponent] mimeType:[response MIMEType]]; 277 | } error:&requestError]; 278 | 279 | if (requestError || !request) { 280 | if (failure) { 281 | failure(requestError); 282 | } 283 | 284 | return nil; 285 | } 286 | } else { 287 | request = [self.requestSerializer requestWithMethod:method URLString:[[self.baseURL URLByAppendingPathComponent:destinationPath] absoluteString] parameters:nil error:nil]; 288 | 289 | // S3 expects parameters as headers for PUT requests 290 | if (parameters != nil) { 291 | for (id key in parameters) { 292 | [request setValue:[parameters objectForKey:key] forHTTPHeaderField:key]; 293 | } 294 | } 295 | 296 | request.HTTPBody = data; 297 | } 298 | 299 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) { 300 | if (success) { 301 | success(responseObject); 302 | } 303 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) { 304 | if (failure) { 305 | failure(error); 306 | } 307 | }]; 308 | 309 | [requestOperation setUploadProgressBlock:progress]; 310 | 311 | [self.operationQueue addOperation:requestOperation]; 312 | 313 | return requestOperation; 314 | } 315 | 316 | - (AFHTTPRequestOperation *)deleteObjectWithPath:(NSString *)path 317 | success:(void (^)(id responseObject))success 318 | failure:(void (^)(NSError *error))failure 319 | { 320 | NSParameterAssert(path); 321 | 322 | path = AFPathByEscapingSpacesWithPlusSigns(path); 323 | 324 | return [self enqueueS3RequestOperationWithMethod:@"DELETE" path:path parameters:nil success:success failure:failure]; 325 | } 326 | 327 | #pragma mark - NSKeyValueObserving 328 | 329 | + (NSSet *)keyPathsForValuesAffectingBaseURL { 330 | return [NSSet setWithObjects:@"baseURL", @"requestSerializer.bucket", @"requestSerializer.region", @"requestSerializer.useSSL", nil]; 331 | } 332 | 333 | #pragma mark - NSCopying 334 | 335 | - (id)copyWithZone:(NSZone *)zone { 336 | AFAmazonS3Manager *manager = [[[self class] allocWithZone:zone] initWithBaseURL:_s3_baseURL]; 337 | 338 | manager.requestSerializer = [self.requestSerializer copyWithZone:zone]; 339 | manager.responseSerializer = [self.responseSerializer copyWithZone:zone]; 340 | 341 | return manager; 342 | } 343 | 344 | @end 345 | -------------------------------------------------------------------------------- /AFAmazonS3Manager/AFAmazonS3RequestSerializer.h: -------------------------------------------------------------------------------- 1 | // AFAmazonS3RequestSerializer.h 2 | // 3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AFURLRequestSerialization.h" 24 | 25 | /** 26 | `AFAmazonS3RequestSerializer` is an `AFHTTPRequestSerializer` subclass with convenience methods for creating requests for the Amazon S3 webservice, including creating an authorization header and building an endpoint URL for a given bucket, region, and TLS preferences. 27 | 28 | @discussion Due to aggressive cache policies from the Foundation URL Loading System cause the unsupported `If-Modified-Since` header to be sent for certain requests, the default `cachePolicy` is `NSURLRequestReloadIgnoringCacheData`. 29 | */ 30 | @interface AFAmazonS3RequestSerializer : AFHTTPRequestSerializer 31 | 32 | /** 33 | The S3 bucket for the client. `nil` by default. 34 | 35 | @see `AFAmazonS3Manager -baseURL` 36 | */ 37 | @property (nonatomic, copy) NSString *bucket; 38 | 39 | /** 40 | The AWS region for the client. `AFAmazonS3USStandardRegion` by default. Must not be `nil`. See "AWS Regions" for defined constant values. 41 | 42 | @see `AFAmazonS3Manager -baseURL` 43 | */ 44 | @property (nonatomic, copy) NSString *region; 45 | 46 | /** 47 | The AWS STS session token. `nil` by default. 48 | */ 49 | @property (nonatomic, copy) NSString *sessionToken; 50 | 51 | /** 52 | Whether to connect over HTTPS. `YES` by default. 53 | 54 | @see `AFAmazonS3Manager -baseURL` 55 | */ 56 | @property (nonatomic, assign) BOOL useSSL; 57 | 58 | /** 59 | A readonly endpoint URL created for the specified bucket, region, and TLS preference. `AFAmazonS3Manager` uses this as a `baseURL` unless one is manually specified. 60 | */ 61 | @property (readonly, nonatomic, copy) NSURL *endpointURL; 62 | 63 | /** 64 | Sets the access key ID and secret, used to generate authorization headers. 65 | 66 | @param accessKey The Amazon S3 Access Key ID. 67 | @param secret The Amazon S3 Secret. 68 | 69 | @discussion These values can be found on the AWS control panel: http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key 70 | */ 71 | - (void)setAccessKeyID:(NSString *)accessKey 72 | secret:(NSString *)secret; 73 | 74 | /** 75 | Returns a request with the necessary AWS authorization HTTP header fields from the specified request using the provided credentials. 76 | 77 | @param request The request. 78 | @param error The error that occured while constructing the request. 79 | 80 | @return The request with necessary `Authorization` and `Date` HTTP header fields. 81 | */ 82 | - (NSURLRequest *)requestBySettingAuthorizationHeadersForRequest:(NSURLRequest *)request 83 | error:(NSError * __autoreleasing *)error; 84 | 85 | /** 86 | Returns a request with pre-signed credentials in the query string. 87 | 88 | @param request The request. `HTTPMethod` must be `GET`. 89 | @param expiration The request expiration. If `nil`, defaults to 1 hour from when method is called. 90 | @param error The error that occured while constructing the request. 91 | 92 | @return The request with credentials signed in query string. 93 | */ 94 | - (NSURLRequest *)preSignedRequestWithRequest:(NSURLRequest *)request 95 | expiration:(NSDate *)expiration 96 | error:(NSError * __autoreleasing *)error; 97 | 98 | @end 99 | 100 | ///---------------- 101 | /// @name Constants 102 | ///---------------- 103 | 104 | /** 105 | ## AWS Regions 106 | 107 | The following AWS regions are defined: 108 | 109 | `AFAmazonS3USStandardRegion`: US Standard (s3.amazonaws.com); 110 | `AFAmazonS3USWest1Region`: US West (Oregon) Region (s3-us-west-1.amazonaws.com) 111 | `AFAmazonS3USWest2Region`: US West (Northern California) Region (s3-us-west-2.amazonaws.com) 112 | `AFAmazonS3EUWest1Region`: EU (Ireland) Region (s3-eu-west-1.amazonaws.com) 113 | `AFAmazonS3APSoutheast1Region`: Asia Pacific (Singapore) Region (s3-ap-southeast-1.amazonaws.com) 114 | `AFAmazonS3APSoutheast2Region`: Asia Pacific (Sydney) Region (s3-ap-southeast-2.amazonaws.com) 115 | `AFAmazonS3APNortheast2Region`: Asia Pacific (Tokyo) Region (s3-ap-northeast-1.amazonaws.com) 116 | `AFAmazonS3SAEast1Region`: South America (Sao Paulo) Region (s3-sa-east-1.amazonaws.com) 117 | 118 | For a full list of available regions, see http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region 119 | */ 120 | extern NSString * const AFAmazonS3USStandardRegion; 121 | extern NSString * const AFAmazonS3USWest1Region; 122 | extern NSString * const AFAmazonS3USWest2Region; 123 | extern NSString * const AFAmazonS3EUWest1Region; 124 | extern NSString * const AFAmazonS3EUCentral1Region; 125 | extern NSString * const AFAmazonS3APSoutheast1Region; 126 | extern NSString * const AFAmazonS3APSoutheast2Region; 127 | extern NSString * const AFAmazonS3APNortheast2Region; 128 | extern NSString * const AFAmazonS3SAEast1Region; 129 | -------------------------------------------------------------------------------- /AFAmazonS3Manager/AFAmazonS3RequestSerializer.m: -------------------------------------------------------------------------------- 1 | // AFAmazonS3RequestSerializer.m 2 | // 3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AFAmazonS3RequestSerializer.h" 24 | #import "AFAmazonS3Manager.h" 25 | 26 | #import 27 | 28 | NSString * const AFAmazonS3USStandardRegion = @"s3.amazonaws.com"; 29 | NSString * const AFAmazonS3USWest1Region = @"s3-us-west-1.amazonaws.com"; 30 | NSString * const AFAmazonS3USWest2Region = @"s3-us-west-2.amazonaws.com"; 31 | NSString * const AFAmazonS3EUWest1Region = @"s3-eu-west-1.amazonaws.com"; 32 | NSString * const AFAmazonS3EUCentral1Region = @"s3-eu-central-1.amazonaws.com"; 33 | NSString * const AFAmazonS3APSoutheast1Region = @"s3-ap-southeast-1.amazonaws.com"; 34 | NSString * const AFAmazonS3APSoutheast2Region = @"s3-ap-southeast-2.amazonaws.com"; 35 | NSString * const AFAmazonS3APNortheast1Region = @"s3-ap-northeast-1.amazonaws.com"; 36 | NSString * const AFAmazonS3SAEast1Region = @"s3-sa-east-1.amazonaws.com"; 37 | 38 | static NSTimeInterval const AFAmazonS3DefaultExpirationTimeInterval = 60 * 60; 39 | 40 | static NSData * AFHMACSHA1EncodedDataFromStringWithKey(NSString *string, NSString *key) { 41 | NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding]; 42 | CCHmacContext context; 43 | const char *keyCString = [key cStringUsingEncoding:NSASCIIStringEncoding]; 44 | 45 | CCHmacInit(&context, kCCHmacAlgSHA1, keyCString, strlen(keyCString)); 46 | CCHmacUpdate(&context, [data bytes], [data length]); 47 | 48 | unsigned char digestRaw[CC_SHA1_DIGEST_LENGTH]; 49 | NSUInteger digestLength = CC_SHA1_DIGEST_LENGTH; 50 | 51 | CCHmacFinal(&context, digestRaw); 52 | 53 | return [NSData dataWithBytes:digestRaw length:digestLength]; 54 | } 55 | 56 | static NSString * AFRFC822FormatStringFromDate(NSDate *date) { 57 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 58 | [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]]; 59 | [dateFormatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss z"]; 60 | [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]]; 61 | 62 | return [dateFormatter stringFromDate:date]; 63 | } 64 | 65 | static NSString * AFBase64EncodedStringFromData(NSData *data) { 66 | NSUInteger length = [data length]; 67 | NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; 68 | 69 | uint8_t *input = (uint8_t *)[data bytes]; 70 | uint8_t *output = (uint8_t *)[mutableData mutableBytes]; 71 | 72 | for (NSUInteger i = 0; i < length; i += 3) { 73 | NSUInteger value = 0; 74 | for (NSUInteger j = i; j < (i + 3); j++) { 75 | value <<= 8; 76 | if (j < length) { 77 | value |= (0xFF & input[j]); 78 | } 79 | } 80 | 81 | static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 82 | 83 | NSUInteger idx = (i / 3) * 4; 84 | output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F]; 85 | output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F]; 86 | output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '='; 87 | output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '='; 88 | } 89 | 90 | return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding]; 91 | } 92 | 93 | static NSString * AFAWSSignatureForRequest(NSURLRequest *request, NSString *bucket, NSString *timestamp, NSString *key) { 94 | NSMutableDictionary *mutableAMZHeaderFields = [NSMutableDictionary dictionary]; 95 | [[request allHTTPHeaderFields] enumerateKeysAndObjectsUsingBlock:^(NSString *field, id value, __unused BOOL *stop) { 96 | field = [field lowercaseString]; 97 | if ([field hasPrefix:@"x-amz"]) { 98 | if ([mutableAMZHeaderFields objectForKey:field]) { 99 | value = [[mutableAMZHeaderFields objectForKey:field] stringByAppendingFormat:@",%@", value]; 100 | } 101 | [mutableAMZHeaderFields setObject:value forKey:field]; 102 | } 103 | }]; 104 | 105 | NSMutableString *mutableCanonicalizedAMZHeaderString = [NSMutableString string]; 106 | for (NSString *field in [[mutableAMZHeaderFields allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 107 | id value = [mutableAMZHeaderFields objectForKey:field]; 108 | [mutableCanonicalizedAMZHeaderString appendFormat:@"%@:%@\n", field, value]; 109 | } 110 | 111 | NSString *canonicalizedResource = bucket ? [NSString stringWithFormat:@"/%@%@", bucket, request.URL.path] : request.URL.path; 112 | NSString *method = [request HTTPMethod]; 113 | NSString *contentMD5 = [request valueForHTTPHeaderField:@"Content-MD5"]; 114 | NSString *contentType = [request valueForHTTPHeaderField:@"Content-Type"]; 115 | 116 | NSMutableString *mutableString = [NSMutableString string]; 117 | [mutableString appendFormat:@"%@\n", method ? method : @""]; 118 | [mutableString appendFormat:@"%@\n", contentMD5 ? contentMD5 : @""]; 119 | [mutableString appendFormat:@"%@\n", contentType ? contentType : @""]; 120 | [mutableString appendFormat:@"%@\n", timestamp ? timestamp : @""]; 121 | [mutableString appendFormat:@"%@", mutableCanonicalizedAMZHeaderString]; 122 | [mutableString appendFormat:@"%@", canonicalizedResource]; 123 | 124 | return AFBase64EncodedStringFromData(AFHMACSHA1EncodedDataFromStringWithKey(mutableString, key)); 125 | } 126 | 127 | @interface AFAmazonS3RequestSerializer () 128 | @property (readwrite, nonatomic, copy) NSString *accessKey; 129 | @property (readwrite, nonatomic, copy) NSString *secret; 130 | @end 131 | 132 | @implementation AFAmazonS3RequestSerializer 133 | 134 | - (instancetype)init { 135 | self = [super init]; 136 | if (!self) { 137 | return nil; 138 | } 139 | 140 | self.cachePolicy = NSURLRequestReloadIgnoringCacheData; 141 | 142 | self.region = AFAmazonS3USStandardRegion; 143 | self.useSSL = YES; 144 | 145 | return self; 146 | } 147 | 148 | - (void)setAccessKeyID:(NSString *)accessKey 149 | secret:(NSString *)secret 150 | { 151 | NSParameterAssert(accessKey); 152 | NSParameterAssert(secret); 153 | 154 | self.accessKey = accessKey; 155 | self.secret = secret; 156 | } 157 | 158 | - (void)setRegion:(NSString *)region { 159 | NSParameterAssert(region); 160 | _region = region; 161 | } 162 | 163 | - (NSURL *)endpointURL { 164 | NSString *URLString = nil; 165 | NSString *scheme = self.useSSL ? @"https" : @"http"; 166 | if (self.bucket) { 167 | URLString = [NSString stringWithFormat:@"%@://%@.%@", scheme, self.bucket, self.region]; 168 | } else { 169 | URLString = [NSString stringWithFormat:@"%@://%@", scheme, self.region]; 170 | } 171 | 172 | return [NSURL URLWithString:URLString]; 173 | } 174 | 175 | #pragma mark - 176 | 177 | - (NSURLRequest *)requestBySettingAuthorizationHeadersForRequest:(NSURLRequest *)request 178 | error:(NSError * __autoreleasing *)error 179 | { 180 | NSMutableURLRequest *mutableRequest = [request mutableCopy]; 181 | 182 | if (self.accessKey && self.secret) { 183 | NSDate *date = [NSDate date]; 184 | NSString *signature = AFAWSSignatureForRequest(request, self.bucket, AFRFC822FormatStringFromDate(date), self.secret); 185 | 186 | [mutableRequest setValue:[NSString stringWithFormat:@"AWS %@:%@", self.accessKey, signature] forHTTPHeaderField:@"Authorization"]; 187 | [mutableRequest setValue:date ? AFRFC822FormatStringFromDate(date) : @"" forHTTPHeaderField:@"Date"]; 188 | } else { 189 | if (error) { 190 | NSDictionary *userInfo = @{ 191 | NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Access Key and Secret Required", @"AFAmazonS3Manager", nil) 192 | }; 193 | 194 | *error = [[NSError alloc] initWithDomain:AFAmazonS3ManagerErrorDomain code:NSURLErrorUserAuthenticationRequired userInfo:userInfo]; 195 | } 196 | } 197 | 198 | return mutableRequest; 199 | } 200 | 201 | - (NSURLRequest *)preSignedRequestWithRequest:(NSURLRequest *)request 202 | expiration:(NSDate *)expiration 203 | error:(NSError * __autoreleasing *)error 204 | { 205 | NSParameterAssert(request); 206 | NSParameterAssert([request.HTTPMethod compare:@"GET" options:NSCaseInsensitiveSearch] == NSOrderedSame); 207 | 208 | if (!expiration) { 209 | expiration = [[NSDate date] dateByAddingTimeInterval:AFAmazonS3DefaultExpirationTimeInterval]; 210 | } 211 | 212 | if (self.accessKey && self.secret) { 213 | NSString *expires = @((NSUInteger)[expiration timeIntervalSince1970]).stringValue; 214 | NSString *signature = AFAWSSignatureForRequest(request, self.bucket, expires, self.secret); 215 | 216 | NSDictionary *parameters = @{ 217 | @"AWSAccessKeyId": self.accessKey, 218 | @"Expires": expires, 219 | @"Signature": signature 220 | }; 221 | 222 | return [self requestBySerializingRequest:request withParameters:parameters error:error]; 223 | } else { 224 | if (error) { 225 | NSDictionary *userInfo = @{ 226 | NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Access Key required", @"AFAmazonS3Manager", nil) 227 | }; 228 | 229 | *error = [[NSError alloc] initWithDomain:AFAmazonS3ManagerErrorDomain code:NSURLErrorUserAuthenticationRequired userInfo:userInfo]; 230 | } 231 | 232 | return nil; 233 | } 234 | } 235 | 236 | #pragma mark - AFHTTPRequestSerializer 237 | 238 | - (NSMutableURLRequest *)requestWithMethod:(NSString *)method 239 | URLString:(NSString *)URLString 240 | parameters:(NSDictionary *)parameters 241 | error:(NSError *__autoreleasing *)error 242 | { 243 | NSMutableURLRequest *request = [super requestWithMethod:method URLString:URLString parameters:parameters error:error]; 244 | 245 | if (self.sessionToken) { 246 | [request setValue:self.sessionToken forHTTPHeaderField:@"x-amz-security-token"]; 247 | } 248 | 249 | return [[self requestBySettingAuthorizationHeadersForRequest:request error:error] mutableCopy]; 250 | } 251 | 252 | - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method 253 | URLString:(NSString *)URLString 254 | parameters:(NSDictionary *)parameters 255 | constructingBodyWithBlock:(void (^)(id))block 256 | error:(NSError *__autoreleasing *)error 257 | { 258 | NSMutableURLRequest *request = [super multipartFormRequestWithMethod:method URLString:URLString parameters:parameters constructingBodyWithBlock:block error:error]; 259 | 260 | if (self.sessionToken) { 261 | [request setValue:self.sessionToken forHTTPHeaderField:@"x-amz-security-token"]; 262 | } 263 | 264 | return [[self requestBySettingAuthorizationHeadersForRequest:request error:error] mutableCopy]; 265 | } 266 | 267 | @end 268 | -------------------------------------------------------------------------------- /AFAmazonS3Manager/AFAmazonS3ResponseSerializer.h: -------------------------------------------------------------------------------- 1 | // AFAmazonS3ResponseSerializer.h 2 | // 3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AFURLResponseSerialization.h" 24 | 25 | /** 26 | Returns an `AFAmazonS3ResponseObject` object from the AmazonS3 HTTP response 27 | */ 28 | @interface AFAmazonS3ResponseSerializer : AFHTTPResponseSerializer 29 | 30 | @end 31 | 32 | #pragma mark - 33 | 34 | /** 35 | Returned as the response object for S3 requests 36 | */ 37 | @interface AFAmazonS3ResponseObject : NSObject 38 | 39 | /** 40 | Creates a new from the HTTP response S3 returns 41 | 42 | @param response AFAmazonS3ResponseObject 43 | 44 | @return Returns an initialized instance of AFAmazonS3ResponseObject 45 | */ 46 | + (instancetype)responseObject:(NSHTTPURLResponse *)response; 47 | 48 | ///------------------------- 49 | /// @name Reading Attributes 50 | ///------------------------- 51 | 52 | /** 53 | The URL of the file sent to or retrieved from S3 54 | */ 55 | @property (readonly, nonatomic, copy) NSURL *URL; 56 | 57 | /** 58 | Contains the MD5 hash S3 computed for the file in the request 59 | */ 60 | @property (readonly, nonatomic, copy) NSString *ETag; 61 | 62 | /** 63 | The original NSHTTPURLResponse object returned by S3 64 | */ 65 | @property (readonly, nonatomic, strong) NSHTTPURLResponse *originalResponse; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /AFAmazonS3Manager/AFAmazonS3ResponseSerializer.m: -------------------------------------------------------------------------------- 1 | // AFAmazonS3ResponseSerializer.m 2 | // 3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AFAmazonS3ResponseSerializer.h" 24 | 25 | @implementation AFAmazonS3ResponseSerializer 26 | 27 | - (id)responseObjectForResponse:(NSURLResponse *)response 28 | data:(NSData *)data 29 | error:(NSError * __autoreleasing *)error 30 | { 31 | if ([self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { 32 | return [AFAmazonS3ResponseObject responseObject:(NSHTTPURLResponse *)response]; 33 | } 34 | 35 | return nil; 36 | } 37 | 38 | @end 39 | 40 | #pragma mark - 41 | 42 | @interface AFAmazonS3ResponseObject () 43 | @property (readwrite, nonatomic, strong) NSHTTPURLResponse *originalResponse; 44 | @end 45 | 46 | #pragma mark - 47 | 48 | @implementation AFAmazonS3ResponseObject 49 | 50 | + (instancetype)responseObject:(NSHTTPURLResponse *)response { 51 | AFAmazonS3ResponseObject *responseObject = [[AFAmazonS3ResponseObject alloc] init]; 52 | responseObject.originalResponse = response; 53 | 54 | return responseObject; 55 | } 56 | 57 | #pragma mark - 58 | 59 | - (NSURL *)URL { 60 | return self.originalResponse.URL; 61 | } 62 | 63 | - (NSString *)ETag { 64 | NSString *ETag = self.originalResponse.allHeaderFields[@"ETag"]; 65 | 66 | if ([ETag length] == 0) { 67 | return nil; 68 | } 69 | 70 | return [ETag stringByReplacingOccurrencesOfString:@"\"" withString:@""]; 71 | } 72 | 73 | #pragma mark - NSObject 74 | 75 | - (NSString *)description { 76 | return [NSString stringWithFormat:@"<%@: %p, URL: %@, ETAG: %@, originalResponse: %@>", NSStringFromClass([self class]), self, [self.URL absoluteString], self.ETag, self.originalResponse]; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AFAmazonS3Manager 2 | 3 | `AFAmazonS3Manager` is an `AFHTTPRequestOperationManager` subclass for interacting with the [Amazon S3 API](http://aws.amazon.com/s3/). 4 | 5 | ## Example Usage 6 | 7 | ```objective-c 8 | AFAmazonS3Manager *s3Manager = [[AFAmazonS3Manager alloc] initWithAccessKeyID:@"..." secret:@"..."]; 9 | s3Manager.requestSerializer.region = AFAmazonS3USWest1Region; 10 | s3Manager.requestSerializer.bucket = @"my-bucket-name"; 11 | 12 | NSString *destinationPath = @"/pathOnS3/to/file.txt"; 13 | 14 | [s3Manager postObjectWithFile:@"/path/to/file.txt" 15 | destinationPath:destinationPath 16 | parameters:nil 17 | progress:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) { 18 | NSLog(@"%f%% Uploaded", (totalBytesWritten / (totalBytesExpectedToWrite * 1.0f) * 100)); 19 | } 20 | success:^(AFAmazonS3ResponseObject *responseObject) { 21 | NSLog(@"Upload Complete: %@", responseObject.URL); 22 | } 23 | failure:^(NSError *error) { 24 | NSLog(@"Error: %@", error); 25 | }]; 26 | ``` 27 | 28 | ## Contact 29 | 30 | Mattt Thompson 31 | 32 | - http://github.com/mattt 33 | - http://twitter.com/mattt 34 | - m@mattt.me 35 | 36 | ## License 37 | 38 | AFAmazonS3Manager is available under the MIT license. See the LICENSE file for more info. 39 | --------------------------------------------------------------------------------