react-native-blob-util 0.22.0 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.eslintrc.js +56 -56
  2. package/LICENSE +21 -21
  3. package/Migration.md +41 -41
  4. package/NuGet.config +11 -11
  5. package/README.md +1109 -1104
  6. package/android/gradle.properties +1 -1
  7. package/android/src/main/AndroidManifest.xml +37 -37
  8. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +57 -57
  9. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConst.java +20 -20
  10. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +9 -9
  11. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilImpl.java +431 -431
  12. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilProgressConfig.java +39 -39
  13. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +1014 -1014
  14. package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilDefaultResp.java +98 -98
  15. package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +166 -166
  16. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileDescription.java +19 -19
  17. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileProvider.java +4 -4
  18. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/MimeType.java +70 -70
  19. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/PathResolver.java +223 -223
  20. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/Tls12SocketFactory.java +68 -68
  21. package/android/src/main/res/values/strings.xml +3 -3
  22. package/android/src/main/res/xml/provider_paths.xml +12 -12
  23. package/android.js +58 -58
  24. package/class/ReactNativeBlobUtilCanceledFetchError.js +9 -9
  25. package/class/ReactNativeBlobUtilFile.js +5 -5
  26. package/class/ReactNativeBlobUtilReadStream.js +84 -84
  27. package/class/ReactNativeBlobUtilSession.js +68 -68
  28. package/class/ReactNativeBlobUtilWriteStream.js +50 -50
  29. package/class/StatefulPromise.js +7 -7
  30. package/codegenSpecs/NativeBlobUtils.js +86 -86
  31. package/components/Fetch.onPress.js +10 -10
  32. package/components/Fetch.when.js +15 -15
  33. package/fetch.js +350 -350
  34. package/fs.js +480 -480
  35. package/index.js +64 -64
  36. package/index.js.flow +192 -192
  37. package/index.web.js +1 -1
  38. package/ios/PrivacyInfo.xcprivacy +28 -28
  39. package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.h +55 -55
  40. package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.mm +992 -992
  41. package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +405 -405
  42. package/ios/ReactNativeBlobUtilConst.h +58 -58
  43. package/ios/ReactNativeBlobUtilConst.mm +47 -47
  44. package/ios/ReactNativeBlobUtilFS.h +108 -108
  45. package/ios/ReactNativeBlobUtilFS.mm +1064 -1064
  46. package/ios/ReactNativeBlobUtilFileTransformer.h +23 -23
  47. package/ios/ReactNativeBlobUtilFileTransformer.mm +20 -20
  48. package/ios/ReactNativeBlobUtilNetwork.h +49 -49
  49. package/ios/ReactNativeBlobUtilNetwork.mm +159 -159
  50. package/ios/ReactNativeBlobUtilProgress.h +32 -32
  51. package/ios/ReactNativeBlobUtilProgress.mm +57 -57
  52. package/ios/ReactNativeBlobUtilReqBuilder.h +37 -37
  53. package/ios/ReactNativeBlobUtilReqBuilder.mm +292 -292
  54. package/ios/ReactNativeBlobUtilRequest.h +48 -48
  55. package/ios/ReactNativeBlobUtilRequest.mm +584 -584
  56. package/ios.js +61 -61
  57. package/json-stream.js +42 -42
  58. package/lib/oboe-browser.js +2703 -2703
  59. package/mediacollection.js +36 -36
  60. package/package.json +68 -68
  61. package/polyfill/Blob.js +362 -362
  62. package/polyfill/Event.js +11 -11
  63. package/polyfill/EventTarget.js +78 -78
  64. package/polyfill/Fetch.js +219 -219
  65. package/polyfill/File.js +27 -27
  66. package/polyfill/FileReader.js +91 -91
  67. package/polyfill/ProgressEvent.js +32 -32
  68. package/polyfill/XMLHttpRequest.js +466 -466
  69. package/polyfill/XMLHttpRequestEventTarget.js +126 -126
  70. package/polyfill/index.js +11 -11
  71. package/react-native-blob-util.podspec +48 -48
  72. package/types.js +69 -69
  73. package/utils/log.js +40 -40
  74. package/utils/unicode.js +7 -7
  75. package/utils/uri.js +33 -33
  76. package/utils/uuid.js +4 -4
  77. package/windows/ExperimentalFeatures.props +33 -33
  78. package/windows/README.md +2 -2
  79. package/windows/ReactNativeBlobUtil/PropertySheet.props +15 -15
  80. package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.cpp +1836 -1836
  81. package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.def +3 -3
  82. package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.h +372 -372
  83. package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj +139 -139
  84. package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj.filters +43 -43
  85. package/windows/ReactNativeBlobUtil/ReactPackageProvider.cpp +20 -20
  86. package/windows/ReactNativeBlobUtil/ReactPackageProvider.h +24 -24
  87. package/windows/ReactNativeBlobUtil/ReactPackageProvider.idl +9 -9
  88. package/windows/ReactNativeBlobUtil/codegen/.clang-format +1 -1
  89. package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsDataTypes.g.h +42 -42
  90. package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsSpec.g.h +353 -353
  91. package/windows/ReactNativeBlobUtil/packages.config +3 -3
  92. package/windows/ReactNativeBlobUtil/packages.lock.json +59 -59
  93. package/windows/ReactNativeBlobUtil/pch.cpp +1 -1
  94. package/windows/ReactNativeBlobUtil/pch.h +30 -30
  95. package/windows/ReactNativeBlobUtil/resource.h +5 -5
  96. package/windows/ReactNativeBlobUtil/targetver.h +8 -8
  97. package/windows/ReactNativeBlobUtil.sln +43 -43
@@ -1,584 +1,584 @@
1
- //
2
- // ReactNativeBlobUtilRequest.m
3
- // ReactNativeBlobUtil
4
- //
5
- // Created by Artur Chrusciel on 15.01.18.
6
- // Copyright © 2018 wkh237.github.io. All rights reserved.
7
- //
8
-
9
- #import "ReactNativeBlobUtilRequest.h"
10
-
11
- #import "ReactNativeBlobUtilFS.h"
12
- #import "ReactNativeBlobUtilConst.h"
13
- #import "ReactNativeBlobUtilFileTransformer.h"
14
- #import "ReactNativeBlobUtilReqBuilder.h"
15
-
16
- #import <CommonCrypto/CommonDigest.h>
17
-
18
-
19
- typedef NS_ENUM(NSUInteger, ResponseFormat) {
20
- UTF8,
21
- BASE64,
22
- AUTO
23
- };
24
-
25
- @interface ReactNativeBlobUtilRequest ()
26
- {
27
- BOOL respFile;
28
- BOOL isNewPart;
29
- BOOL isIncrement;
30
- NSMutableData * partBuffer;
31
- NSString * destPath;
32
- NSOutputStream * writeStream;
33
- long bodyLength;
34
- NSInteger respStatus;
35
- NSMutableArray * redirects;
36
- ResponseFormat responseFormat;
37
- BOOL followRedirect;
38
- BOOL backgroundTask;
39
- }
40
-
41
- @end
42
-
43
- @implementation ReactNativeBlobUtilRequest
44
-
45
- @synthesize taskId;
46
- @synthesize expectedBytes;
47
- @synthesize receivedBytes;
48
- @synthesize respData;
49
- @synthesize callback;
50
- @synthesize baseModule;
51
- @synthesize options;
52
- @synthesize error;
53
-
54
-
55
- - (NSString *)md5:(NSString *)input {
56
- const char* str = [input UTF8String];
57
- unsigned char result[CC_MD5_DIGEST_LENGTH];
58
- CC_MD5(str, (CC_LONG)strlen(str), result);
59
-
60
- NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
61
- for (int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
62
- [ret appendFormat:@"%02x",result[i]];
63
- }
64
- return ret;
65
- }
66
-
67
- // send HTTP request
68
- - (void) sendRequest:(__weak NSDictionary * _Nullable )options
69
- contentLength:(long) contentLength
70
- baseModule:(__strong ReactNativeBlobUtil * _Nullable)baseModule
71
- taskId:(NSString * _Nullable)taskId
72
- withRequest:(__weak NSURLRequest * _Nullable)req
73
- taskOperationQueue:(NSOperationQueue * _Nonnull)operationQueue
74
- callback:(_Nullable RCTResponseSenderBlock) callback
75
- {
76
- self.taskId = taskId;
77
- self.respData = [[NSMutableData alloc] initWithLength:0];
78
- self.callback = callback;
79
- self.baseModule = baseModule;
80
- self.expectedBytes = 0;
81
- self.receivedBytes = 0;
82
- self.options = options;
83
-
84
- backgroundTask = [[options valueForKey:@"IOSBackgroundTask"] boolValue];
85
- // when followRedirect not set in options, defaults to TRUE
86
- followRedirect = [options valueForKey:@"followRedirect"] == nil ? YES : [[options valueForKey:@"followRedirect"] boolValue];
87
- isIncrement = [[options valueForKey:@"increment"] boolValue];
88
- redirects = [[NSMutableArray alloc] init];
89
-
90
- if (req.URL) {
91
- [redirects addObject:req.URL.absoluteString];
92
- }
93
-
94
- // set response format
95
- NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
96
-
97
- if ([[rnfbResp lowercaseString] isEqualToString:@"base64"]) {
98
- responseFormat = BASE64;
99
- } else if ([[rnfbResp lowercaseString] isEqualToString:@"utf8"]) {
100
- responseFormat = UTF8;
101
- } else {
102
- responseFormat = AUTO;
103
- }
104
-
105
- NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
106
- NSString * key = [self.options valueForKey:CONFIG_KEY];
107
- NSURLSession * session;
108
-
109
- bodyLength = contentLength;
110
-
111
- // the session trust any SSL certification
112
- NSURLSessionConfiguration *defaultConfigObject;
113
-
114
- defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
115
-
116
- if (backgroundTask) {
117
- defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
118
- }
119
-
120
- // request timeout, -1 if not set in options
121
- float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
122
-
123
- if (timeout > 0) {
124
- defaultConfigObject.timeoutIntervalForRequest = timeout/1000;
125
- }
126
-
127
- if([options valueForKey:CONFIG_WIFI_ONLY] != nil && [options[CONFIG_WIFI_ONLY] boolValue]){
128
- [defaultConfigObject setAllowsCellularAccess:NO];
129
- }
130
-
131
- defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
132
- session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue];
133
-
134
- if (path || [self.options valueForKey:CONFIG_USE_TEMP]) {
135
- respFile = YES;
136
-
137
- NSString* cacheKey = taskId;
138
- if (key) {
139
- cacheKey = [self md5:key];
140
-
141
- if (!cacheKey) {
142
- cacheKey = taskId;
143
- }
144
-
145
- destPath = [ReactNativeBlobUtilFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
146
-
147
- if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
148
- callback(@[[NSNull null], RESP_TYPE_PATH, destPath]);
149
-
150
- return;
151
- }
152
- }
153
-
154
- if (path) {
155
- destPath = path;
156
- } else {
157
- destPath = [ReactNativeBlobUtilFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
158
- }
159
-
160
- // We still need to initialize this as a placeholder in memory before we perform
161
- // the conversion
162
- if ([self ShouldTransformFile]) {
163
- respData = [[NSMutableData alloc] init];
164
- }
165
- } else {
166
- respData = [[NSMutableData alloc] init];
167
- respFile = NO;
168
- }
169
-
170
- if(backgroundTask) {
171
- NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:req];
172
- [task resume];
173
- self.task = task;
174
- } else {
175
- NSURLSessionDataTask *task = [session dataTaskWithRequest:req];
176
- [task resume];
177
- self.task = task;
178
- }
179
-
180
- // network status indicator
181
- if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
182
- dispatch_async(dispatch_get_main_queue(), ^{
183
- [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
184
- });
185
- }
186
- }
187
-
188
- ////////////////////////////////////////
189
- //
190
- // NSURLSession delegates
191
- //
192
- ////////////////////////////////////////
193
-
194
-
195
- #pragma mark NSURLSession delegate methods
196
-
197
- - (void)configureWriteStream {
198
- if (respFile)
199
- {
200
- @try{
201
- NSFileManager * fm = [NSFileManager defaultManager];
202
- NSString * folder = [destPath stringByDeletingLastPathComponent];
203
-
204
- if (![fm fileExistsAtPath:folder]) {
205
- [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
206
- }
207
-
208
- // if not set overwrite in options, defaults to TRUE
209
- BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
210
- BOOL appendToExistingFile = [destPath containsString:@"?append=true"];
211
-
212
- appendToExistingFile = !overwrite;
213
-
214
- // For solving #141 append response data if the file already exists
215
- // base on PR#139 @kejinliang
216
- if (appendToExistingFile) {
217
- destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
218
- }
219
-
220
- if (![fm fileExistsAtPath:destPath]) {
221
- [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
222
- }
223
-
224
- writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
225
- [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
226
- [writeStream open];
227
- }
228
- @catch(NSException * ex)
229
- {
230
- NSLog(@"write file error");
231
- }
232
- }
233
- }
234
-
235
- - (void)processData:(NSData *)data {
236
- if (respFile && ![self ShouldTransformFile]) {
237
- [writeStream write:(const uint8_t *)[data bytes] maxLength:[data length]];
238
- } else {
239
- [respData appendData:data];
240
- }
241
- }
242
-
243
-
244
- #pragma mark - Received Response
245
- // set expected content length on response received
246
- - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
247
- {
248
- expectedBytes = [response expectedContentLength];
249
-
250
- NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
251
- NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
252
- NSString * respType = @"";
253
- respStatus = statusCode;
254
-
255
- if ([response respondsToSelector:@selector(allHeaderFields)])
256
- {
257
- NSDictionary *headers = [httpResponse allHeaderFields];
258
- NSString * respCType = [[ReactNativeBlobUtilReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
259
-
260
- if (self.isServerPush) {
261
- if (partBuffer) {
262
- [self.baseModule
263
- emitEventDict:EVENT_SERVER_PUSH
264
- body:@{
265
- @"taskId": taskId,
266
- @"chunk": [partBuffer base64EncodedStringWithOptions:0],
267
- }
268
- ];
269
- }
270
-
271
- partBuffer = [[NSMutableData alloc] init];
272
- completionHandler(NSURLSessionResponseAllow);
273
-
274
- return;
275
- } else {
276
- self.isServerPush = [[respCType lowercaseString] containsString:@"multipart/x-mixed-replace;"];
277
- }
278
-
279
- if(respCType)
280
- {
281
- NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
282
-
283
- if ([respCType containsString:@"text/"]) {
284
- respType = @"text";
285
- } else if ([respCType containsString:@"application/json"]) {
286
- respType = @"json";
287
- } else if(extraBlobCTypes) { // If extra blob content type is not empty, check if response type matches
288
- for (NSString * substr in extraBlobCTypes) {
289
- if ([respCType containsString:[substr lowercaseString]]) {
290
- respType = @"blob";
291
- respFile = YES;
292
- destPath = [ReactNativeBlobUtilFS getTempPath:taskId withExtension:nil];
293
- break;
294
- }
295
- }
296
- } else {
297
- respType = @"blob";
298
-
299
- // for XMLHttpRequest, switch response data handling strategy automatically
300
- if ([options valueForKey:@"auto"]) {
301
- respFile = YES;
302
- destPath = [ReactNativeBlobUtilFS getTempPath:taskId withExtension:@""];
303
- }
304
- }
305
- } else {
306
- respType = @"text";
307
- }
308
-
309
- #pragma mark - handling cookies
310
- // # 153 get cookies
311
- if (response.URL) {
312
- NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
313
- NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
314
- if (cookies.count) {
315
- [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil];
316
- }
317
- }
318
- [self.baseModule emitEventDict : EVENT_STATE_CHANGE
319
- body:@{
320
- @"taskId": taskId,
321
- @"state": @"2",
322
- @"headers": headers,
323
- @"redirects": redirects,
324
- @"respType" : respType,
325
- @"timeout" : @NO,
326
- @"status": [NSNumber numberWithInteger:statusCode]
327
- }
328
- ];
329
- } else {
330
- NSLog(@"oops");
331
- }
332
-
333
- [self configureWriteStream];
334
-
335
- completionHandler(NSURLSessionResponseAllow);
336
- }
337
-
338
-
339
- // download progress handler
340
- - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
341
- {
342
- // For #143 handling multipart/x-mixed-replace response
343
- if (self.isServerPush)
344
- {
345
- [partBuffer appendData:data];
346
-
347
- return ;
348
- }
349
-
350
- NSNumber * received = [NSNumber numberWithLong:[data length]];
351
- receivedBytes += [received longValue];
352
- NSString * chunkString = @"";
353
-
354
- if (isIncrement) {
355
- chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
356
- }
357
-
358
- // If we need to process the data, we defer writing into the file until the we have all the data, at which point
359
- // we can perform the processing and then write into the file
360
- [self processData:data];
361
-
362
- if (expectedBytes == 0) {
363
- return;
364
- }
365
-
366
- // For non-chunked download, progress is received / expected
367
- // For chunked download, progress can be either 0 (started) or 1 (ended)
368
- NSNumber *now;
369
- if (expectedBytes != NSURLResponseUnknownLength) {
370
- now = [NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
371
- } else {
372
- now = @0;
373
- }
374
-
375
- if ([self.progressConfig shouldReport:now]) {
376
- NSDictionary *body;
377
- if (expectedBytes == NSURLResponseUnknownLength) {
378
- // For chunked downloads
379
- body = @{
380
- @"taskId": taskId,
381
- @"written": [NSString stringWithFormat:@"%d", 0],
382
- @"total": [NSString stringWithFormat:@"%lld", (long long) expectedBytes],
383
- @"chunk": chunkString,
384
- };
385
- } else {
386
- // For non-chunked downloads
387
- body = @{
388
- @"taskId": taskId,
389
- @"written": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
390
- @"total": [NSString stringWithFormat:@"%lld", (long long) expectedBytes],
391
- @"chunk": chunkString,
392
- };
393
- }
394
- [self.baseModule emitEventDict:EVENT_PROGRESS body:body];
395
- }
396
- }
397
-
398
- - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
399
- {
400
- if ([session isEqual:session]) {
401
- session = nil;
402
- }
403
- }
404
-
405
-
406
- - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
407
- {
408
-
409
- self.error = error;
410
- NSString * errMsg;
411
- NSString * respStr;
412
- NSString * rnfbRespType;
413
-
414
- // only run this if we were requested to change it
415
- if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
416
- dispatch_async(dispatch_get_main_queue(), ^{
417
- [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
418
- });
419
- }
420
-
421
- if (error) {
422
- if (error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled) {
423
- errMsg = @"task cancelled";
424
- } else {
425
- errMsg = [error localizedDescription];
426
- }
427
- } else if ([self.progressConfig shouldReport:@1] && expectedBytes == NSURLResponseUnknownLength) {
428
- // For chunked downloads
429
- [self.baseModule emitEventDict:EVENT_PROGRESS body:@{
430
- @"taskId": taskId,
431
- @"written": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
432
- @"total": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
433
- @"chunk": @"",
434
- }];
435
- }
436
-
437
- if (respFile) {
438
- if ([self ShouldTransformFile]) {
439
- // At this point, we have the data that we deferred to write into a file. We can now
440
- // perform the conversion and write the converted data into the file.
441
- NSObject<FileTransformer>* fileTransformer = [ReactNativeBlobUtilFileTransformer getFileTransformer];
442
- if (!fileTransformer) {
443
- errMsg = @"Transform file specified but file transfomer not set";
444
- } else {
445
- @try{
446
- NSData* transformedData = [fileTransformer onWriteFile:respData];
447
- [writeStream write:(const uint8_t *)[transformedData bytes] maxLength:[transformedData length]];
448
- } @catch(NSException * ex)
449
- {
450
- errMsg = [NSString stringWithFormat:@"Exception on File Transformer: '%@' ", [ex description]];
451
- }
452
- }
453
- }
454
- [writeStream close];
455
- rnfbRespType = RESP_TYPE_PATH;
456
- respStr = destPath;
457
- } else { // base64 response
458
- // #73 fix unicode data encoding issue :
459
- // when response type is BASE64, we should first try to encode the response data to UTF8 format
460
- // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
461
- // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
462
- NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
463
-
464
- if (responseFormat == BASE64) {
465
- rnfbRespType = RESP_TYPE_BASE64;
466
- respStr = [respData base64EncodedStringWithOptions:0];
467
- } else if (responseFormat == UTF8) {
468
- rnfbRespType = RESP_TYPE_UTF8;
469
- respStr = utf8;
470
- } else {
471
- if (utf8) {
472
- rnfbRespType = RESP_TYPE_UTF8;
473
- respStr = utf8;
474
- } else {
475
- rnfbRespType = RESP_TYPE_BASE64;
476
- respStr = [respData base64EncodedStringWithOptions:0];
477
- }
478
- }
479
- }
480
-
481
- NSHTTPURLResponse *response = (NSHTTPURLResponse *) [task response];
482
-
483
- callback(@[
484
- errMsg ?: [NSNull null],
485
- rnfbRespType ?: @"",
486
- respStr ?: [NSNull null],
487
- @{
488
- @"status": [NSNumber numberWithInteger:[response statusCode]]
489
- }
490
- ]);
491
-
492
- respData = nil;
493
- receivedBytes = 0;
494
- [session finishTasksAndInvalidate];
495
-
496
- }
497
-
498
- - (BOOL) ShouldTransformFile{
499
- return [[options valueForKey:CONFIG_TRANSFORM_FILE] boolValue];
500
- }
501
-
502
- // upload progress handler
503
- - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
504
- {
505
- if (totalBytesExpectedToWrite == 0) {
506
- return;
507
- }
508
-
509
- NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
510
-
511
- if ([self.uploadProgressConfig shouldReport:now]) {
512
- [self.baseModule emitEventDict:EVENT_PROGRESS_UPLOAD
513
- body:@{
514
- @"taskId": taskId,
515
- @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten],
516
- @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite]
517
- }
518
- ];
519
- }
520
- }
521
-
522
-
523
- - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
524
- {
525
- if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
526
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
527
- } else {
528
- completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
529
- }
530
- }
531
-
532
-
533
- - (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
534
- {
535
- NSLog(@"sess done in background");
536
- }
537
-
538
- - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
539
- {
540
-
541
- if (followRedirect) {
542
- if (request.URL) {
543
- [redirects addObject:[request.URL absoluteString]];
544
- }
545
-
546
- completionHandler(request);
547
- } else {
548
- completionHandler(nil);
549
- }
550
- }
551
-
552
- // NSURLSessionDownloadTask delegates
553
-
554
- #pragma mark NSURLSessionDownloadTask delegate methods
555
-
556
- - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
557
-
558
- NSFileManager *fm = [NSFileManager defaultManager];
559
- NSData *data = [fm contentsAtPath:location.path];
560
-
561
- [self configureWriteStream];
562
-
563
- [self processData:data];
564
-
565
- }
566
-
567
- - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
568
- if (totalBytesExpectedToWrite == 0) {
569
- return;
570
- }
571
-
572
- NSNumber * now =[NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
573
- if ([self.progressConfig shouldReport:now]) {
574
- [self.baseModule emitEventDict:EVENT_PROGRESS
575
- body:@{
576
- @"taskId": taskId,
577
- @"written": [NSString stringWithFormat:@"%lld", (long long) totalBytesWritten],
578
- @"total": [NSString stringWithFormat:@"%lld", (long long) totalBytesExpectedToWrite]
579
- }
580
- ];
581
- }
582
- }
583
-
584
- @end
1
+ //
2
+ // ReactNativeBlobUtilRequest.m
3
+ // ReactNativeBlobUtil
4
+ //
5
+ // Created by Artur Chrusciel on 15.01.18.
6
+ // Copyright © 2018 wkh237.github.io. All rights reserved.
7
+ //
8
+
9
+ #import "ReactNativeBlobUtilRequest.h"
10
+
11
+ #import "ReactNativeBlobUtilFS.h"
12
+ #import "ReactNativeBlobUtilConst.h"
13
+ #import "ReactNativeBlobUtilFileTransformer.h"
14
+ #import "ReactNativeBlobUtilReqBuilder.h"
15
+
16
+ #import <CommonCrypto/CommonDigest.h>
17
+
18
+
19
+ typedef NS_ENUM(NSUInteger, ResponseFormat) {
20
+ UTF8,
21
+ BASE64,
22
+ AUTO
23
+ };
24
+
25
+ @interface ReactNativeBlobUtilRequest ()
26
+ {
27
+ BOOL respFile;
28
+ BOOL isNewPart;
29
+ BOOL isIncrement;
30
+ NSMutableData * partBuffer;
31
+ NSString * destPath;
32
+ NSOutputStream * writeStream;
33
+ long bodyLength;
34
+ NSInteger respStatus;
35
+ NSMutableArray * redirects;
36
+ ResponseFormat responseFormat;
37
+ BOOL followRedirect;
38
+ BOOL backgroundTask;
39
+ }
40
+
41
+ @end
42
+
43
+ @implementation ReactNativeBlobUtilRequest
44
+
45
+ @synthesize taskId;
46
+ @synthesize expectedBytes;
47
+ @synthesize receivedBytes;
48
+ @synthesize respData;
49
+ @synthesize callback;
50
+ @synthesize baseModule;
51
+ @synthesize options;
52
+ @synthesize error;
53
+
54
+
55
+ - (NSString *)md5:(NSString *)input {
56
+ const char* str = [input UTF8String];
57
+ unsigned char result[CC_MD5_DIGEST_LENGTH];
58
+ CC_MD5(str, (CC_LONG)strlen(str), result);
59
+
60
+ NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
61
+ for (int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
62
+ [ret appendFormat:@"%02x",result[i]];
63
+ }
64
+ return ret;
65
+ }
66
+
67
+ // send HTTP request
68
+ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
69
+ contentLength:(long) contentLength
70
+ baseModule:(__strong ReactNativeBlobUtil * _Nullable)baseModule
71
+ taskId:(NSString * _Nullable)taskId
72
+ withRequest:(__weak NSURLRequest * _Nullable)req
73
+ taskOperationQueue:(NSOperationQueue * _Nonnull)operationQueue
74
+ callback:(_Nullable RCTResponseSenderBlock) callback
75
+ {
76
+ self.taskId = taskId;
77
+ self.respData = [[NSMutableData alloc] initWithLength:0];
78
+ self.callback = callback;
79
+ self.baseModule = baseModule;
80
+ self.expectedBytes = 0;
81
+ self.receivedBytes = 0;
82
+ self.options = options;
83
+
84
+ backgroundTask = [[options valueForKey:@"IOSBackgroundTask"] boolValue];
85
+ // when followRedirect not set in options, defaults to TRUE
86
+ followRedirect = [options valueForKey:@"followRedirect"] == nil ? YES : [[options valueForKey:@"followRedirect"] boolValue];
87
+ isIncrement = [[options valueForKey:@"increment"] boolValue];
88
+ redirects = [[NSMutableArray alloc] init];
89
+
90
+ if (req.URL) {
91
+ [redirects addObject:req.URL.absoluteString];
92
+ }
93
+
94
+ // set response format
95
+ NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
96
+
97
+ if ([[rnfbResp lowercaseString] isEqualToString:@"base64"]) {
98
+ responseFormat = BASE64;
99
+ } else if ([[rnfbResp lowercaseString] isEqualToString:@"utf8"]) {
100
+ responseFormat = UTF8;
101
+ } else {
102
+ responseFormat = AUTO;
103
+ }
104
+
105
+ NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
106
+ NSString * key = [self.options valueForKey:CONFIG_KEY];
107
+ NSURLSession * session;
108
+
109
+ bodyLength = contentLength;
110
+
111
+ // the session trust any SSL certification
112
+ NSURLSessionConfiguration *defaultConfigObject;
113
+
114
+ defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
115
+
116
+ if (backgroundTask) {
117
+ defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
118
+ }
119
+
120
+ // request timeout, -1 if not set in options
121
+ float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
122
+
123
+ if (timeout > 0) {
124
+ defaultConfigObject.timeoutIntervalForRequest = timeout/1000;
125
+ }
126
+
127
+ if([options valueForKey:CONFIG_WIFI_ONLY] != nil && [options[CONFIG_WIFI_ONLY] boolValue]){
128
+ [defaultConfigObject setAllowsCellularAccess:NO];
129
+ }
130
+
131
+ defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
132
+ session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue];
133
+
134
+ if (path || [self.options valueForKey:CONFIG_USE_TEMP]) {
135
+ respFile = YES;
136
+
137
+ NSString* cacheKey = taskId;
138
+ if (key) {
139
+ cacheKey = [self md5:key];
140
+
141
+ if (!cacheKey) {
142
+ cacheKey = taskId;
143
+ }
144
+
145
+ destPath = [ReactNativeBlobUtilFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
146
+
147
+ if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
148
+ callback(@[[NSNull null], RESP_TYPE_PATH, destPath]);
149
+
150
+ return;
151
+ }
152
+ }
153
+
154
+ if (path) {
155
+ destPath = path;
156
+ } else {
157
+ destPath = [ReactNativeBlobUtilFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
158
+ }
159
+
160
+ // We still need to initialize this as a placeholder in memory before we perform
161
+ // the conversion
162
+ if ([self ShouldTransformFile]) {
163
+ respData = [[NSMutableData alloc] init];
164
+ }
165
+ } else {
166
+ respData = [[NSMutableData alloc] init];
167
+ respFile = NO;
168
+ }
169
+
170
+ if(backgroundTask) {
171
+ NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:req];
172
+ [task resume];
173
+ self.task = task;
174
+ } else {
175
+ NSURLSessionDataTask *task = [session dataTaskWithRequest:req];
176
+ [task resume];
177
+ self.task = task;
178
+ }
179
+
180
+ // network status indicator
181
+ if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
182
+ dispatch_async(dispatch_get_main_queue(), ^{
183
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
184
+ });
185
+ }
186
+ }
187
+
188
+ ////////////////////////////////////////
189
+ //
190
+ // NSURLSession delegates
191
+ //
192
+ ////////////////////////////////////////
193
+
194
+
195
+ #pragma mark NSURLSession delegate methods
196
+
197
+ - (void)configureWriteStream {
198
+ if (respFile)
199
+ {
200
+ @try{
201
+ NSFileManager * fm = [NSFileManager defaultManager];
202
+ NSString * folder = [destPath stringByDeletingLastPathComponent];
203
+
204
+ if (![fm fileExistsAtPath:folder]) {
205
+ [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
206
+ }
207
+
208
+ // if not set overwrite in options, defaults to TRUE
209
+ BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
210
+ BOOL appendToExistingFile = [destPath containsString:@"?append=true"];
211
+
212
+ appendToExistingFile = !overwrite;
213
+
214
+ // For solving #141 append response data if the file already exists
215
+ // base on PR#139 @kejinliang
216
+ if (appendToExistingFile) {
217
+ destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
218
+ }
219
+
220
+ if (![fm fileExistsAtPath:destPath]) {
221
+ [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
222
+ }
223
+
224
+ writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
225
+ [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
226
+ [writeStream open];
227
+ }
228
+ @catch(NSException * ex)
229
+ {
230
+ NSLog(@"write file error");
231
+ }
232
+ }
233
+ }
234
+
235
+ - (void)processData:(NSData *)data {
236
+ if (respFile && ![self ShouldTransformFile]) {
237
+ [writeStream write:(const uint8_t *)[data bytes] maxLength:[data length]];
238
+ } else {
239
+ [respData appendData:data];
240
+ }
241
+ }
242
+
243
+
244
+ #pragma mark - Received Response
245
+ // set expected content length on response received
246
+ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
247
+ {
248
+ expectedBytes = [response expectedContentLength];
249
+
250
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
251
+ NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
252
+ NSString * respType = @"";
253
+ respStatus = statusCode;
254
+
255
+ if ([response respondsToSelector:@selector(allHeaderFields)])
256
+ {
257
+ NSDictionary *headers = [httpResponse allHeaderFields];
258
+ NSString * respCType = [[ReactNativeBlobUtilReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
259
+
260
+ if (self.isServerPush) {
261
+ if (partBuffer) {
262
+ [self.baseModule
263
+ emitEventDict:EVENT_SERVER_PUSH
264
+ body:@{
265
+ @"taskId": taskId,
266
+ @"chunk": [partBuffer base64EncodedStringWithOptions:0],
267
+ }
268
+ ];
269
+ }
270
+
271
+ partBuffer = [[NSMutableData alloc] init];
272
+ completionHandler(NSURLSessionResponseAllow);
273
+
274
+ return;
275
+ } else {
276
+ self.isServerPush = [[respCType lowercaseString] containsString:@"multipart/x-mixed-replace;"];
277
+ }
278
+
279
+ if(respCType)
280
+ {
281
+ NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
282
+
283
+ if ([respCType containsString:@"text/"]) {
284
+ respType = @"text";
285
+ } else if ([respCType containsString:@"application/json"]) {
286
+ respType = @"json";
287
+ } else if(extraBlobCTypes) { // If extra blob content type is not empty, check if response type matches
288
+ for (NSString * substr in extraBlobCTypes) {
289
+ if ([respCType containsString:[substr lowercaseString]]) {
290
+ respType = @"blob";
291
+ respFile = YES;
292
+ destPath = [ReactNativeBlobUtilFS getTempPath:taskId withExtension:nil];
293
+ break;
294
+ }
295
+ }
296
+ } else {
297
+ respType = @"blob";
298
+
299
+ // for XMLHttpRequest, switch response data handling strategy automatically
300
+ if ([options valueForKey:@"auto"]) {
301
+ respFile = YES;
302
+ destPath = [ReactNativeBlobUtilFS getTempPath:taskId withExtension:@""];
303
+ }
304
+ }
305
+ } else {
306
+ respType = @"text";
307
+ }
308
+
309
+ #pragma mark - handling cookies
310
+ // # 153 get cookies
311
+ if (response.URL) {
312
+ NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
313
+ NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
314
+ if (cookies.count) {
315
+ [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil];
316
+ }
317
+ }
318
+ [self.baseModule emitEventDict : EVENT_STATE_CHANGE
319
+ body:@{
320
+ @"taskId": taskId,
321
+ @"state": @"2",
322
+ @"headers": headers,
323
+ @"redirects": redirects,
324
+ @"respType" : respType,
325
+ @"timeout" : @NO,
326
+ @"status": [NSNumber numberWithInteger:statusCode]
327
+ }
328
+ ];
329
+ } else {
330
+ NSLog(@"oops");
331
+ }
332
+
333
+ [self configureWriteStream];
334
+
335
+ completionHandler(NSURLSessionResponseAllow);
336
+ }
337
+
338
+
339
+ // download progress handler
340
+ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
341
+ {
342
+ // For #143 handling multipart/x-mixed-replace response
343
+ if (self.isServerPush)
344
+ {
345
+ [partBuffer appendData:data];
346
+
347
+ return ;
348
+ }
349
+
350
+ NSNumber * received = [NSNumber numberWithLong:[data length]];
351
+ receivedBytes += [received longValue];
352
+ NSString * chunkString = @"";
353
+
354
+ if (isIncrement) {
355
+ chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
356
+ }
357
+
358
+ // If we need to process the data, we defer writing into the file until the we have all the data, at which point
359
+ // we can perform the processing and then write into the file
360
+ [self processData:data];
361
+
362
+ if (expectedBytes == 0) {
363
+ return;
364
+ }
365
+
366
+ // For non-chunked download, progress is received / expected
367
+ // For chunked download, progress can be either 0 (started) or 1 (ended)
368
+ NSNumber *now;
369
+ if (expectedBytes != NSURLResponseUnknownLength) {
370
+ now = [NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
371
+ } else {
372
+ now = @0;
373
+ }
374
+
375
+ if ([self.progressConfig shouldReport:now]) {
376
+ NSDictionary *body;
377
+ if (expectedBytes == NSURLResponseUnknownLength) {
378
+ // For chunked downloads
379
+ body = @{
380
+ @"taskId": taskId,
381
+ @"written": [NSString stringWithFormat:@"%d", 0],
382
+ @"total": [NSString stringWithFormat:@"%lld", (long long) expectedBytes],
383
+ @"chunk": chunkString,
384
+ };
385
+ } else {
386
+ // For non-chunked downloads
387
+ body = @{
388
+ @"taskId": taskId,
389
+ @"written": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
390
+ @"total": [NSString stringWithFormat:@"%lld", (long long) expectedBytes],
391
+ @"chunk": chunkString,
392
+ };
393
+ }
394
+ [self.baseModule emitEventDict:EVENT_PROGRESS body:body];
395
+ }
396
+ }
397
+
398
+ - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
399
+ {
400
+ if ([session isEqual:session]) {
401
+ session = nil;
402
+ }
403
+ }
404
+
405
+
406
+ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
407
+ {
408
+
409
+ self.error = error;
410
+ NSString * errMsg;
411
+ NSString * respStr;
412
+ NSString * rnfbRespType;
413
+
414
+ // only run this if we were requested to change it
415
+ if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
416
+ dispatch_async(dispatch_get_main_queue(), ^{
417
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
418
+ });
419
+ }
420
+
421
+ if (error) {
422
+ if (error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled) {
423
+ errMsg = @"task cancelled";
424
+ } else {
425
+ errMsg = [error localizedDescription];
426
+ }
427
+ } else if ([self.progressConfig shouldReport:@1] && expectedBytes == NSURLResponseUnknownLength) {
428
+ // For chunked downloads
429
+ [self.baseModule emitEventDict:EVENT_PROGRESS body:@{
430
+ @"taskId": taskId,
431
+ @"written": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
432
+ @"total": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
433
+ @"chunk": @"",
434
+ }];
435
+ }
436
+
437
+ if (respFile) {
438
+ if ([self ShouldTransformFile]) {
439
+ // At this point, we have the data that we deferred to write into a file. We can now
440
+ // perform the conversion and write the converted data into the file.
441
+ NSObject<FileTransformer>* fileTransformer = [ReactNativeBlobUtilFileTransformer getFileTransformer];
442
+ if (!fileTransformer) {
443
+ errMsg = @"Transform file specified but file transfomer not set";
444
+ } else {
445
+ @try{
446
+ NSData* transformedData = [fileTransformer onWriteFile:respData];
447
+ [writeStream write:(const uint8_t *)[transformedData bytes] maxLength:[transformedData length]];
448
+ } @catch(NSException * ex)
449
+ {
450
+ errMsg = [NSString stringWithFormat:@"Exception on File Transformer: '%@' ", [ex description]];
451
+ }
452
+ }
453
+ }
454
+ [writeStream close];
455
+ rnfbRespType = RESP_TYPE_PATH;
456
+ respStr = destPath;
457
+ } else { // base64 response
458
+ // #73 fix unicode data encoding issue :
459
+ // when response type is BASE64, we should first try to encode the response data to UTF8 format
460
+ // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
461
+ // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
462
+ NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
463
+
464
+ if (responseFormat == BASE64) {
465
+ rnfbRespType = RESP_TYPE_BASE64;
466
+ respStr = [respData base64EncodedStringWithOptions:0];
467
+ } else if (responseFormat == UTF8) {
468
+ rnfbRespType = RESP_TYPE_UTF8;
469
+ respStr = utf8;
470
+ } else {
471
+ if (utf8) {
472
+ rnfbRespType = RESP_TYPE_UTF8;
473
+ respStr = utf8;
474
+ } else {
475
+ rnfbRespType = RESP_TYPE_BASE64;
476
+ respStr = [respData base64EncodedStringWithOptions:0];
477
+ }
478
+ }
479
+ }
480
+
481
+ NSHTTPURLResponse *response = (NSHTTPURLResponse *) [task response];
482
+
483
+ callback(@[
484
+ errMsg ?: [NSNull null],
485
+ rnfbRespType ?: @"",
486
+ respStr ?: [NSNull null],
487
+ @{
488
+ @"status": [NSNumber numberWithInteger:[response statusCode]]
489
+ }
490
+ ]);
491
+
492
+ respData = nil;
493
+ receivedBytes = 0;
494
+ [session finishTasksAndInvalidate];
495
+
496
+ }
497
+
498
+ - (BOOL) ShouldTransformFile{
499
+ return [[options valueForKey:CONFIG_TRANSFORM_FILE] boolValue];
500
+ }
501
+
502
+ // upload progress handler
503
+ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
504
+ {
505
+ if (totalBytesExpectedToWrite == 0) {
506
+ return;
507
+ }
508
+
509
+ NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
510
+
511
+ if ([self.uploadProgressConfig shouldReport:now]) {
512
+ [self.baseModule emitEventDict:EVENT_PROGRESS_UPLOAD
513
+ body:@{
514
+ @"taskId": taskId,
515
+ @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten],
516
+ @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite]
517
+ }
518
+ ];
519
+ }
520
+ }
521
+
522
+
523
+ - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
524
+ {
525
+ if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
526
+ completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
527
+ } else {
528
+ completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
529
+ }
530
+ }
531
+
532
+
533
+ - (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
534
+ {
535
+ NSLog(@"sess done in background");
536
+ }
537
+
538
+ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
539
+ {
540
+
541
+ if (followRedirect) {
542
+ if (request.URL) {
543
+ [redirects addObject:[request.URL absoluteString]];
544
+ }
545
+
546
+ completionHandler(request);
547
+ } else {
548
+ completionHandler(nil);
549
+ }
550
+ }
551
+
552
+ // NSURLSessionDownloadTask delegates
553
+
554
+ #pragma mark NSURLSessionDownloadTask delegate methods
555
+
556
+ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
557
+
558
+ NSFileManager *fm = [NSFileManager defaultManager];
559
+ NSData *data = [fm contentsAtPath:location.path];
560
+
561
+ [self configureWriteStream];
562
+
563
+ [self processData:data];
564
+
565
+ }
566
+
567
+ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
568
+ if (totalBytesExpectedToWrite == 0) {
569
+ return;
570
+ }
571
+
572
+ NSNumber * now =[NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
573
+ if ([self.progressConfig shouldReport:now]) {
574
+ [self.baseModule emitEventDict:EVENT_PROGRESS
575
+ body:@{
576
+ @"taskId": taskId,
577
+ @"written": [NSString stringWithFormat:@"%lld", (long long) totalBytesWritten],
578
+ @"total": [NSString stringWithFormat:@"%lld", (long long) totalBytesExpectedToWrite]
579
+ }
580
+ ];
581
+ }
582
+ }
583
+
584
+ @end