AWSS3TransferManager.m 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. //
  2. // Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License").
  5. // You may not use this file except in compliance with the License.
  6. // A copy of the License is located at
  7. //
  8. // http://aws.amazon.com/apache2.0
  9. //
  10. // or in the "license" file accompanying this file. This file is distributed
  11. // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  12. // express or implied. See the License for the specific language governing
  13. // permissions and limitations under the License.
  14. //
  15. #import "AWSS3.h"
  16. #import "AWSS3TransferManager.h"
  17. #import <AWSCore/AWSBolts.h>
  18. #import <AWSCore/AWSCategory.h>
  19. #import <AWSCore/AWSCocoaLumberjack.h>
  20. #import <AWSCore/AWSSynchronizedMutableDictionary.h>
  21. #import <AWSCore/AWSTMCache.h>
  22. static NSString *const AWSInfoS3TransferManager = @"S3TransferManager";
  23. // Private constants
  24. NSUInteger const AWSS3TransferManagerMinimumPartSize = 5 * 1024 * 1024; // 5MB
  25. NSString *const AWSS3TransferManagerCacheName = @"com.amazonaws.AWSS3TransferManager.CacheName";
  26. NSString *const AWSS3TransferManagerErrorDomain = @"com.amazonaws.AWSS3TransferManagerErrorDomain";
  27. NSUInteger const AWSS3TransferManagerByteLimitDefault = 5 * 1024 * 1024; // 5MB
  28. NSTimeInterval const AWSS3TransferManagerAgeLimitDefault = 0.0; // Keeps the data indefinitely unless it hits the size limit.
  29. NSString *const AWSS3TransferManagerUserAgentPrefix = @"transfer-manager";
  30. @interface AWSS3TransferManager()
  31. @property (nonatomic, strong) AWSS3 *s3;
  32. @property (nonatomic, strong) AWSTMCache *cache;
  33. @end
  34. @interface AWSS3TransferManagerUploadRequest ()
  35. @property (nonatomic, assign) AWSS3TransferManagerRequestState state;
  36. @property (nonatomic, assign) NSUInteger currentUploadingPartNumber;
  37. @property (nonatomic, strong) NSMutableArray *completedPartsArray;
  38. @property (nonatomic, strong) NSString *uploadId;
  39. @property (nonatomic, strong) NSString *cacheIdentifier;
  40. @property (atomic, strong) AWSS3UploadPartRequest *currentUploadingPart;
  41. @property (atomic, assign) int64_t totalSuccessfullySentPartsDataLength;
  42. @end
  43. @interface AWSS3TransferManagerDownloadRequest ()
  44. @property (nonatomic, strong) NSURL *temporaryFileURL;
  45. @property (nonatomic, strong) NSURL *originalFileURL;
  46. @property (nonatomic, assign) AWSS3TransferManagerRequestState state;
  47. @property (nonatomic, strong) NSString *cacheIdentifier;
  48. @end
  49. @interface AWSS3()
  50. - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration;
  51. @end
  52. @implementation AWSS3TransferManager
  53. static AWSSynchronizedMutableDictionary *_serviceClients = nil;
  54. + (instancetype)defaultS3TransferManager {
  55. static AWSS3TransferManager *_defaultS3TransferManager = nil;
  56. static dispatch_once_t onceToken;
  57. dispatch_once(&onceToken, ^{
  58. AWSServiceConfiguration *serviceConfiguration = nil;
  59. AWSServiceInfo *serviceInfo = [[AWSInfo defaultAWSInfo] defaultServiceInfo:AWSInfoS3TransferManager];
  60. if (serviceInfo) {
  61. serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:serviceInfo.region
  62. credentialsProvider:serviceInfo.cognitoCredentialsProvider];
  63. }
  64. if (!serviceConfiguration) {
  65. serviceConfiguration = [AWSServiceManager defaultServiceManager].defaultServiceConfiguration;
  66. }
  67. if (!serviceConfiguration) {
  68. @throw [NSException exceptionWithName:NSInternalInconsistencyException
  69. reason:@"The service configuration is `nil`. You need to configure `Info.plist` or set `defaultServiceConfiguration` before using this method."
  70. userInfo:nil];
  71. }
  72. _defaultS3TransferManager = [[AWSS3TransferManager alloc] initWithConfiguration:serviceConfiguration
  73. cacheName:AWSS3TransferManagerCacheName];
  74. });
  75. return _defaultS3TransferManager;
  76. }
  77. + (void)registerS3TransferManagerWithConfiguration:(AWSServiceConfiguration *)configuration forKey:(NSString *)key {
  78. static dispatch_once_t onceToken;
  79. dispatch_once(&onceToken, ^{
  80. _serviceClients = [AWSSynchronizedMutableDictionary new];
  81. });
  82. AWSS3TransferManager *s3TransferManager = [[AWSS3TransferManager alloc] initWithConfiguration:configuration
  83. cacheName:[NSString stringWithFormat:@"%@.%@", AWSS3TransferManagerCacheName, key]];
  84. [_serviceClients setObject:s3TransferManager
  85. forKey:key];
  86. }
  87. + (instancetype)S3TransferManagerForKey:(NSString *)key {
  88. @synchronized(self) {
  89. AWSS3TransferManager *serviceClient = [_serviceClients objectForKey:key];
  90. if (serviceClient) {
  91. return serviceClient;
  92. }
  93. AWSServiceInfo *serviceInfo = [[AWSInfo defaultAWSInfo] serviceInfo:AWSInfoS3TransferManager
  94. forKey:key];
  95. if (serviceInfo) {
  96. AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:serviceInfo.region
  97. credentialsProvider:serviceInfo.cognitoCredentialsProvider];
  98. [AWSS3TransferManager registerS3TransferManagerWithConfiguration:serviceConfiguration
  99. forKey:key];
  100. }
  101. return [_serviceClients objectForKey:key];
  102. }
  103. }
  104. + (void)removeS3TransferManagerForKey:(NSString *)key {
  105. [_serviceClients removeObjectForKey:key];
  106. }
  107. - (instancetype)init {
  108. @throw [NSException exceptionWithName:NSInternalInconsistencyException
  109. reason:@"`- init` is not a valid initializer. Use `+ defaultS3TransferManager` or `+ S3TransferManagerForKey:` instead."
  110. userInfo:nil];
  111. return nil;
  112. }
  113. - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration
  114. identifier:(NSString *)identifier {
  115. if (self = [self initWithConfiguration:configuration
  116. cacheName:[NSString stringWithFormat:@"%@.%@", AWSS3TransferManagerCacheName, identifier]]) {
  117. }
  118. return self;
  119. }
  120. - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration
  121. cacheName:(NSString *)cacheName {
  122. if (self = [super init]) {
  123. AWSServiceConfiguration *_configuration = [configuration copy];
  124. [_configuration addUserAgentProductToken:AWSS3TransferManagerUserAgentPrefix];
  125. _s3 = [[AWSS3 alloc] initWithConfiguration:_configuration];
  126. _cache = [[AWSTMCache alloc] initWithName:cacheName
  127. rootPath:[NSTemporaryDirectory() stringByAppendingPathComponent:AWSS3TransferManagerCacheName]];
  128. _cache.diskCache.byteLimit = AWSS3TransferManagerByteLimitDefault;
  129. _cache.diskCache.ageLimit = AWSS3TransferManagerAgeLimitDefault;
  130. }
  131. return self;
  132. }
  133. - (AWSTask *)upload:(AWSS3TransferManagerUploadRequest *)uploadRequest {
  134. NSString *cacheKey = nil;
  135. if ([uploadRequest valueForKey:@"cacheIdentifier"]) {
  136. cacheKey = [uploadRequest valueForKey:@"cacheIdentifier"];
  137. } else {
  138. cacheKey = [[NSProcessInfo processInfo] globallyUniqueString];
  139. [uploadRequest setValue:cacheKey forKey:@"cacheIdentifier"];
  140. }
  141. return [self upload:uploadRequest cacheKey:cacheKey];
  142. }
  143. - (AWSTask *)upload:(AWSS3TransferManagerUploadRequest *)uploadRequest
  144. cacheKey:(NSString *)cacheKey {
  145. //validate input
  146. if ([uploadRequest.bucket length] == 0) {
  147. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"'bucket' name can not be empty", nil)};
  148. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorMissingRequiredParameters userInfo:userInfo]];
  149. }
  150. if ([uploadRequest.key length] == 0) {
  151. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"'key' name can not be empty", nil)};
  152. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorMissingRequiredParameters userInfo:userInfo]];
  153. }
  154. if (uploadRequest.body == nil) {
  155. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"'body' can not be nil", nil)};
  156. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorMissingRequiredParameters userInfo:userInfo]];
  157. } else if ([uploadRequest.body isKindOfClass:[NSURL class]] == NO) {
  158. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid 'body' Type, must be an instance of NSURL Class", nil)};
  159. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorInvalidParameters userInfo:userInfo]];
  160. }
  161. //Check if the task has already completed
  162. if (uploadRequest.state == AWSS3TransferManagerRequestStateCompleted) {
  163. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"can not continue to upload a completed task", nil)]};
  164. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCompleted userInfo:userInfo]];
  165. } else if (uploadRequest.state == AWSS3TransferManagerRequestStateCanceling){
  166. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"can not continue to upload a cancelled task.", nil)]};
  167. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCancelled userInfo:userInfo]];
  168. } else {
  169. //change state to running
  170. [uploadRequest setValue:[NSNumber numberWithInteger:AWSS3TransferManagerRequestStateRunning] forKey:@"state"];
  171. }
  172. NSError *error = nil;
  173. NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[[uploadRequest.body path] stringByResolvingSymlinksInPath]
  174. error:&error];
  175. if (!attributes) {
  176. return [AWSTask taskWithError:error];
  177. }
  178. unsigned long long fileSize = [attributes fileSize];
  179. __weak AWSS3TransferManager *weakSelf = self;
  180. AWSTask *task = [AWSTask taskWithResult:nil];
  181. task = [[[task continueWithSuccessBlock:^id(AWSTask *task) {
  182. [weakSelf.cache setObject:uploadRequest
  183. forKey:cacheKey];
  184. return nil;
  185. }] continueWithSuccessBlock:^id(AWSTask *task) {
  186. if (fileSize > AWSS3TransferManagerMinimumPartSize) {
  187. return [weakSelf multipartUpload:uploadRequest fileSize:fileSize cacheKey:cacheKey];
  188. } else {
  189. return [weakSelf putObject:uploadRequest fileSize:fileSize cacheKey:cacheKey];
  190. }
  191. }] continueWithBlock:^id(AWSTask *task) {
  192. if (task.error) {
  193. if ([task.error.domain isEqualToString:NSURLErrorDomain]
  194. && task.error.code == NSURLErrorCancelled) {
  195. if (uploadRequest.state == AWSS3TransferManagerRequestStatePaused) {
  196. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain
  197. code:AWSS3TransferManagerErrorPaused
  198. userInfo:task.error.userInfo]];
  199. } else {
  200. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain
  201. code:AWSS3TransferManagerErrorCancelled
  202. userInfo:task.error.userInfo]];
  203. }
  204. } else {
  205. return [AWSTask taskWithError:task.error];
  206. }
  207. } else {
  208. uploadRequest.state = AWSS3TransferManagerRequestStateCompleted;
  209. [uploadRequest setValue:nil forKey:@"internalRequest"];
  210. return [AWSTask taskWithResult:task.result];
  211. }
  212. }];
  213. return task;
  214. }
  215. - (AWSTask *)putObject:(AWSS3TransferManagerUploadRequest *)uploadRequest
  216. fileSize:(unsigned long long) fileSize
  217. cacheKey:(NSString *)cacheKey {
  218. uploadRequest.contentLength = [NSNumber numberWithUnsignedLongLong:fileSize];
  219. AWSS3PutObjectRequest *putObjectRequest = [AWSS3PutObjectRequest new];
  220. [putObjectRequest aws_copyPropertiesFromObject:uploadRequest];
  221. __weak AWSS3TransferManager *weakSelf = self;
  222. AWSTask *uploadTask = [[weakSelf.s3 putObject:putObjectRequest] continueWithBlock:^id(AWSTask *task) {
  223. //delete cached Object if state is not Paused
  224. if (uploadRequest.state != AWSS3TransferManagerRequestStatePaused) {
  225. [weakSelf.cache removeObjectForKey:cacheKey];
  226. }
  227. if (task.error) {
  228. return [AWSTask taskWithError:task.error];
  229. }
  230. AWSS3TransferManagerUploadOutput *uploadOutput = [AWSS3TransferManagerUploadOutput new];
  231. if (task.result) {
  232. AWSS3PutObjectOutput *putObjectOutput = task.result;
  233. [uploadOutput aws_copyPropertiesFromObject:putObjectOutput];
  234. }
  235. return uploadOutput;
  236. }];
  237. return uploadTask;
  238. }
  239. - (AWSTask *)multipartUpload:(AWSS3TransferManagerUploadRequest *)uploadRequest
  240. fileSize:(unsigned long long) fileSize
  241. cacheKey:(NSString *)cacheKey {
  242. NSUInteger partCount = ceil((double)fileSize / AWSS3TransferManagerMinimumPartSize);
  243. AWSTask *initRequest = nil;
  244. __weak AWSS3TransferManager *weakSelf = self;
  245. //if it is a new request, Init multipart upload request
  246. if (uploadRequest.currentUploadingPartNumber == 0) {
  247. AWSS3CreateMultipartUploadRequest *createMultipartUploadRequest = [AWSS3CreateMultipartUploadRequest new];
  248. [createMultipartUploadRequest aws_copyPropertiesFromObject:uploadRequest];
  249. [createMultipartUploadRequest setValue:[AWSNetworkingRequest new] forKey:@"internalRequest"]; //recreate a new internalRequest
  250. initRequest = [weakSelf.s3 createMultipartUpload:createMultipartUploadRequest];
  251. [uploadRequest setValue:[NSMutableArray arrayWithCapacity:partCount] forKey:@"completedPartsArray"];
  252. } else {
  253. //if it is a paused request, skip initMultipart Upload request.
  254. initRequest = [AWSTask taskWithResult:nil];
  255. }
  256. AWSS3CompleteMultipartUploadRequest *completeMultipartUploadRequest = [AWSS3CompleteMultipartUploadRequest new];
  257. [completeMultipartUploadRequest aws_copyPropertiesFromObject:uploadRequest];
  258. [completeMultipartUploadRequest setValue:[AWSNetworkingRequest new] forKey:@"internalRequest"]; //recreate a new internalRequest
  259. AWSTask *uploadTask = [[[initRequest continueWithSuccessBlock:^id(AWSTask *task) {
  260. AWSS3CreateMultipartUploadOutput *output = task.result;
  261. if (output.uploadId) {
  262. completeMultipartUploadRequest.uploadId = output.uploadId;
  263. uploadRequest.uploadId = output.uploadId; //pass uploadId to the request for reference.
  264. } else {
  265. completeMultipartUploadRequest.uploadId = uploadRequest.uploadId;
  266. }
  267. AWSTask *uploadPartsTask = [AWSTask taskWithResult:nil];
  268. NSUInteger c = uploadRequest.currentUploadingPartNumber;
  269. if (c == 0) {
  270. c = 1;
  271. }
  272. __block int64_t multiplePartsTotalBytesSent = 0;
  273. for (NSUInteger i = c; i < partCount + 1; i++) {
  274. uploadPartsTask = [uploadPartsTask continueWithSuccessBlock:^id(AWSTask *task) {
  275. //Cancel this task if state is canceling
  276. if (uploadRequest.state == AWSS3TransferManagerRequestStateCanceling) {
  277. //return a error task
  278. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"S3 MultipartUpload has been cancelled.", nil)]};
  279. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCancelled userInfo:userInfo]];
  280. }
  281. //Pause this task if state is Paused
  282. if (uploadRequest.state == AWSS3TransferManagerRequestStatePaused) {
  283. //return an error task
  284. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"S3 MultipartUpload has been paused.", nil)]};
  285. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorPaused userInfo:userInfo]];
  286. }
  287. NSUInteger dataLength = i == partCount ? (NSUInteger)fileSize - ((i - 1) * AWSS3TransferManagerMinimumPartSize) : AWSS3TransferManagerMinimumPartSize;
  288. NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:[uploadRequest.body path]];
  289. [fileHandle seekToFileOffset:(i - 1) * AWSS3TransferManagerMinimumPartSize];
  290. NSData *partData = [fileHandle readDataOfLength:dataLength];
  291. NSURL *tempURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]]];
  292. [partData writeToURL:tempURL atomically:YES];
  293. partData = nil;
  294. [fileHandle closeFile];
  295. AWSS3UploadPartRequest *uploadPartRequest = [AWSS3UploadPartRequest new];
  296. uploadPartRequest.bucket = uploadRequest.bucket;
  297. uploadPartRequest.key = uploadRequest.key;
  298. uploadPartRequest.partNumber = @(i);
  299. uploadPartRequest.body = tempURL;
  300. uploadPartRequest.contentLength = @(dataLength);
  301. uploadPartRequest.uploadId = output.uploadId?output.uploadId:uploadRequest.uploadId;
  302. //pass SSE Value
  303. uploadPartRequest.SSECustomerAlgorithm = uploadRequest.SSECustomerAlgorithm;
  304. uploadPartRequest.SSECustomerKey = uploadRequest.SSECustomerKey;
  305. uploadPartRequest.SSECustomerKeyMD5 = uploadRequest.SSECustomerKeyMD5;
  306. uploadRequest.currentUploadingPart = uploadPartRequest; //retain the current uploading parts for cancel/pause purpose
  307. //reprocess the progressFeed received from s3 client
  308. uploadPartRequest.uploadProgress = ^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
  309. AWSNetworkingRequest *internalRequest = [uploadRequest valueForKey:@"internalRequest"];
  310. if (internalRequest.uploadProgress) {
  311. int64_t previousSentDataLengh = [[uploadRequest valueForKey:@"totalSuccessfullySentPartsDataLength"] longLongValue];
  312. if (multiplePartsTotalBytesSent == 0) {
  313. multiplePartsTotalBytesSent += bytesSent;
  314. multiplePartsTotalBytesSent += previousSentDataLengh;
  315. internalRequest.uploadProgress(bytesSent,multiplePartsTotalBytesSent,fileSize);
  316. } else {
  317. multiplePartsTotalBytesSent += bytesSent;
  318. internalRequest.uploadProgress(bytesSent,multiplePartsTotalBytesSent,fileSize);
  319. }
  320. }
  321. };
  322. return [[[weakSelf.s3 uploadPart:uploadPartRequest] continueWithSuccessBlock:^id(AWSTask *task) {
  323. AWSS3UploadPartOutput *partOuput = task.result;
  324. AWSS3CompletedPart *completedPart = [AWSS3CompletedPart new];
  325. completedPart.partNumber = @(i);
  326. completedPart.ETag = partOuput.ETag;
  327. NSMutableArray *completedParts = [uploadRequest valueForKey:@"completedPartsArray"];
  328. if (![completedParts containsObject:completedPart]) {
  329. [completedParts addObject:completedPart];
  330. }
  331. int64_t totalSentLenght = [[uploadRequest valueForKey:@"totalSuccessfullySentPartsDataLength"] longLongValue];
  332. totalSentLenght += dataLength;
  333. [uploadRequest setValue:@(totalSentLenght) forKey:@"totalSuccessfullySentPartsDataLength"];
  334. //set currentUploadingPartNumber to i+1 to prevent it be downloaded again if pause happened right after parts finished.
  335. uploadRequest.currentUploadingPartNumber = i + 1;
  336. [weakSelf.cache setObject:uploadRequest forKey:cacheKey];
  337. return nil;
  338. }] continueWithBlock:^id(AWSTask *task) {
  339. NSError *error = nil;
  340. [[NSFileManager defaultManager] removeItemAtURL:tempURL
  341. error:&error];
  342. if (error) {
  343. AWSDDLogError(@"Failed to delete a temporary file for part upload: [%@]", error);
  344. }
  345. if (task.error) {
  346. return [AWSTask taskWithError:task.error];
  347. } else {
  348. return nil;
  349. }
  350. }];
  351. }];
  352. }
  353. return uploadPartsTask;
  354. }] continueWithSuccessBlock:^id(AWSTask *task) {
  355. //If all parts upload succeed, send completeMultipartUpload request
  356. NSMutableArray *completedParts = [uploadRequest valueForKey:@"completedPartsArray"];
  357. if ([completedParts count] != partCount) {
  358. NSDictionary *userInfo = @{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"completedParts count is not equal to totalPartCount. expect %lu but got %lu",(unsigned long)partCount,(unsigned long)[completedParts count]],@"completedParts":completedParts};
  359. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain
  360. code:AWSS3TransferManagerErrorUnknown
  361. userInfo:userInfo]];
  362. }
  363. AWSS3CompletedMultipartUpload *completedMultipartUpload = [AWSS3CompletedMultipartUpload new];
  364. completedMultipartUpload.parts = completedParts;
  365. completeMultipartUploadRequest.multipartUpload = completedMultipartUpload;
  366. return [weakSelf.s3 completeMultipartUpload:completeMultipartUploadRequest];
  367. }] continueWithBlock:^id(AWSTask *task) {
  368. //delete cached Object if state is not Paused
  369. if (uploadRequest.state != AWSS3TransferManagerRequestStatePaused) {
  370. [weakSelf.cache removeObjectForKey:cacheKey];
  371. }
  372. if (uploadRequest.state == AWSS3TransferManagerRequestStateCanceling) {
  373. [weakSelf abortMultipartUploadsForRequest:uploadRequest];
  374. }
  375. if (task.error) {
  376. return [AWSTask taskWithError:task.error];
  377. }
  378. AWSS3TransferManagerUploadOutput *uploadOutput = [AWSS3TransferManagerUploadOutput new];
  379. if (task.result) {
  380. AWSS3CompleteMultipartUploadOutput *completeMultipartUploadOutput = task.result;
  381. [uploadOutput aws_copyPropertiesFromObject:completeMultipartUploadOutput];
  382. }
  383. return uploadOutput;
  384. }];
  385. return uploadTask;
  386. }
  387. - (AWSTask *)download:(AWSS3TransferManagerDownloadRequest *)downloadRequest {
  388. NSString *cacheKey = nil;
  389. if ([downloadRequest valueForKey:@"cacheIdentifier"]) {
  390. cacheKey = [downloadRequest valueForKey:@"cacheIdentifier"];
  391. } else {
  392. cacheKey = [[NSProcessInfo processInfo] globallyUniqueString];
  393. [downloadRequest setValue:cacheKey forKey:@"cacheIdentifier"];
  394. }
  395. return [self download:downloadRequest cacheKey:cacheKey];
  396. }
  397. - (AWSTask *)download:(AWSS3TransferManagerDownloadRequest *)downloadRequest
  398. cacheKey:(NSString *)cacheKey {
  399. //validate input
  400. if ([downloadRequest.bucket length] == 0) {
  401. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"'bucket' name can not be empty", nil)};
  402. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorMissingRequiredParameters userInfo:userInfo]];
  403. }
  404. if ([downloadRequest.key length] == 0) {
  405. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"'key' name can not be empty", nil)};
  406. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorMissingRequiredParameters userInfo:userInfo]];
  407. }
  408. //Check if the task has already completed
  409. if (downloadRequest.state == AWSS3TransferManagerRequestStateCompleted) {
  410. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"can not continue to download a completed task", nil)]};
  411. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCompleted userInfo:userInfo]];
  412. } else if (downloadRequest.state == AWSS3TransferManagerRequestStateCanceling){
  413. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"can not continue to download a cancelled task.", nil)]};
  414. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCancelled userInfo:userInfo]];
  415. }
  416. //if it is a new request.
  417. if (downloadRequest.state != AWSS3TransferManagerRequestStatePaused) {
  418. //If downloadFileURL is nil, create a URL in temporary folder for user.
  419. if (downloadRequest.downloadingFileURL == nil) {
  420. NSString *adjustedKeyName = [[downloadRequest.key componentsSeparatedByString:@"/"] lastObject];
  421. NSString *generatedfileName = adjustedKeyName;
  422. //check if the file already exists, if yes, create another fileName;
  423. NSUInteger suffixCount = 2;
  424. while ([[NSFileManager defaultManager] fileExistsAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:generatedfileName]]) {
  425. NSMutableArray *components = [[adjustedKeyName componentsSeparatedByString:@"."] mutableCopy];
  426. if ([components count] == 1) {
  427. generatedfileName = [NSString stringWithFormat:@"%@ (%lu)",adjustedKeyName,(unsigned long)suffixCount];
  428. } else if ([components count] >= 2) {
  429. NSString *modifiedFileName = [NSString stringWithFormat:@"%@ (%lu)",[components objectAtIndex:[components count]-2],(unsigned long)suffixCount];
  430. [components replaceObjectAtIndex:[components count]-2 withObject:modifiedFileName];
  431. generatedfileName = [components componentsJoinedByString:@"."];
  432. } else {
  433. NSString *errorString = @"[generatedPath componentsSeparatedByString] returns empty array or nil, generatedfileName:%@";
  434. AWSDDLogError(errorString, generatedfileName);
  435. NSString *localizedErrorString = [NSString stringWithFormat:NSLocalizedString(errorString, @"[generatedPath componentsSeparatedByString] returns empty array or nil, generatedfileName:{Generated File Name}"), generatedfileName];
  436. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: localizedErrorString};
  437. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorInternalInConsistency userInfo:userInfo]];
  438. }
  439. suffixCount++;
  440. }
  441. downloadRequest.downloadingFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:generatedfileName]];
  442. }
  443. //create a tempFileURL
  444. NSString *tempFileName = [[downloadRequest.downloadingFileURL lastPathComponent] stringByAppendingString:cacheKey];
  445. NSURL *tempFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tempFileName]];
  446. //save current downloadFileURL
  447. downloadRequest.originalFileURL = downloadRequest.downloadingFileURL;
  448. //save the tempFileURL
  449. downloadRequest.temporaryFileURL = tempFileURL;
  450. } else {
  451. //if the is a paused task, set the range
  452. NSURL *tempFileURL = downloadRequest.temporaryFileURL;
  453. if (tempFileURL) {
  454. if ([[NSFileManager defaultManager] fileExistsAtPath:tempFileURL.path] == NO) {
  455. AWSDDLogError(@"tempfile is not exist, unable to resume");
  456. }
  457. NSError *error = nil;
  458. NSString *tempFilePath = tempFileURL.path;
  459. NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[tempFilePath stringByResolvingSymlinksInPath]
  460. error:&error];
  461. if (error) {
  462. AWSDDLogError(@"Unable to resume download task: Failed to retrival tempFileURL. [%@]",error);
  463. }
  464. unsigned long long fileSize = [attributes fileSize];
  465. downloadRequest.range = [NSString stringWithFormat:@"bytes=%llu-",fileSize];
  466. }
  467. }
  468. //change state to running
  469. [downloadRequest setValue:[NSNumber numberWithInteger:AWSS3TransferManagerRequestStateRunning] forKey:@"state"];
  470. //set shouldWriteDirectly to YES
  471. [downloadRequest setValue:@YES forKey:@"shouldWriteDirectly"];
  472. __weak AWSS3TransferManager *weakSelf = self;
  473. AWSTask *task = [AWSTask taskWithResult:nil];
  474. task = [[task continueWithSuccessBlock:^id(AWSTask *task) {
  475. [weakSelf.cache setObject:downloadRequest forKey:cacheKey];
  476. return nil;
  477. }] continueWithSuccessBlock:^id(AWSTask *task) {
  478. return [weakSelf getObject:downloadRequest cacheKey:cacheKey];
  479. }];
  480. return task;
  481. }
  482. - (AWSTask *)getObject:(AWSS3TransferManagerDownloadRequest *)downloadRequest
  483. cacheKey:(NSString *)cacheKey {
  484. AWSS3GetObjectRequest *getObjectRequest = [AWSS3GetObjectRequest new];
  485. [getObjectRequest aws_copyPropertiesFromObject:downloadRequest];
  486. //set the downloadURL to use this tempURL instead.
  487. getObjectRequest.downloadingFileURL = downloadRequest.temporaryFileURL;
  488. __weak AWSS3TransferManager *weakSelf = self;
  489. AWSTask *downloadTask = [[[weakSelf.s3 getObject:getObjectRequest] continueWithBlock:^id(AWSTask *task) {
  490. //delete cached Object if state is not Paused
  491. if (downloadRequest.state != AWSS3TransferManagerRequestStatePaused) {
  492. [weakSelf.cache removeObjectForKey:cacheKey];
  493. }
  494. NSURL *tempFileURL = downloadRequest.temporaryFileURL;
  495. NSURL *originalFileURL = downloadRequest.originalFileURL;
  496. if (task.error) {
  497. //download got error, check if tempFile has been created.
  498. if ([[NSFileManager defaultManager] fileExistsAtPath:tempFileURL.path]) {
  499. AWSDDLogDebug(@"tempFile has not been created yet.");
  500. }
  501. return [AWSTask taskWithError:task.error];
  502. }
  503. //If task complete without error, move the completed file to originalFileURL
  504. BOOL isTempFileExists = [[NSFileManager defaultManager] fileExistsAtPath:tempFileURL.path];
  505. if (isTempFileExists && originalFileURL) {
  506. NSError *error = nil;
  507. //delete the orginalFileURL if it already exists
  508. if ([[NSFileManager defaultManager] fileExistsAtPath:originalFileURL.path]) {
  509. [[NSFileManager defaultManager] removeItemAtPath:originalFileURL.path error:nil];
  510. }
  511. [[NSFileManager defaultManager] moveItemAtURL:tempFileURL
  512. toURL:originalFileURL
  513. error:&error];
  514. if (error) {
  515. //got error when try to move completed file.
  516. return [AWSTask taskWithError:error];
  517. }
  518. }
  519. AWSS3TransferManagerDownloadOutput *downloadOutput = [AWSS3TransferManagerDownloadOutput new];
  520. if (task.result) {
  521. AWSS3GetObjectOutput *getObjectOutput = task.result;
  522. [downloadOutput aws_copyPropertiesFromObject:getObjectOutput];
  523. //set the body to originalFileURL only if tempFileExists(file has been downloaded successfully)
  524. if (isTempFileExists) {
  525. downloadOutput.body = downloadRequest.originalFileURL;
  526. }
  527. }
  528. downloadRequest.state = AWSS3TransferManagerRequestStateCompleted;
  529. [downloadRequest setValue:nil forKey:@"internalRequest"];
  530. return downloadOutput;
  531. }] continueWithBlock:^id(AWSTask *task) {
  532. if (task.error) {
  533. if ([task.error.domain isEqualToString:NSURLErrorDomain]
  534. && task.error.code == NSURLErrorCancelled) {
  535. if (downloadRequest.state == AWSS3TransferManagerRequestStatePaused) {
  536. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain
  537. code:AWSS3TransferManagerErrorPaused
  538. userInfo:task.error.userInfo]];
  539. } else {
  540. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain
  541. code:AWSS3TransferManagerErrorCancelled
  542. userInfo:task.error.userInfo]];
  543. }
  544. } else {
  545. return [AWSTask taskWithError:task.error];
  546. }
  547. } else {
  548. return [AWSTask taskWithResult:task.result];
  549. }
  550. }];
  551. return downloadTask;
  552. }
  553. - (AWSTask *)cancelAll {
  554. NSMutableArray *keys = [NSMutableArray new];
  555. [self.cache.diskCache enumerateObjectsWithBlock:^(AWSTMDiskCache *cache, NSString *key, id<NSCoding> object, NSURL *fileURL) {
  556. [keys addObject:key];
  557. }];
  558. NSMutableArray *tasks = [NSMutableArray new];
  559. for (NSString *key in keys) {
  560. AWSRequest *cachedObject = [self.cache objectForKey:key];
  561. if ([cachedObject isKindOfClass:[AWSS3TransferManagerUploadRequest class]]
  562. || [cachedObject isKindOfClass:[AWSS3TransferManagerDownloadRequest class]]) {
  563. [tasks addObject:[cachedObject cancel]];
  564. }
  565. }
  566. return [AWSTask taskForCompletionOfAllTasks:tasks];
  567. }
  568. - (AWSTask *)pauseAll {
  569. NSMutableArray *keys = [NSMutableArray new];
  570. [self.cache.diskCache enumerateObjectsWithBlock:^(AWSTMDiskCache *cache, NSString *key, id<NSCoding> object, NSURL *fileURL) {
  571. [keys addObject:key];
  572. }];
  573. NSMutableArray *tasks = [NSMutableArray new];
  574. for (NSString *key in keys) {
  575. AWSRequest *cachedObject = [self.cache objectForKey:key];
  576. if ([cachedObject isKindOfClass:[AWSS3TransferManagerUploadRequest class]]
  577. || [cachedObject isKindOfClass:[AWSS3TransferManagerDownloadRequest class]]) {
  578. [tasks addObject:[cachedObject pause]];
  579. }
  580. }
  581. return [AWSTask taskForCompletionOfAllTasks:tasks];
  582. }
  583. - (AWSTask *)resumeAll:(AWSS3TransferManagerResumeAllBlock)block {
  584. NSMutableArray *keys = [NSMutableArray new];
  585. [self.cache.diskCache enumerateObjectsWithBlock:^(AWSTMDiskCache *cache, NSString *key, id<NSCoding> object, NSURL *fileURL) {
  586. [keys addObject:key];
  587. }];
  588. NSMutableArray *tasks = [NSMutableArray new];
  589. NSMutableArray *results = [NSMutableArray new];
  590. __weak AWSS3TransferManager *weakSelf = self;
  591. for (NSString *key in keys) {
  592. id cachedObject = [self.cache objectForKey:key];
  593. if (block) {
  594. if ([cachedObject isKindOfClass:[AWSRequest class]]) {
  595. block(cachedObject);
  596. }
  597. }
  598. if ([cachedObject isKindOfClass:[AWSS3TransferManagerUploadRequest class]]) {
  599. [tasks addObject:[[weakSelf upload:cachedObject cacheKey:key] continueWithSuccessBlock:^id(AWSTask *task) {
  600. [results addObject:task.result];
  601. return nil;
  602. }]];
  603. }
  604. if ([cachedObject isKindOfClass:[AWSS3TransferManagerDownloadRequest class]]) {
  605. [tasks addObject:[[weakSelf download:cachedObject cacheKey:key] continueWithSuccessBlock:^id(AWSTask *task){
  606. [results addObject:task.result];
  607. return nil;
  608. }]];
  609. }
  610. //remove Resumed Object
  611. [weakSelf.cache removeObjectForKey:key];
  612. }
  613. return [[AWSTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(AWSTask *task) {
  614. if (task.error) {
  615. return [AWSTask taskWithError:task.error];
  616. }
  617. return [AWSTask taskWithResult:results];
  618. }];
  619. }
  620. - (AWSTask *)clearCache {
  621. AWSTaskCompletionSource *taskCompletionSource = [AWSTaskCompletionSource new];
  622. [self.cache removeAllObjects:^(AWSTMCache *cache) {
  623. taskCompletionSource.result = nil;
  624. }];
  625. return taskCompletionSource.task;
  626. }
  627. - (void)abortMultipartUploadsForRequest:(AWSS3TransferManagerUploadRequest *)uploadRequest{
  628. AWSS3AbortMultipartUploadRequest *abortMultipartUploadRequest = [AWSS3AbortMultipartUploadRequest new];
  629. abortMultipartUploadRequest.bucket = uploadRequest.bucket;
  630. abortMultipartUploadRequest.key = uploadRequest.key;
  631. abortMultipartUploadRequest.uploadId = uploadRequest.uploadId;
  632. __weak AWSS3TransferManager *weakSelf = self;
  633. [[weakSelf.s3 abortMultipartUpload:abortMultipartUploadRequest] continueWithBlock:^id(AWSTask *task) {
  634. if (task.error) {
  635. AWSDDLogDebug(@"Received response for abortMultipartUpload with Error:%@",task.error);
  636. } else {
  637. AWSDDLogDebug(@"Received response for abortMultipartUpload.");
  638. }
  639. return nil;
  640. }];
  641. }
  642. @end
  643. @implementation AWSS3TransferManagerUploadRequest
  644. @dynamic body;
  645. - (instancetype)init {
  646. if (self = [super init]) {
  647. _state = AWSS3TransferManagerRequestStateNotStarted;
  648. }
  649. return self;
  650. }
  651. - (AWSTask *)cancel {
  652. if (self.state != AWSS3TransferManagerRequestStateCompleted) {
  653. self.state = AWSS3TransferManagerRequestStateCanceling;
  654. NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[[self.body path] stringByResolvingSymlinksInPath]
  655. error:nil];
  656. unsigned long long fileSize = [attributes fileSize];
  657. if (fileSize > AWSS3TransferManagerMinimumPartSize) {
  658. //If using multipart upload, need to cancel current parts upload and send AbortMultiPartUpload Request.
  659. [self.currentUploadingPart cancel];
  660. } else {
  661. //Otherwise, just call super to cancel current task.
  662. return [super cancel];
  663. }
  664. }
  665. return [AWSTask taskWithResult:nil];
  666. }
  667. - (AWSTask *)pause {
  668. switch (self.state) {
  669. case AWSS3TransferManagerRequestStateCompleted: {
  670. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Can not pause a completed task.", nil)]};
  671. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCompleted userInfo:userInfo]];
  672. }
  673. break;
  674. case AWSS3TransferManagerRequestStateCanceling: {
  675. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Can not pause a cancelled task.", nil)]};
  676. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCancelled userInfo:userInfo]];
  677. }
  678. break;
  679. default: {
  680. //change state to Paused
  681. self.state = AWSS3TransferManagerRequestStatePaused;
  682. //pause the current uploadTask
  683. NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[[self.body path] stringByResolvingSymlinksInPath]
  684. error:nil];
  685. unsigned long long fileSize = [attributes fileSize];
  686. if (fileSize > AWSS3TransferManagerMinimumPartSize) {
  687. //If using multipart upload, need to check state flag and then pause the current parts upload and save the current status.
  688. [self.currentUploadingPart pause];
  689. } else {
  690. //otherwise, pause the current task. (cancel without set isCancelled flag)
  691. [super pause];
  692. }
  693. return [AWSTask taskWithResult:nil];
  694. }
  695. break;
  696. }
  697. }
  698. @end
  699. @implementation AWSS3TransferManagerUploadOutput
  700. @end
  701. @implementation AWSS3TransferManagerDownloadRequest
  702. - (instancetype)init {
  703. if (self = [super init]) {
  704. _state = AWSS3TransferManagerRequestStateNotStarted;
  705. }
  706. return self;
  707. }
  708. - (AWSTask *)cancel {
  709. if (self.state != AWSS3TransferManagerRequestStateCompleted) {
  710. self.state = AWSS3TransferManagerRequestStateCanceling;
  711. return [super cancel];
  712. }
  713. return [AWSTask taskWithResult:nil];
  714. }
  715. - (AWSTask *)pause {
  716. switch (self.state) {
  717. case AWSS3TransferManagerRequestStateCompleted: {
  718. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Can not pause a completed task.", nil)]};
  719. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCompleted userInfo:userInfo]];
  720. }
  721. break;
  722. case AWSS3TransferManagerRequestStateCanceling: {
  723. NSDictionary *userInfo = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Can not pause a cancelled task.", nil)]};
  724. return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferManagerErrorDomain code:AWSS3TransferManagerErrorCancelled userInfo:userInfo]];
  725. }
  726. break;
  727. default: {
  728. //change state to Paused
  729. self.state = AWSS3TransferManagerRequestStatePaused;
  730. //pause the current download task (i.e. cancel without set the isCancelled flag)
  731. [super pause];
  732. return [AWSTask taskWithResult:nil];
  733. }
  734. break;
  735. }
  736. }
  737. @end
  738. @implementation AWSS3TransferManagerDownloadOutput
  739. @end