FIRStorageUploadTask.m 7.8 KB

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