FIRStorageUploadTask.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // Copyright 2017 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FIRStorageUploadTask.h"
  15. #import "FIRStorageConstants_Private.h"
  16. #import "FIRStorageMetadata_Private.h"
  17. #import "FIRStorageObservableTask_Private.h"
  18. #import "FIRStorageTask_Private.h"
  19. #import "FIRStorageUploadTask_Private.h"
  20. #import <GTMSessionFetcher/GTMSessionUploadFetcher.h>
  21. @implementation FIRStorageUploadTask
  22. @synthesize progress = _progress;
  23. @synthesize fetcherCompletion = _fetcherCompletion;
  24. - (instancetype)initWithReference:(FIRStorageReference *)reference
  25. fetcherService:(GTMSessionFetcherService *)service
  26. dispatchQueue:(dispatch_queue_t)queue
  27. data:(NSData *)uploadData
  28. metadata:(FIRStorageMetadata *)metadata {
  29. self = [super initWithReference:reference fetcherService:service dispatchQueue:queue];
  30. if (self) {
  31. _uploadMetadata = [metadata copy];
  32. _uploadData = [uploadData copy];
  33. _progress = [NSProgress progressWithTotalUnitCount:[_uploadData length]];
  34. if (!_uploadMetadata.contentType) {
  35. _uploadMetadata.contentType = @"application/octet-stream";
  36. }
  37. }
  38. return self;
  39. }
  40. - (instancetype)initWithReference:(FIRStorageReference *)reference
  41. fetcherService:(GTMSessionFetcherService *)service
  42. dispatchQueue:(dispatch_queue_t)queue
  43. file:(NSURL *)fileURL
  44. metadata:(FIRStorageMetadata *)metadata {
  45. self = [super initWithReference:reference fetcherService:service dispatchQueue:queue];
  46. if (self) {
  47. _uploadMetadata = [metadata copy];
  48. _fileURL = [fileURL copy];
  49. _progress = [NSProgress progressWithTotalUnitCount:0];
  50. NSString *mimeType = [FIRStorageUtils MIMETypeForExtension:[_fileURL pathExtension]];
  51. if (!_uploadMetadata.contentType) {
  52. _uploadMetadata.contentType = mimeType ?: @"application/octet-stream";
  53. }
  54. }
  55. return self;
  56. }
  57. - (void)dealloc {
  58. [_uploadFetcher stopFetching];
  59. }
  60. - (void)enqueue {
  61. __weak FIRStorageUploadTask *weakSelf = self;
  62. [self dispatchAsync:^() {
  63. FIRStorageUploadTask *strongSelf = weakSelf;
  64. if (!strongSelf) {
  65. return;
  66. }
  67. NSError *contentValidationError;
  68. if (![strongSelf isContentToUploadValid:&contentValidationError]) {
  69. strongSelf.error = contentValidationError;
  70. [strongSelf finishTaskWithStatus:FIRStorageTaskStatusFailure snapshot:strongSelf.snapshot];
  71. return;
  72. }
  73. strongSelf.state = FIRStorageTaskStateQueueing;
  74. NSMutableURLRequest *request = [strongSelf.baseRequest mutableCopy];
  75. request.HTTPMethod = @"POST";
  76. request.timeoutInterval = strongSelf.reference.storage.maxUploadRetryTime;
  77. NSData *bodyData =
  78. [NSData frs_dataFromJSONDictionary:[strongSelf->_uploadMetadata dictionaryRepresentation]];
  79. request.HTTPBody = bodyData;
  80. [request setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
  81. NSString *contentLengthString =
  82. [NSString stringWithFormat:@"%zu", (unsigned long)[bodyData length]];
  83. [request setValue:contentLengthString forHTTPHeaderField:@"Content-Length"];
  84. NSURLComponents *components = [NSURLComponents componentsWithURL:request.URL
  85. resolvingAgainstBaseURL:NO];
  86. if ([components.host isEqual:kGCSHost]) {
  87. [components setPercentEncodedPath:[@"/upload" stringByAppendingString:components.path]];
  88. }
  89. NSDictionary *queryParams = @{@"uploadType" : @"resumable", @"name" : self.uploadMetadata.path};
  90. [components setPercentEncodedQuery:[FIRStorageUtils queryStringForDictionary:queryParams]];
  91. request.URL = components.URL;
  92. GTMSessionUploadFetcher *uploadFetcher =
  93. [GTMSessionUploadFetcher uploadFetcherWithRequest:request
  94. uploadMIMEType:strongSelf->_uploadMetadata.contentType
  95. chunkSize:kGTMSessionUploadFetcherStandardChunkSize
  96. fetcherService:self.fetcherService];
  97. if (strongSelf->_uploadData) {
  98. [uploadFetcher setUploadData:strongSelf->_uploadData];
  99. uploadFetcher.comment = @"Data UploadTask";
  100. } else if (strongSelf->_fileURL) {
  101. [uploadFetcher setUploadFileURL:strongSelf->_fileURL];
  102. uploadFetcher.comment = @"File UploadTask";
  103. }
  104. uploadFetcher.maxRetryInterval = self.reference.storage.maxUploadRetryTime;
  105. [uploadFetcher setSendProgressBlock:^(int64_t bytesSent, int64_t totalBytesSent,
  106. int64_t totalBytesExpectedToSend) {
  107. weakSelf.state = FIRStorageTaskStateProgress;
  108. weakSelf.progress.completedUnitCount = totalBytesSent;
  109. weakSelf.progress.totalUnitCount = totalBytesExpectedToSend;
  110. weakSelf.metadata = self->_uploadMetadata;
  111. [weakSelf fireHandlersForStatus:FIRStorageTaskStatusProgress snapshot:weakSelf.snapshot];
  112. weakSelf.state = FIRStorageTaskStateRunning;
  113. }];
  114. strongSelf->_uploadFetcher = uploadFetcher;
  115. // Process fetches
  116. strongSelf.state = FIRStorageTaskStateRunning;
  117. #pragma clang diagnostic push
  118. #pragma clang diagnostic ignored "-Warc-retain-cycles"
  119. strongSelf->_fetcherCompletion = ^(NSData *_Nullable data, NSError *_Nullable error) {
  120. // Fire last progress updates
  121. [self fireHandlersForStatus:FIRStorageTaskStatusProgress snapshot:self.snapshot];
  122. // Handle potential issues with upload
  123. if (error) {
  124. self.state = FIRStorageTaskStateFailed;
  125. self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference];
  126. self.metadata = self->_uploadMetadata;
  127. [self finishTaskWithStatus:FIRStorageTaskStatusFailure snapshot:self.snapshot];
  128. return;
  129. }
  130. // Upload completed successfully, fire completion callbacks
  131. self.state = FIRStorageTaskStateSuccess;
  132. NSDictionary *responseDictionary = [NSDictionary frs_dictionaryFromJSONData:data];
  133. if (responseDictionary) {
  134. FIRStorageMetadata *metadata =
  135. [[FIRStorageMetadata alloc] initWithDictionary:responseDictionary];
  136. [metadata setType:FIRStorageMetadataTypeFile];
  137. self.metadata = metadata;
  138. } else {
  139. self.error = [FIRStorageErrors errorWithInvalidRequest:data];
  140. }
  141. [self finishTaskWithStatus:FIRStorageTaskStatusSuccess snapshot:self.snapshot];
  142. };
  143. #pragma clang diagnostic pop
  144. [strongSelf->_uploadFetcher
  145. beginFetchWithCompletionHandler:^(NSData *_Nullable data, NSError *_Nullable error) {
  146. weakSelf.fetcherCompletion(data, error);
  147. }];
  148. }];
  149. }
  150. - (void)finishTaskWithStatus:(FIRStorageTaskStatus)status
  151. snapshot:(FIRStorageTaskSnapshot *)snapshot {
  152. [self fireHandlersForStatus:status snapshot:self.snapshot];
  153. [self removeAllObservers];
  154. self->_fetcherCompletion = nil;
  155. }
  156. - (BOOL)isContentToUploadValid:(NSError **)outError {
  157. if (_uploadData != nil) {
  158. return YES;
  159. }
  160. NSError *fileReachabilityError;
  161. if (![_fileURL checkResourceIsReachableAndReturnError:&fileReachabilityError]) {
  162. if (outError != NULL) {
  163. NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:2];
  164. userInfo[NSLocalizedDescriptionKey] =
  165. [NSString stringWithFormat:@"File at URL: %@ is not reachable.", _fileURL.absoluteString];
  166. if (fileReachabilityError) {
  167. userInfo[NSUnderlyingErrorKey] = fileReachabilityError;
  168. }
  169. *outError = [NSError errorWithDomain:FIRStorageErrorDomain
  170. code:FIRStorageErrorCodeUnknown
  171. userInfo:userInfo];
  172. }
  173. return NO;
  174. }
  175. return YES;
  176. }
  177. #pragma mark - Upload Management
  178. - (void)cancel {
  179. __weak FIRStorageUploadTask *weakSelf = self;
  180. [self dispatchAsync:^() {
  181. weakSelf.state = FIRStorageTaskStateCancelled;
  182. [weakSelf.uploadFetcher stopFetching];
  183. if (weakSelf.state != FIRStorageTaskStateSuccess) {
  184. weakSelf.metadata = weakSelf.uploadMetadata;
  185. }
  186. weakSelf.error = [FIRStorageErrors errorWithCode:FIRStorageErrorCodeCancelled];
  187. [weakSelf fireHandlersForStatus:FIRStorageTaskStatusFailure snapshot:weakSelf.snapshot];
  188. }];
  189. }
  190. - (void)pause {
  191. __weak FIRStorageUploadTask *weakSelf = self;
  192. [self dispatchAsync:^() {
  193. weakSelf.state = FIRStorageTaskStatePaused;
  194. [weakSelf.uploadFetcher pauseFetching];
  195. if (weakSelf.state != FIRStorageTaskStateSuccess) {
  196. weakSelf.metadata = weakSelf.uploadMetadata;
  197. }
  198. [weakSelf fireHandlersForStatus:FIRStorageTaskStatusPause snapshot:weakSelf.snapshot];
  199. }];
  200. }
  201. - (void)resume {
  202. __weak FIRStorageUploadTask *weakSelf = self;
  203. [self dispatchAsync:^() {
  204. weakSelf.state = FIRStorageTaskStateResuming;
  205. [weakSelf.uploadFetcher resumeFetching];
  206. if (weakSelf.state != FIRStorageTaskStateSuccess) {
  207. weakSelf.metadata = weakSelf.uploadMetadata;
  208. }
  209. [weakSelf fireHandlersForStatus:FIRStorageTaskStatusResume snapshot:weakSelf.snapshot];
  210. weakSelf.state = FIRStorageTaskStateRunning;
  211. }];
  212. }
  213. @end