FIRStorageUploadTask.m 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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 "GTMSessionUploadFetcher.h"
  21. @implementation FIRStorageUploadTask
  22. @synthesize progress = _progress;
  23. @synthesize fetcherCompletion = _fetcherCompletion;
  24. - (instancetype)initWithReference:(FIRStorageReference *)reference
  25. fetcherService:(GTMSessionFetcherService *)service
  26. data:(NSData *)uploadData
  27. metadata:(FIRStorageMetadata *)metadata {
  28. self = [super initWithReference:reference fetcherService:service];
  29. if (self) {
  30. _uploadMetadata = [metadata copy];
  31. _uploadData = [uploadData copy];
  32. _progress = [NSProgress progressWithTotalUnitCount:[_uploadData length]];
  33. if (!_uploadMetadata.contentType) {
  34. _uploadMetadata.contentType = @"application/octet-stream";
  35. }
  36. }
  37. return self;
  38. }
  39. - (instancetype)initWithReference:(FIRStorageReference *)reference
  40. fetcherService:(GTMSessionFetcherService *)service
  41. file:(NSURL *)fileURL
  42. metadata:(FIRStorageMetadata *)metadata {
  43. self = [super initWithReference:reference fetcherService:service];
  44. if (self) {
  45. _uploadMetadata = [metadata copy];
  46. _fileURL = [fileURL copy];
  47. _progress = [NSProgress progressWithTotalUnitCount:0];
  48. NSString *mimeType = [FIRStorageUtils MIMETypeForExtension:[_fileURL pathExtension]];
  49. if (!_uploadMetadata.contentType) {
  50. _uploadMetadata.contentType = mimeType ?: @"application/octet-stream";
  51. }
  52. }
  53. return self;
  54. }
  55. - (void)dealloc {
  56. [_uploadFetcher stopFetching];
  57. }
  58. - (void)enqueue {
  59. NSAssert([NSThread isMainThread],
  60. @"Upload attempting to execute on non main queue! Please only "
  61. @"execute this method on the main queue.");
  62. self.state = FIRStorageTaskStateQueueing;
  63. NSMutableURLRequest *request = [self.baseRequest mutableCopy];
  64. request.HTTPMethod = @"POST";
  65. request.timeoutInterval = self.reference.storage.maxUploadRetryTime;
  66. NSData *bodyData = [NSData frs_dataFromJSONDictionary:[_uploadMetadata dictionaryRepresentation]];
  67. request.HTTPBody = bodyData;
  68. [request setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
  69. NSString *contentLengthString =
  70. [NSString stringWithFormat:@"%zu", (unsigned long)[bodyData length]];
  71. [request setValue:contentLengthString forHTTPHeaderField:@"Content-Length"];
  72. NSURLComponents *components =
  73. [NSURLComponents componentsWithURL:request.URL resolvingAgainstBaseURL:NO];
  74. if ([components.host isEqual:kGCSHost]) {
  75. [components setPercentEncodedPath:[@"/upload" stringByAppendingString:components.path]];
  76. }
  77. NSDictionary *queryParams = @{@"uploadType" : @"resumable", @"name" : self.uploadMetadata.path};
  78. [components setPercentEncodedQuery:[FIRStorageUtils queryStringForDictionary:queryParams]];
  79. request.URL = components.URL;
  80. GTMSessionUploadFetcher *uploadFetcher =
  81. [GTMSessionUploadFetcher uploadFetcherWithRequest:request
  82. uploadMIMEType:_uploadMetadata.contentType
  83. chunkSize:kGTMSessionUploadFetcherStandardChunkSize
  84. fetcherService:self.fetcherService];
  85. if (_uploadData) {
  86. [uploadFetcher setUploadData:_uploadData];
  87. uploadFetcher.comment = @"Data UploadTask";
  88. } else if (_fileURL) {
  89. [uploadFetcher setUploadFileURL:_fileURL];
  90. uploadFetcher.comment = @"File UploadTask";
  91. }
  92. uploadFetcher.maxRetryInterval = self.reference.storage.maxUploadRetryTime;
  93. __weak FIRStorageUploadTask *weakSelf = self;
  94. [uploadFetcher setSendProgressBlock:^(int64_t bytesSent, int64_t totalBytesSent,
  95. int64_t totalBytesExpectedToSend) {
  96. weakSelf.state = FIRStorageTaskStateProgress;
  97. weakSelf.progress.completedUnitCount = totalBytesSent;
  98. weakSelf.progress.totalUnitCount = totalBytesExpectedToSend;
  99. weakSelf.metadata = self->_uploadMetadata;
  100. [weakSelf fireHandlersForStatus:FIRStorageTaskStatusProgress snapshot:weakSelf.snapshot];
  101. weakSelf.state = FIRStorageTaskStateRunning;
  102. }];
  103. _uploadFetcher = uploadFetcher;
  104. // Process fetches
  105. self.state = FIRStorageTaskStateRunning;
  106. #pragma clang diagnostic push
  107. #pragma clang diagnostic ignored "-Warc-retain-cycles"
  108. _fetcherCompletion = ^(NSData *_Nullable data, NSError *_Nullable error) {
  109. // Fire last progress updates
  110. [self fireHandlersForStatus:FIRStorageTaskStatusProgress snapshot:self.snapshot];
  111. // Handle potential issues with upload
  112. if (error) {
  113. self.state = FIRStorageTaskStateFailed;
  114. self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference];
  115. self.metadata = self->_uploadMetadata;
  116. [self fireHandlersForStatus:FIRStorageTaskStatusFailure snapshot:self.snapshot];
  117. [self removeAllObservers];
  118. self->_fetcherCompletion = nil;
  119. return;
  120. }
  121. // Upload completed successfully, fire completion callbacks
  122. self.state = FIRStorageTaskStateSuccess;
  123. NSDictionary *responseDictionary = [NSDictionary frs_dictionaryFromJSONData:data];
  124. if (responseDictionary) {
  125. FIRStorageMetadata *metadata =
  126. [[FIRStorageMetadata alloc] initWithDictionary:responseDictionary];
  127. [metadata setType:FIRStorageMetadataTypeFile];
  128. self.metadata = metadata;
  129. } else {
  130. self.error = [FIRStorageErrors errorWithInvalidRequest:data];
  131. }
  132. [self fireHandlersForStatus:FIRStorageTaskStatusSuccess snapshot:self.snapshot];
  133. [self removeAllObservers];
  134. self->_fetcherCompletion = nil;
  135. };
  136. #pragma clang diagnostic pop
  137. [_uploadFetcher
  138. beginFetchWithCompletionHandler:^(NSData *_Nullable data, NSError *_Nullable error) {
  139. weakSelf.fetcherCompletion(data, error);
  140. }];
  141. }
  142. #pragma mark - Upload Management
  143. - (void)cancel {
  144. NSAssert([NSThread isMainThread],
  145. @"Cancel attempting to execute on non main queue! Please only "
  146. @"execute this method on the main queue.");
  147. self.state = FIRStorageTaskStateCancelled;
  148. [_uploadFetcher stopFetching];
  149. if (self.state != FIRStorageTaskStateSuccess) {
  150. self.metadata = _uploadMetadata;
  151. }
  152. self.error = [FIRStorageErrors errorWithCode:FIRStorageErrorCodeCancelled];
  153. [self fireHandlersForStatus:FIRStorageTaskStatusFailure snapshot:self.snapshot];
  154. }
  155. - (void)pause {
  156. NSAssert([NSThread isMainThread],
  157. @"Pause attempting to execute on non main queue! Please only "
  158. @"execute this method on the main queue.");
  159. self.state = FIRStorageTaskStatePaused;
  160. [_uploadFetcher pauseFetching];
  161. if (self.state != FIRStorageTaskStateSuccess) {
  162. self.metadata = _uploadMetadata;
  163. }
  164. [self fireHandlersForStatus:FIRStorageTaskStatusPause snapshot:self.snapshot];
  165. }
  166. - (void)resume {
  167. NSAssert([NSThread isMainThread],
  168. @"Resume attempting to execute on non main queue! Please only "
  169. @"execute this method on the main queue.");
  170. self.state = FIRStorageTaskStateResuming;
  171. [_uploadFetcher resumeFetching];
  172. if (self.state != FIRStorageTaskStateSuccess) {
  173. self.metadata = _uploadMetadata;
  174. }
  175. [self fireHandlersForStatus:FIRStorageTaskStatusResume snapshot:self.snapshot];
  176. self.state = FIRStorageTaskStateRunning;
  177. }
  178. @end