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,1064 +1,1064 @@
1
-
2
- // ReactNativeBlobUtilFS.m
3
- // ReactNativeBlobUtil
4
- //
5
- // Created by Ben Hsieh on 2016/6/6.
6
- // Copyright © 2016年 suzuri04x2. All rights reserved.
7
- //
8
-
9
- #import <Foundation/Foundation.h>
10
- #import "ReactNativeBlobUtil.h"
11
- #import "ReactNativeBlobUtilFS.h"
12
- #import "ReactNativeBlobUtilConst.h"
13
- #import "ReactNativeBlobUtilFileTransformer.h"
14
- #import <AssetsLibrary/AssetsLibrary.h>
15
-
16
- #import <CommonCrypto/CommonDigest.h>
17
-
18
- #if __has_include(<React/RCTAssert.h>)
19
- #import <React/RCTBridge.h>
20
- #else
21
- #import "RCTBridge.h"
22
- #endif
23
-
24
-
25
- NSMutableDictionary *fileStreams = nil;
26
-
27
- ////////////////////////////////////////
28
- //
29
- // File system access methods
30
- //
31
- ////////////////////////////////////////
32
- @interface ReactNativeBlobUtilFS() {
33
- UIDocumentInteractionController * docCtrl;
34
- }
35
- @end
36
- @implementation ReactNativeBlobUtilFS
37
-
38
-
39
- @synthesize outStream;
40
- @synthesize inStream;
41
- @synthesize encoding;
42
- @synthesize callback;
43
- @synthesize taskId;
44
- @synthesize path;
45
- @synthesize appendData;
46
- @synthesize bufferSize;
47
-
48
- - (id)init {
49
- self = [super init];
50
- return self;
51
- }
52
-
53
- - (id)initWithCallback:(RCTResponseSenderBlock)callback {
54
- self = [super init];
55
- self.callback = callback;
56
- return self;
57
- }
58
-
59
- // static member getter
60
- + (NSDictionary *) getFileStreams {
61
-
62
- if(fileStreams == nil)
63
- fileStreams = [[NSMutableDictionary alloc] init];
64
- return fileStreams;
65
- }
66
-
67
- +(void) setFileStream:(ReactNativeBlobUtilFS *) instance withId:(NSString *) uuid {
68
- if(fileStreams == nil)
69
- fileStreams = [[NSMutableDictionary alloc] init];
70
- [fileStreams setValue:instance forKey:uuid];
71
- }
72
-
73
- +(NSString *) getPathOfAsset:(NSString *)assetURI
74
- {
75
- // get file path of an app asset
76
- if([assetURI hasPrefix:ASSET_PREFIX])
77
- {
78
- assetURI = [assetURI stringByReplacingOccurrencesOfString:ASSET_PREFIX withString:@""];
79
- assetURI = [[NSBundle mainBundle] pathForResource: [assetURI stringByDeletingPathExtension]
80
- ofType: [assetURI pathExtension]];
81
- }
82
- return assetURI;
83
- }
84
-
85
- #pragma mark - system directories
86
-
87
- + (NSString *) getMainBundleDir {
88
- return [[NSBundle mainBundle] bundlePath];
89
- }
90
-
91
- + (NSString *) getCacheDir {
92
- return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
93
- }
94
-
95
- + (NSString *) getDocumentDir {
96
- return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
97
- }
98
-
99
- + (NSString *) getDownloadDir {
100
- return [NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES) firstObject];
101
- }
102
-
103
- + (NSString *) getLibraryDir {
104
- return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];
105
- }
106
-
107
- + (NSString *) getMusicDir {
108
- return [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) firstObject];
109
- }
110
-
111
- + (NSString *) getMovieDir {
112
- return [NSSearchPathForDirectoriesInDomains(NSMoviesDirectory, NSUserDomainMask, YES) firstObject];
113
- }
114
-
115
- + (NSString *) getPictureDir {
116
- return [NSSearchPathForDirectoriesInDomains(NSPicturesDirectory, NSUserDomainMask, YES) firstObject];
117
- }
118
-
119
- + (NSString *) getApplicationSupportDir {
120
- return [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject];
121
- }
122
-
123
- + (NSString *) getTempPath {
124
-
125
- return NSTemporaryDirectory();
126
- }
127
-
128
- + (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext {
129
-
130
- NSString * documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
131
- NSString * filename = [NSString stringWithFormat:@"/ReactNativeBlobUtil_tmp/ReactNativeBlobUtilTmp_%@", taskId];
132
- if(ext != nil)
133
- filename = [filename stringByAppendingString: [NSString stringWithFormat:@".%@", ext]];
134
- NSString * tempPath = [documentDir stringByAppendingString: filename];
135
- return tempPath;
136
- }
137
-
138
- + (NSString *) getPathForAppGroup:(NSString *)groupName {
139
- NSFileManager* fileManager = [NSFileManager defaultManager];
140
- NSURL* containerURL = [fileManager containerURLForSecurityApplicationGroupIdentifier:groupName];
141
- if(containerURL) {
142
- return [containerURL path];
143
- } else {
144
- return nil;
145
- }
146
- }
147
-
148
- #pragma margk - readStream
149
-
150
- + (void) readStream:(NSString *)uri
151
- encoding:(NSString * )encoding
152
- bufferSize:(int)bufferSize
153
- tick:(int)tick
154
- streamId:(NSString *)streamId
155
- baseModule:(ReactNativeBlobUtil*)baseModule
156
- {
157
- [[self class] getPathFromUri:uri completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
158
-
159
- __block int read = 0;
160
- __block int backoff = tick *1000;
161
- __block int chunkSize = bufferSize;
162
- // allocate buffer in heap instead of stack
163
- uint8_t * buffer;
164
- @try
165
- {
166
- buffer = (uint8_t *) malloc(bufferSize);
167
- if(path != nil)
168
- {
169
- if([[NSFileManager defaultManager] fileExistsAtPath:path] == NO)
170
- {
171
- NSString * message = [NSString stringWithFormat:@"File does not exist at path %@", path];
172
- NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_ERROR, @"code": @"ENOENT", @"detail": message };
173
- [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
174
- free(buffer);
175
- return ;
176
- }
177
- NSInputStream * stream = [[NSInputStream alloc] initWithFileAtPath:path];
178
- [stream open];
179
- while((read = [stream read:buffer maxLength:bufferSize]) > 0)
180
- {
181
- [[self class] emitDataChunks:[NSData dataWithBytes:buffer length:read] encoding:encoding streamId:streamId baseModule:baseModule];
182
- if(tick > 0)
183
- {
184
- usleep(backoff);
185
- }
186
- }
187
- [stream close];
188
- }
189
- else if (asset != nil)
190
- {
191
- int cursor = 0;
192
- NSError * err;
193
- while((read = [asset getBytes:buffer fromOffset:cursor length:bufferSize error:&err]) > 0)
194
- {
195
- cursor += read;
196
- [[self class] emitDataChunks:[NSData dataWithBytes:buffer length:read] encoding:encoding streamId:streamId baseModule:baseModule];
197
- if(tick > 0)
198
- {
199
- usleep(backoff);
200
- }
201
- }
202
- }
203
- else
204
- {
205
- NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_ERROR, @"code": @"EINVAL", @"detail": @"Unable to resolve URI" };
206
- [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
207
- }
208
- // release buffer
209
- if(buffer != nil)
210
- free(buffer);
211
-
212
- }
213
- @catch (NSException * ex)
214
- {
215
- NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_ERROR, @"code": @"EUNSPECIFIED", @"detail": [ex description] };
216
- [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
217
- }
218
- @finally
219
- {
220
- NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_END, @"detail": @"" };
221
- [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
222
- }
223
-
224
- }];
225
-
226
-
227
- }
228
-
229
- // send read stream chunks via native event emitter
230
- + (void) emitDataChunks:(NSData *)data encoding:(NSString *) encoding streamId:(NSString *)streamId baseModule:(ReactNativeBlobUtil *)baseModule
231
- {
232
- @try
233
- {
234
- NSString * encodedChunk = @"";
235
- if([[encoding lowercaseString] isEqualToString:@"utf8"])
236
- {
237
- NSDictionary * payload = @{
238
- @"streamId":streamId,
239
- @"event": FS_EVENT_DATA,
240
- @"detail" : [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]
241
- };
242
- [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
243
- }
244
- else if ([[encoding lowercaseString] isEqualToString:@"base64"])
245
- {
246
- NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_DATA, @"detail" : [data base64EncodedStringWithOptions:0] };
247
- [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
248
- }
249
- else if([[encoding lowercaseString] isEqualToString:@"ascii"])
250
- {
251
- // RCTBridge only emits string data, so we have to create JSON byte array string
252
- NSMutableArray * asciiArray = [NSMutableArray array];
253
- unsigned char *bytePtr;
254
- if (data.length > 0)
255
- {
256
- bytePtr = (unsigned char *)[data bytes];
257
- NSInteger byteLen = data.length/sizeof(uint8_t);
258
- for (int i = 0; i < byteLen; i++)
259
- {
260
- [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
261
- }
262
- }
263
-
264
- NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_DATA, @"detail" : asciiArray };
265
- [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
266
- }
267
-
268
- }
269
- @catch (NSException * ex)
270
- {
271
- NSString * message = [NSString stringWithFormat:@"Failed to convert data to '%@' encoded string, this might due to the source data is not able to convert using this encoding. source = %@", encoding, [ex description]];
272
- [baseModule
273
- emitEventDict:EVENT_FILESYSTEM
274
- body:@{
275
- @"streamId":streamId,
276
- @"event" : MSG_EVENT_ERROR,
277
- @"detail" : message
278
- }];
279
- [baseModule
280
- emitEventDict:MSG_EVENT
281
- body:@{
282
- @"streamId":streamId,
283
- @"event" : MSG_EVENT_WARN,
284
- @"detail" : message
285
- }];
286
- }
287
- }
288
-
289
- # pragma write file from file
290
-
291
- + (NSNumber *) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append callback:(void(^)(NSString * errMsg, NSNumber *size))callback
292
- {
293
- [[self class] getPathFromUri:src completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
294
- if(path != nil)
295
- {
296
- __block NSInputStream * is = [[NSInputStream alloc] initWithFileAtPath:path];
297
- __block NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:append];
298
- [is open];
299
- [os open];
300
- uint8_t buffer[10240];
301
- __block long written = 0;
302
- int read = [is read:buffer maxLength:10240];
303
- written += read;
304
- while(read > 0) {
305
- [os write:buffer maxLength:read];
306
- read = [is read:buffer maxLength:10240];
307
- written += read;
308
- }
309
- [os close];
310
- [is close];
311
- __block NSNumber * size = [NSNumber numberWithLong:written];
312
- callback(nil, size);
313
- }
314
- else if(asset != nil)
315
- {
316
-
317
- __block NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:append];
318
- int read = 0;
319
- int cursor = 0;
320
- __block long written = 0;
321
- uint8_t buffer[10240];
322
- [os open];
323
- while((read = [asset getBytes:buffer fromOffset:cursor length:10240 error:nil]) > 0)
324
- {
325
- cursor += read;
326
- [os write:buffer maxLength:read];
327
- }
328
- __block NSNumber * size = [NSNumber numberWithLong:written];
329
- [os close];
330
- callback(nil, size);
331
- }
332
- else
333
- callback(@"failed to resolve path", nil);
334
- }];
335
-
336
- return 0;
337
- }
338
-
339
- # pragma mark - write file
340
-
341
- + (void) writeFile:(NSString *)path
342
- encoding:(NSString *)encoding
343
- data:(NSString *)data
344
- transformFile:(BOOL)transformFile
345
- append:(BOOL)append
346
- resolver:(RCTPromiseResolveBlock)resolve
347
- rejecter:(RCTPromiseRejectBlock)reject
348
- {
349
- @try {
350
- NSFileManager * fm = [NSFileManager defaultManager];
351
- NSError * err = nil;
352
- // check if the folder exists, if it does not exist create folders recursively
353
- // after the folders created, write data into the file
354
- NSString * folder = [path stringByDeletingLastPathComponent];
355
- encoding = [encoding lowercaseString];
356
-
357
- BOOL isDir = NO;
358
- BOOL exists = NO;
359
- exists = [fm fileExistsAtPath:path isDirectory: &isDir];
360
-
361
- if (isDir) {
362
- return reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
363
- }
364
-
365
- if(!exists) {
366
- [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:&err];
367
- if(err != nil) {
368
- return reject(@"ENOTDIR", [NSString stringWithFormat:@"Failed to create parent directory of '%@'; error: %@", path, [err description]], nil);
369
- }
370
- if(![fm createFileAtPath:path contents:nil attributes:nil]) {
371
- return reject(@"ENOENT", [NSString stringWithFormat:@"File '%@' does not exist and could not be created", path], nil);
372
- }
373
- }
374
-
375
- NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
376
- NSData * content = nil;
377
- if([encoding containsString:@"base64"]) {
378
- content = [[NSData alloc] initWithBase64EncodedString:data options:0];
379
- }
380
- else if([encoding isEqualToString:@"uri"]) {
381
- NSNumber* size = [[self class] writeFileFromFile:data toFile:path append:append callback:^(NSString *errMsg, NSNumber *size) {
382
- if(errMsg != nil)
383
- reject(@"EUNSPECIFIED", errMsg, nil);
384
- else
385
- resolve(size);
386
- }];
387
- return;
388
- }
389
- else {
390
- content = [data dataUsingEncoding:NSUTF8StringEncoding];
391
- }
392
-
393
- if (transformFile) {
394
- NSObject<FileTransformer>* fileTransformer = [ReactNativeBlobUtilFileTransformer getFileTransformer];
395
- if (fileTransformer) {
396
- content = [fileTransformer onWriteFile:content];
397
- } else {
398
- return reject(@"EUNSPECIFIED",@"Transform specified but transformer not set", nil);
399
- }
400
- }
401
-
402
- if(append == YES) {
403
- [fileHandle seekToEndOfFile];
404
- [fileHandle writeData:content];
405
- [fileHandle closeFile];
406
- }
407
- else {
408
- if (![content writeToFile:path options:NSDataWritingAtomic error:&err]) {
409
- fm = nil;
410
- return reject(@"EUNSPECIFIED", [NSString stringWithFormat:@"File '%@' could not be written; error: %@", path, [err description]], err);
411
- }
412
- }
413
- fm = nil;
414
-
415
- resolve([NSNumber numberWithInteger:[content length]]);
416
- }
417
- @catch (NSException * e)
418
- {
419
- reject(@"EUNSPECIFIED", [e description], nil);
420
- }
421
- }
422
-
423
- # pragma mark - write file (array)
424
-
425
- + (void) writeFileArray:(NSString *)path
426
- data:(NSArray *)data
427
- append:(BOOL)append
428
- resolver:(RCTPromiseResolveBlock)resolve
429
- rejecter:(RCTPromiseRejectBlock)reject
430
- {
431
- @try {
432
- NSFileManager * fm = [NSFileManager defaultManager];
433
- NSError * err = nil;
434
- // check if the folder exists, if not exists, create folders recursively
435
- // after the folders created, write data into the file
436
- NSString * folder = [path stringByDeletingLastPathComponent];
437
-
438
- BOOL isDir = NO;
439
- BOOL exists = NO;
440
- exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
441
-
442
- if (isDir) {
443
- return reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
444
- }
445
-
446
- if(!exists) {
447
- [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:&err];
448
- if(err != nil) {
449
- return reject(@"EUNSPECIFIED", [NSString stringWithFormat:@"Failed to create parent directory of '%@'; error: %@", path, [err description]], nil);
450
- }
451
- }
452
-
453
- NSMutableData * fileContent = [NSMutableData alloc];
454
- // prevent stack overflow, alloc on heap
455
- char * bytes = (char*) malloc([data count]);
456
- for(int i = 0; i < data.count; i++) {
457
- bytes[i] = [[data objectAtIndex:i] charValue];
458
- }
459
- [fileContent appendBytes:bytes length:data.count];
460
-
461
- if(!exists) {
462
- if(![fm createFileAtPath:path contents:fileContent attributes:NULL]) {
463
- return reject(@"ENOENT", [NSString stringWithFormat:@"File '%@' does not exist and could not be created", path], nil);
464
- }
465
- }
466
- // if file exists, write file
467
- else {
468
- if(append == YES) {
469
- NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
470
- [fileHandle seekToEndOfFile];
471
- [fileHandle writeData:fileContent];
472
- [fileHandle closeFile];
473
- }
474
- else {
475
- if (![fileContent writeToFile:path atomically:YES]) {
476
- free(bytes);
477
- fm = nil;
478
- return reject(@"EUNSPECIFIED", [NSString stringWithFormat:@"File '%@' could not be written.", path], nil);
479
- }
480
- }
481
- }
482
- free(bytes);
483
- fm = nil;
484
- resolve([NSNumber numberWithInteger: data.count]);
485
- }
486
- @catch (NSException * e)
487
- {
488
- reject(@"EUNSPECIFIED", [e description], nil);
489
- }
490
- }
491
-
492
- # pragma mark - read file
493
-
494
- + (void) readFile:(NSString *)path
495
- encoding:(NSString *)encoding
496
- transformFile:(BOOL) transformFile
497
- onComplete:(void (^)(NSData * content, NSString * codeStr, NSString * errMsg))onComplete
498
- {
499
- [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
500
- __block NSData * fileContent;
501
- NSError * err;
502
- __block Byte * buffer;
503
- if(asset != nil)
504
- {
505
- int size = asset.size;
506
- buffer = (Byte *)malloc(size);
507
- [asset getBytes:buffer fromOffset:0 length:asset.size error:&err];
508
- if(err != nil)
509
- {
510
- onComplete(nil, @"EUNSPECIFIED", [err description]);
511
- free(buffer);
512
- return;
513
- }
514
- fileContent = [NSData dataWithBytes:buffer length:size];
515
- free(buffer);
516
- }
517
- else
518
- {
519
- BOOL isDir = NO;
520
- if(![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir]) {
521
- if (isDir) {
522
- onComplete(nil, @"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path]);
523
- } else {
524
- onComplete(nil, @"ENOENT", [NSString stringWithFormat:@"No such file '%@'", path]);
525
- }
526
- return;
527
- }
528
- fileContent = [NSData dataWithContentsOfFile:path];
529
-
530
- }
531
-
532
- if (transformFile) {
533
- NSObject<FileTransformer>* fileTransformer = [ReactNativeBlobUtilFileTransformer getFileTransformer];
534
- if (fileTransformer) {
535
- @try{
536
- fileContent = [fileTransformer onReadFile:fileContent];
537
- } @catch (NSException * ex)
538
- {
539
- onComplete(nil, @"EUNSPECIFIED", [NSString stringWithFormat:@"Exception on File Transformer: '%@' ", [ex description]]);
540
- return;
541
- }
542
- } else {
543
- onComplete(nil, @"EUNSPECIFIED", @"Transform specified but transformer not set");
544
- return;
545
- }
546
- }
547
-
548
- if(encoding != nil)
549
- {
550
- if([[encoding lowercaseString] isEqualToString:@"utf8"])
551
- {
552
- NSString * utf8 = [[NSString alloc] initWithData:fileContent encoding:NSUTF8StringEncoding];
553
- if(utf8 == nil) {
554
- NSString * latin1 = [[NSString alloc] initWithData:fileContent encoding:NSISOLatin1StringEncoding];
555
- NSData * latin1Data = [latin1 dataUsingEncoding:NSISOLatin1StringEncoding];
556
- onComplete(latin1Data, nil, nil);
557
- } else {
558
- onComplete(fileContent, nil, nil);
559
- }
560
- }
561
- else if ([[encoding lowercaseString] isEqualToString:@"base64"]) {
562
- NSString * base64String = [fileContent base64EncodedStringWithOptions:0];
563
- onComplete([[NSData alloc] initWithBase64EncodedString:base64String options:0], nil, nil);
564
- }
565
- else if ([[encoding lowercaseString] isEqualToString:@"ascii"]) {
566
- NSMutableArray * resultArray = [NSMutableArray array];
567
- char * bytes = (char *)[fileContent bytes];
568
- for(int i=0;i<[fileContent length];i++) {
569
- [resultArray addObject:[NSNumber numberWithChar:bytes[i]]];
570
- }
571
- onComplete((NSData *)resultArray, nil, nil);
572
- }
573
- }
574
- else
575
- {
576
- onComplete(fileContent, nil, nil);
577
- }
578
-
579
- }];
580
- }
581
-
582
- # pragma mark - hash
583
-
584
- typedef enum {
585
- HashAlgorithmMD5,
586
- HashAlgorithmSHA1,
587
- HashAlgorithmSHA224,
588
- HashAlgorithmSHA256,
589
- HashAlgorithmSHA384,
590
- HashAlgorithmSHA512,
591
- } HashAlgorithm;
592
-
593
- + (void) hash:(NSString *)path
594
- algorithm:(NSString *)algorithm
595
- resolver:(RCTPromiseResolveBlock)resolve
596
- rejecter:(RCTPromiseRejectBlock)reject
597
- {
598
- BOOL isDir = NO;
599
- BOOL exists = NO;
600
- exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
601
-
602
- if (isDir) {
603
- return reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
604
- }
605
- if (!exists) {
606
- return reject(@"ENOENT", [NSString stringWithFormat:@"No such file '%@'", path], nil);
607
- }
608
-
609
- NSError *error = nil;
610
-
611
- NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
612
-
613
- if (error) {
614
- reject(@"EUNKNOWN", [error description], nil);
615
- return;
616
- }
617
-
618
- if ([attributes objectForKey:NSFileType] == NSFileTypeDirectory) {
619
- reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
620
- return;
621
- }
622
-
623
- NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
624
- if (fileHandle == nil) {
625
- reject(@"EUNKNOWN", [NSString stringWithFormat:@"Error opening '%@' for reading", path], error);
626
- return;
627
- }
628
-
629
- NSArray *keys = [NSArray arrayWithObjects:@"md5", @"sha1", @"sha224", @"sha256", @"sha384", @"sha512", nil];
630
-
631
- NSArray *digestLengths = [NSArray arrayWithObjects:
632
- @CC_MD5_DIGEST_LENGTH,
633
- @CC_SHA1_DIGEST_LENGTH,
634
- @CC_SHA224_DIGEST_LENGTH,
635
- @CC_SHA256_DIGEST_LENGTH,
636
- @CC_SHA384_DIGEST_LENGTH,
637
- @CC_SHA512_DIGEST_LENGTH,
638
- nil];
639
-
640
- NSDictionary *keysToDigestLengths = [NSDictionary dictionaryWithObjects:digestLengths forKeys:keys];
641
-
642
- int digestLength = [[keysToDigestLengths objectForKey:algorithm] intValue];
643
-
644
- if (!digestLength) {
645
- return reject(@"EINVAL", [NSString stringWithFormat:@"Invalid algorithm '%@', must be one of md5, sha1, sha224, sha256, sha384, sha512", algorithm], nil);
646
- }
647
-
648
- unsigned char buffer[digestLength];
649
-
650
- const NSUInteger chunkSize = 1024 * 1024; // 1 Megabyte
651
- NSData *dataChunk;
652
-
653
- CC_MD5_CTX md5Context;
654
- CC_SHA1_CTX sha1Context;
655
- CC_SHA256_CTX sha256Context;
656
- CC_SHA512_CTX sha512Context;
657
- HashAlgorithm hashAlgorithm;
658
-
659
- if ([algorithm isEqualToString:@"md5"]) {
660
- CC_MD5_Init(&md5Context);
661
- hashAlgorithm = HashAlgorithmMD5;
662
- } else if ([algorithm isEqualToString:@"sha1"]) {
663
- CC_SHA1_Init(&sha1Context);
664
- hashAlgorithm = HashAlgorithmSHA1;
665
- } else if ([algorithm isEqualToString:@"sha224"]) {
666
- CC_SHA224_Init(&sha256Context);
667
- hashAlgorithm = HashAlgorithmSHA224;
668
- } else if ([algorithm isEqualToString:@"sha256"]) {
669
- CC_SHA256_Init(&sha256Context);
670
- hashAlgorithm = HashAlgorithmSHA256;
671
- } else if ([algorithm isEqualToString:@"sha384"]) {
672
- CC_SHA384_Init(&sha512Context);
673
- hashAlgorithm = HashAlgorithmSHA384;
674
- } else if ([algorithm isEqualToString:@"sha512"]) {
675
- CC_SHA512_Init(&sha512Context);
676
- hashAlgorithm = HashAlgorithmSHA512;
677
- } else {
678
- reject(@"EINVAL", [NSString stringWithFormat:@"Invalid algorithm '%@', must be one of md5, sha1, sha224, sha256, sha384, sha512", algorithm], nil);
679
- return;
680
- }
681
-
682
- while (true) {
683
- @autoreleasepool {
684
- dataChunk = [fileHandle readDataUpToLength:chunkSize error:&error];
685
-
686
- if (error) {
687
- return reject(@"EREAD", [NSString stringWithFormat:@"Error reading file '%@'", path], error);
688
- break;
689
- }
690
-
691
- if (dataChunk == nil || dataChunk.length == 0) {
692
- break;
693
- }
694
-
695
- switch(hashAlgorithm) {
696
- case HashAlgorithmMD5:
697
- CC_MD5_Update(&md5Context, [dataChunk bytes], CC_LONG([dataChunk length]));
698
- break;
699
- case HashAlgorithmSHA1:
700
- CC_SHA1_Update(&sha1Context, [dataChunk bytes], CC_LONG([dataChunk length]));
701
- break;
702
- case HashAlgorithmSHA224:
703
- CC_SHA224_Update(&sha256Context, [dataChunk bytes], CC_LONG([dataChunk length]));
704
- break;
705
- case HashAlgorithmSHA256:
706
- CC_SHA256_Update(&sha256Context, [dataChunk bytes], CC_LONG([dataChunk length]));
707
- break;
708
- case HashAlgorithmSHA384:
709
- CC_SHA384_Update(&sha512Context, [dataChunk bytes], CC_LONG([dataChunk length]));
710
- break;
711
- case HashAlgorithmSHA512:
712
- CC_SHA512_Update(&sha512Context, [dataChunk bytes], CC_LONG([dataChunk length]));
713
- break;
714
- }
715
-
716
- dataChunk = nil;
717
- }
718
- }
719
-
720
- switch(hashAlgorithm) {
721
- case HashAlgorithmMD5:
722
- CC_MD5_Final(buffer, &md5Context);
723
- break;
724
- case HashAlgorithmSHA1:
725
- CC_SHA1_Final(buffer, &sha1Context);
726
- break;
727
- case HashAlgorithmSHA224:
728
- CC_SHA224_Final(buffer, &sha256Context);
729
- break;
730
- case HashAlgorithmSHA256:
731
- CC_SHA256_Final(buffer, &sha256Context);
732
- break;
733
- case HashAlgorithmSHA384:
734
- CC_SHA384_Final(buffer, &sha512Context);
735
- break;
736
- case HashAlgorithmSHA512:
737
- CC_SHA512_Final(buffer, &sha512Context);
738
- break;
739
- }
740
-
741
- NSMutableString *output = [NSMutableString stringWithCapacity:digestLength * 2];
742
- for(int i = 0; i < digestLength; i++)
743
- [output appendFormat:@"%02x",buffer[i]];
744
-
745
- resolve(output);
746
- }
747
-
748
- # pragma mark - mkdir
749
-
750
- + (void) mkdir:(NSString *) path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
751
- {
752
- BOOL isDir = NO;
753
- NSError * err = nil;
754
- if([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
755
- reject(@"EEXIST", [NSString stringWithFormat:@"%@ '%@' already exists", isDir ? @"Directory" : @"File", path], nil);
756
- return;
757
- }
758
- else {
759
- [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&err];
760
- }
761
- if(err == nil) {
762
- resolve(@YES);
763
- }
764
- else {
765
- reject(@"EUNSPECIFIED", [NSString stringWithFormat:@"Error creating folder '%@', error: %@", path, [err description]], nil);
766
- }
767
- }
768
-
769
- # pragma mark - stat
770
-
771
- + (NSDictionary *) stat:(NSString *) path error:(NSError **) error {
772
-
773
-
774
- BOOL isDir = NO;
775
- NSFileManager * fm = [NSFileManager defaultManager];
776
- if([fm fileExistsAtPath:path isDirectory:&isDir] == NO) {
777
- return nil;
778
- }
779
- NSDictionary * info = [fm attributesOfItemAtPath:path error:error];
780
- NSString * size = [NSString stringWithFormat:@"%llu", [info fileSize]];
781
- NSString * filename = [path lastPathComponent];
782
- NSDate * lastModified;
783
- [[NSURL fileURLWithPath:path] getResourceValue:&lastModified forKey:NSURLContentModificationDateKey error:error];
784
- return @{
785
- @"size" : size,
786
- @"filename" : filename,
787
- @"path" : path,
788
- @"lastModified" : [NSNumber numberWithLong:(time_t) [lastModified timeIntervalSince1970]*1000],
789
- @"type" : isDir ? @"directory" : @"file"
790
- };
791
-
792
- }
793
-
794
- # pragma mark - exists
795
-
796
- + (void) exists:(NSString *) path callback:(RCTResponseSenderBlock)callback
797
- {
798
- [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
799
- if(path != nil)
800
- {
801
- BOOL isDir = NO;
802
- BOOL exists = NO;
803
- exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
804
- callback(@[@(exists), @(isDir)]);
805
- }
806
- else if(asset != nil)
807
- {
808
- callback(@[@YES, @NO]);
809
- }
810
- else
811
- {
812
- callback(@[@NO, @NO]);
813
- }
814
- }];
815
- }
816
-
817
- # pragma mark - open file stream
818
-
819
- // Create file stream for write data
820
- - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append {
821
- self.outStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:append];
822
- self.encoding = encode;
823
- [self.outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
824
- [self.outStream open];
825
- NSString *uuid = [[NSUUID UUID] UUIDString];
826
- self.streamId = uuid;
827
- [ReactNativeBlobUtilFS setFileStream:self withId:uuid];
828
- return uuid;
829
- }
830
-
831
- # pragma mark - file stream write chunk
832
-
833
- // Write file chunk into an opened stream
834
- - (void)writeEncodeChunk:(NSString *) chunk {
835
- NSData * decodedData = nil;
836
- if([[self.encoding lowercaseString] isEqualToString:@"base64"]) {
837
- decodedData = [[NSData alloc] initWithBase64EncodedString:chunk options: NSDataBase64DecodingIgnoreUnknownCharacters];
838
- }
839
- else if([[self.encoding lowercaseString] isEqualToString:@"utf8"]) {
840
- decodedData = [chunk dataUsingEncoding:NSUTF8StringEncoding];
841
- }
842
- else if([[self.encoding lowercaseString] isEqualToString:@"ascii"]) {
843
- decodedData = [chunk dataUsingEncoding:NSASCIIStringEncoding];
844
- }
845
- NSUInteger left = [decodedData length];
846
- NSUInteger nwr = 0;
847
- do {
848
- nwr = [self.outStream write:(const uint8_t *)[decodedData bytes] maxLength:left];
849
- if (-1 == nwr) break;
850
- left -= nwr;
851
- } while (left > 0);
852
- if (left) {
853
- NSLog(@"stream error: %@", [self.outStream streamError]);
854
- }
855
- }
856
-
857
- // Write file chunk into an opened stream
858
- - (void)write:(NSData *) chunk {
859
- NSUInteger left = [chunk length];
860
- NSUInteger nwr = 0;
861
- do {
862
- nwr = [self.outStream write:(const uint8_t *)[chunk bytes] maxLength:left];
863
- if (-1 == nwr) break;
864
- left -= nwr;
865
- } while (left > 0);
866
- if (left) {
867
- NSLog(@"stream error: %@", [self.outStream streamError]);
868
- }
869
- }
870
-
871
- // close file write stream
872
- - (void)closeOutStream {
873
- if(self.outStream != nil) {
874
- [self.outStream close];
875
- self.outStream = nil;
876
- }
877
-
878
- }
879
-
880
- // Slice a file into another file, generally for support Blob implementation.
881
- + (void)slice:(NSString *)path
882
- dest:(NSString *)dest
883
- start:(nonnull NSNumber *)start
884
- end:(nonnull NSNumber *)end
885
- encode:(NSString *)encode
886
- resolver:(RCTPromiseResolveBlock)resolve
887
- rejecter:(RCTPromiseRejectBlock)reject
888
- {
889
- [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset)
890
- {
891
- if(path != nil)
892
- {
893
- long expected = [end longValue] - [start longValue];
894
- long read = 0;
895
- NSFileHandle * handle = [NSFileHandle fileHandleForReadingAtPath:path];
896
- NSFileManager * fm = [NSFileManager defaultManager];
897
- NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
898
- [os open];
899
-
900
- BOOL isDir = NO;
901
- BOOL exists = NO;
902
- exists = [fm fileExistsAtPath:path isDirectory: &isDir];
903
-
904
- if (isDir) {
905
- return reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
906
- }
907
- if(!exists) {
908
- return reject(@"ENOENT", [NSString stringWithFormat: @"No such file '%@'", path ], nil);
909
- }
910
-
911
- long size = [fm attributesOfItemAtPath:path error:nil].fileSize;
912
- long max = MIN(size, [end longValue]);
913
-
914
- if(![fm fileExistsAtPath:dest]) {
915
- if(![fm createFileAtPath:dest contents:[NSData new] attributes:nil]) {
916
- return reject(@"ENOENT", [NSString stringWithFormat:@"File '%@' does not exist and could not be created", path], nil);
917
- }
918
- }
919
- [handle seekToFileOffset:[start longValue]];
920
- while(read < expected) {
921
- NSData * chunk;
922
- long chunkSize = 0;
923
- if([start longValue] + read + 10240 > max)
924
- {
925
- NSLog(@"read chunk %lu", max - read - [start longValue]);
926
- chunkSize = max - read - [start longValue];
927
- chunk = [handle readDataOfLength:chunkSize];
928
- }
929
- else
930
- {
931
- NSLog(@"read chunk %d", 10240);
932
- chunkSize = 10240;
933
- chunk = [handle readDataOfLength:10240];
934
- }
935
- if([chunk length] <= 0)
936
- break;
937
- long remain = expected - read;
938
-
939
- [os write:(const uint8_t *)[chunk bytes] maxLength:chunkSize];
940
- read += [chunk length];
941
- }
942
- [handle closeFile];
943
- [os close];
944
- resolve(dest);
945
- }
946
- else if (asset != nil)
947
- {
948
- long expected = [end longValue] - [start longValue];
949
- long read = 0;
950
- long chunkRead = 0;
951
- NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
952
- [os open];
953
- long size = asset.size;
954
- long max = MIN(size, [end longValue]);
955
-
956
- while(read < expected) {
957
- uint8_t chunk[10240];
958
- uint8_t * pointerToChunk = &chunk[0];
959
- long chunkSize = 0;
960
- if([start longValue] + read + 10240 > max)
961
- {
962
- NSLog(@"read chunk %lu", max - read - [start longValue]);
963
- chunkSize = max - read - [start longValue];
964
- chunkRead = [asset getBytes:pointerToChunk fromOffset:[start longValue] + read length:chunkSize error:nil];
965
- }
966
- else
967
- {
968
- NSLog(@"read chunk %lu", 10240);
969
- chunkSize = 10240;
970
- chunkRead = [asset getBytes:pointerToChunk fromOffset:[start longValue] + read length:chunkSize error:nil];
971
- }
972
- if( chunkRead <= 0)
973
- break;
974
- long remain = expected - read;
975
-
976
- [os write:chunk maxLength:chunkSize];
977
- read += chunkRead;
978
- }
979
- [os close];
980
- resolve(dest);
981
- }
982
- else {
983
- reject(@"EINVAL", [NSString stringWithFormat: @"Could not resolve URI %@", path ], nil);
984
- }
985
-
986
- }];
987
- }
988
-
989
- // close file read stream
990
- - (void)closeInStream
991
- {
992
- if(self.inStream != nil) {
993
- [self.inStream close];
994
- [self.inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
995
- [[ReactNativeBlobUtilFS getFileStreams] setValue:nil forKey:self.streamId];
996
- self.streamId = nil;
997
- }
998
-
999
- }
1000
-
1001
-
1002
- # pragma mark - get absolute path of resource
1003
-
1004
- + (void) getPathFromUri:(NSString *)uri completionHandler:(void(^)(NSString * path, ALAssetRepresentation *asset)) onComplete
1005
- {
1006
- if([uri hasPrefix:AL_PREFIX])
1007
- {
1008
- NSURL *asseturl = [NSURL URLWithString:uri];
1009
- __block ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
1010
- [assetslibrary assetForURL:asseturl
1011
- resultBlock:^(ALAsset *asset) {
1012
- __block ALAssetRepresentation * present = [asset defaultRepresentation];
1013
- onComplete(nil, present);
1014
- }
1015
- failureBlock:^(NSError *error) {
1016
- onComplete(nil, nil);
1017
- }];
1018
- }
1019
- else
1020
- {
1021
- onComplete([[self class] getPathOfAsset:uri], nil);
1022
- }
1023
- }
1024
-
1025
- #pragma mark - get disk space
1026
-
1027
- +(void) df:(RCTResponseSenderBlock)callback
1028
- {
1029
- NSError *error = nil;
1030
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
1031
- NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];
1032
-
1033
- if (dictionary) {
1034
- NSNumber *fileSystemSizeInBytes = [dictionary objectForKey: NSFileSystemSize];
1035
- NSNumber *freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize];
1036
-
1037
- callback(@[[NSNull null], @{
1038
- @"free" : freeFileSystemSizeInBytes,
1039
- @"total" : fileSystemSizeInBytes,
1040
- }]);
1041
- } else {
1042
- callback(@[@"failed to get storage usage."]);
1043
- }
1044
-
1045
- }
1046
-
1047
- + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest
1048
- {
1049
- int read = 0;
1050
- int cursor = 0;
1051
- Byte * buffer = (Byte *)malloc(10240);
1052
- NSOutputStream * ostream = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
1053
- [ostream open];
1054
- while((read = [rep getBytes:buffer fromOffset:cursor length:10240 error:nil]) > 0)
1055
- {
1056
- cursor+=10240;
1057
- [ostream write:buffer maxLength:read];
1058
- }
1059
- [ostream close];
1060
- free(buffer);
1061
- return;
1062
- }
1063
-
1064
- @end
1
+
2
+ // ReactNativeBlobUtilFS.m
3
+ // ReactNativeBlobUtil
4
+ //
5
+ // Created by Ben Hsieh on 2016/6/6.
6
+ // Copyright © 2016年 suzuri04x2. All rights reserved.
7
+ //
8
+
9
+ #import <Foundation/Foundation.h>
10
+ #import "ReactNativeBlobUtil.h"
11
+ #import "ReactNativeBlobUtilFS.h"
12
+ #import "ReactNativeBlobUtilConst.h"
13
+ #import "ReactNativeBlobUtilFileTransformer.h"
14
+ #import <AssetsLibrary/AssetsLibrary.h>
15
+
16
+ #import <CommonCrypto/CommonDigest.h>
17
+
18
+ #if __has_include(<React/RCTAssert.h>)
19
+ #import <React/RCTBridge.h>
20
+ #else
21
+ #import "RCTBridge.h"
22
+ #endif
23
+
24
+
25
+ NSMutableDictionary *fileStreams = nil;
26
+
27
+ ////////////////////////////////////////
28
+ //
29
+ // File system access methods
30
+ //
31
+ ////////////////////////////////////////
32
+ @interface ReactNativeBlobUtilFS() {
33
+ UIDocumentInteractionController * docCtrl;
34
+ }
35
+ @end
36
+ @implementation ReactNativeBlobUtilFS
37
+
38
+
39
+ @synthesize outStream;
40
+ @synthesize inStream;
41
+ @synthesize encoding;
42
+ @synthesize callback;
43
+ @synthesize taskId;
44
+ @synthesize path;
45
+ @synthesize appendData;
46
+ @synthesize bufferSize;
47
+
48
+ - (id)init {
49
+ self = [super init];
50
+ return self;
51
+ }
52
+
53
+ - (id)initWithCallback:(RCTResponseSenderBlock)callback {
54
+ self = [super init];
55
+ self.callback = callback;
56
+ return self;
57
+ }
58
+
59
+ // static member getter
60
+ + (NSDictionary *) getFileStreams {
61
+
62
+ if(fileStreams == nil)
63
+ fileStreams = [[NSMutableDictionary alloc] init];
64
+ return fileStreams;
65
+ }
66
+
67
+ +(void) setFileStream:(ReactNativeBlobUtilFS *) instance withId:(NSString *) uuid {
68
+ if(fileStreams == nil)
69
+ fileStreams = [[NSMutableDictionary alloc] init];
70
+ [fileStreams setValue:instance forKey:uuid];
71
+ }
72
+
73
+ +(NSString *) getPathOfAsset:(NSString *)assetURI
74
+ {
75
+ // get file path of an app asset
76
+ if([assetURI hasPrefix:ASSET_PREFIX])
77
+ {
78
+ assetURI = [assetURI stringByReplacingOccurrencesOfString:ASSET_PREFIX withString:@""];
79
+ assetURI = [[NSBundle mainBundle] pathForResource: [assetURI stringByDeletingPathExtension]
80
+ ofType: [assetURI pathExtension]];
81
+ }
82
+ return assetURI;
83
+ }
84
+
85
+ #pragma mark - system directories
86
+
87
+ + (NSString *) getMainBundleDir {
88
+ return [[NSBundle mainBundle] bundlePath];
89
+ }
90
+
91
+ + (NSString *) getCacheDir {
92
+ return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
93
+ }
94
+
95
+ + (NSString *) getDocumentDir {
96
+ return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
97
+ }
98
+
99
+ + (NSString *) getDownloadDir {
100
+ return [NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES) firstObject];
101
+ }
102
+
103
+ + (NSString *) getLibraryDir {
104
+ return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];
105
+ }
106
+
107
+ + (NSString *) getMusicDir {
108
+ return [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) firstObject];
109
+ }
110
+
111
+ + (NSString *) getMovieDir {
112
+ return [NSSearchPathForDirectoriesInDomains(NSMoviesDirectory, NSUserDomainMask, YES) firstObject];
113
+ }
114
+
115
+ + (NSString *) getPictureDir {
116
+ return [NSSearchPathForDirectoriesInDomains(NSPicturesDirectory, NSUserDomainMask, YES) firstObject];
117
+ }
118
+
119
+ + (NSString *) getApplicationSupportDir {
120
+ return [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject];
121
+ }
122
+
123
+ + (NSString *) getTempPath {
124
+
125
+ return NSTemporaryDirectory();
126
+ }
127
+
128
+ + (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext {
129
+
130
+ NSString * documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
131
+ NSString * filename = [NSString stringWithFormat:@"/ReactNativeBlobUtil_tmp/ReactNativeBlobUtilTmp_%@", taskId];
132
+ if(ext != nil)
133
+ filename = [filename stringByAppendingString: [NSString stringWithFormat:@".%@", ext]];
134
+ NSString * tempPath = [documentDir stringByAppendingString: filename];
135
+ return tempPath;
136
+ }
137
+
138
+ + (NSString *) getPathForAppGroup:(NSString *)groupName {
139
+ NSFileManager* fileManager = [NSFileManager defaultManager];
140
+ NSURL* containerURL = [fileManager containerURLForSecurityApplicationGroupIdentifier:groupName];
141
+ if(containerURL) {
142
+ return [containerURL path];
143
+ } else {
144
+ return nil;
145
+ }
146
+ }
147
+
148
+ #pragma margk - readStream
149
+
150
+ + (void) readStream:(NSString *)uri
151
+ encoding:(NSString * )encoding
152
+ bufferSize:(int)bufferSize
153
+ tick:(int)tick
154
+ streamId:(NSString *)streamId
155
+ baseModule:(ReactNativeBlobUtil*)baseModule
156
+ {
157
+ [[self class] getPathFromUri:uri completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
158
+
159
+ __block int read = 0;
160
+ __block int backoff = tick *1000;
161
+ __block int chunkSize = bufferSize;
162
+ // allocate buffer in heap instead of stack
163
+ uint8_t * buffer;
164
+ @try
165
+ {
166
+ buffer = (uint8_t *) malloc(bufferSize);
167
+ if(path != nil)
168
+ {
169
+ if([[NSFileManager defaultManager] fileExistsAtPath:path] == NO)
170
+ {
171
+ NSString * message = [NSString stringWithFormat:@"File does not exist at path %@", path];
172
+ NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_ERROR, @"code": @"ENOENT", @"detail": message };
173
+ [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
174
+ free(buffer);
175
+ return ;
176
+ }
177
+ NSInputStream * stream = [[NSInputStream alloc] initWithFileAtPath:path];
178
+ [stream open];
179
+ while((read = [stream read:buffer maxLength:bufferSize]) > 0)
180
+ {
181
+ [[self class] emitDataChunks:[NSData dataWithBytes:buffer length:read] encoding:encoding streamId:streamId baseModule:baseModule];
182
+ if(tick > 0)
183
+ {
184
+ usleep(backoff);
185
+ }
186
+ }
187
+ [stream close];
188
+ }
189
+ else if (asset != nil)
190
+ {
191
+ int cursor = 0;
192
+ NSError * err;
193
+ while((read = [asset getBytes:buffer fromOffset:cursor length:bufferSize error:&err]) > 0)
194
+ {
195
+ cursor += read;
196
+ [[self class] emitDataChunks:[NSData dataWithBytes:buffer length:read] encoding:encoding streamId:streamId baseModule:baseModule];
197
+ if(tick > 0)
198
+ {
199
+ usleep(backoff);
200
+ }
201
+ }
202
+ }
203
+ else
204
+ {
205
+ NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_ERROR, @"code": @"EINVAL", @"detail": @"Unable to resolve URI" };
206
+ [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
207
+ }
208
+ // release buffer
209
+ if(buffer != nil)
210
+ free(buffer);
211
+
212
+ }
213
+ @catch (NSException * ex)
214
+ {
215
+ NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_ERROR, @"code": @"EUNSPECIFIED", @"detail": [ex description] };
216
+ [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
217
+ }
218
+ @finally
219
+ {
220
+ NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_END, @"detail": @"" };
221
+ [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
222
+ }
223
+
224
+ }];
225
+
226
+
227
+ }
228
+
229
+ // send read stream chunks via native event emitter
230
+ + (void) emitDataChunks:(NSData *)data encoding:(NSString *) encoding streamId:(NSString *)streamId baseModule:(ReactNativeBlobUtil *)baseModule
231
+ {
232
+ @try
233
+ {
234
+ NSString * encodedChunk = @"";
235
+ if([[encoding lowercaseString] isEqualToString:@"utf8"])
236
+ {
237
+ NSDictionary * payload = @{
238
+ @"streamId":streamId,
239
+ @"event": FS_EVENT_DATA,
240
+ @"detail" : [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]
241
+ };
242
+ [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
243
+ }
244
+ else if ([[encoding lowercaseString] isEqualToString:@"base64"])
245
+ {
246
+ NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_DATA, @"detail" : [data base64EncodedStringWithOptions:0] };
247
+ [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
248
+ }
249
+ else if([[encoding lowercaseString] isEqualToString:@"ascii"])
250
+ {
251
+ // RCTBridge only emits string data, so we have to create JSON byte array string
252
+ NSMutableArray * asciiArray = [NSMutableArray array];
253
+ unsigned char *bytePtr;
254
+ if (data.length > 0)
255
+ {
256
+ bytePtr = (unsigned char *)[data bytes];
257
+ NSInteger byteLen = data.length/sizeof(uint8_t);
258
+ for (int i = 0; i < byteLen; i++)
259
+ {
260
+ [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
261
+ }
262
+ }
263
+
264
+ NSDictionary * payload = @{ @"streamId":streamId, @"event": FS_EVENT_DATA, @"detail" : asciiArray };
265
+ [baseModule emitEventDict:EVENT_FILESYSTEM body:payload];
266
+ }
267
+
268
+ }
269
+ @catch (NSException * ex)
270
+ {
271
+ NSString * message = [NSString stringWithFormat:@"Failed to convert data to '%@' encoded string, this might due to the source data is not able to convert using this encoding. source = %@", encoding, [ex description]];
272
+ [baseModule
273
+ emitEventDict:EVENT_FILESYSTEM
274
+ body:@{
275
+ @"streamId":streamId,
276
+ @"event" : MSG_EVENT_ERROR,
277
+ @"detail" : message
278
+ }];
279
+ [baseModule
280
+ emitEventDict:MSG_EVENT
281
+ body:@{
282
+ @"streamId":streamId,
283
+ @"event" : MSG_EVENT_WARN,
284
+ @"detail" : message
285
+ }];
286
+ }
287
+ }
288
+
289
+ # pragma write file from file
290
+
291
+ + (NSNumber *) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append callback:(void(^)(NSString * errMsg, NSNumber *size))callback
292
+ {
293
+ [[self class] getPathFromUri:src completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
294
+ if(path != nil)
295
+ {
296
+ __block NSInputStream * is = [[NSInputStream alloc] initWithFileAtPath:path];
297
+ __block NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:append];
298
+ [is open];
299
+ [os open];
300
+ uint8_t buffer[10240];
301
+ __block long written = 0;
302
+ int read = [is read:buffer maxLength:10240];
303
+ written += read;
304
+ while(read > 0) {
305
+ [os write:buffer maxLength:read];
306
+ read = [is read:buffer maxLength:10240];
307
+ written += read;
308
+ }
309
+ [os close];
310
+ [is close];
311
+ __block NSNumber * size = [NSNumber numberWithLong:written];
312
+ callback(nil, size);
313
+ }
314
+ else if(asset != nil)
315
+ {
316
+
317
+ __block NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:append];
318
+ int read = 0;
319
+ int cursor = 0;
320
+ __block long written = 0;
321
+ uint8_t buffer[10240];
322
+ [os open];
323
+ while((read = [asset getBytes:buffer fromOffset:cursor length:10240 error:nil]) > 0)
324
+ {
325
+ cursor += read;
326
+ [os write:buffer maxLength:read];
327
+ }
328
+ __block NSNumber * size = [NSNumber numberWithLong:written];
329
+ [os close];
330
+ callback(nil, size);
331
+ }
332
+ else
333
+ callback(@"failed to resolve path", nil);
334
+ }];
335
+
336
+ return 0;
337
+ }
338
+
339
+ # pragma mark - write file
340
+
341
+ + (void) writeFile:(NSString *)path
342
+ encoding:(NSString *)encoding
343
+ data:(NSString *)data
344
+ transformFile:(BOOL)transformFile
345
+ append:(BOOL)append
346
+ resolver:(RCTPromiseResolveBlock)resolve
347
+ rejecter:(RCTPromiseRejectBlock)reject
348
+ {
349
+ @try {
350
+ NSFileManager * fm = [NSFileManager defaultManager];
351
+ NSError * err = nil;
352
+ // check if the folder exists, if it does not exist create folders recursively
353
+ // after the folders created, write data into the file
354
+ NSString * folder = [path stringByDeletingLastPathComponent];
355
+ encoding = [encoding lowercaseString];
356
+
357
+ BOOL isDir = NO;
358
+ BOOL exists = NO;
359
+ exists = [fm fileExistsAtPath:path isDirectory: &isDir];
360
+
361
+ if (isDir) {
362
+ return reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
363
+ }
364
+
365
+ if(!exists) {
366
+ [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:&err];
367
+ if(err != nil) {
368
+ return reject(@"ENOTDIR", [NSString stringWithFormat:@"Failed to create parent directory of '%@'; error: %@", path, [err description]], nil);
369
+ }
370
+ if(![fm createFileAtPath:path contents:nil attributes:nil]) {
371
+ return reject(@"ENOENT", [NSString stringWithFormat:@"File '%@' does not exist and could not be created", path], nil);
372
+ }
373
+ }
374
+
375
+ NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
376
+ NSData * content = nil;
377
+ if([encoding containsString:@"base64"]) {
378
+ content = [[NSData alloc] initWithBase64EncodedString:data options:0];
379
+ }
380
+ else if([encoding isEqualToString:@"uri"]) {
381
+ NSNumber* size = [[self class] writeFileFromFile:data toFile:path append:append callback:^(NSString *errMsg, NSNumber *size) {
382
+ if(errMsg != nil)
383
+ reject(@"EUNSPECIFIED", errMsg, nil);
384
+ else
385
+ resolve(size);
386
+ }];
387
+ return;
388
+ }
389
+ else {
390
+ content = [data dataUsingEncoding:NSUTF8StringEncoding];
391
+ }
392
+
393
+ if (transformFile) {
394
+ NSObject<FileTransformer>* fileTransformer = [ReactNativeBlobUtilFileTransformer getFileTransformer];
395
+ if (fileTransformer) {
396
+ content = [fileTransformer onWriteFile:content];
397
+ } else {
398
+ return reject(@"EUNSPECIFIED",@"Transform specified but transformer not set", nil);
399
+ }
400
+ }
401
+
402
+ if(append == YES) {
403
+ [fileHandle seekToEndOfFile];
404
+ [fileHandle writeData:content];
405
+ [fileHandle closeFile];
406
+ }
407
+ else {
408
+ if (![content writeToFile:path options:NSDataWritingAtomic error:&err]) {
409
+ fm = nil;
410
+ return reject(@"EUNSPECIFIED", [NSString stringWithFormat:@"File '%@' could not be written; error: %@", path, [err description]], err);
411
+ }
412
+ }
413
+ fm = nil;
414
+
415
+ resolve([NSNumber numberWithInteger:[content length]]);
416
+ }
417
+ @catch (NSException * e)
418
+ {
419
+ reject(@"EUNSPECIFIED", [e description], nil);
420
+ }
421
+ }
422
+
423
+ # pragma mark - write file (array)
424
+
425
+ + (void) writeFileArray:(NSString *)path
426
+ data:(NSArray *)data
427
+ append:(BOOL)append
428
+ resolver:(RCTPromiseResolveBlock)resolve
429
+ rejecter:(RCTPromiseRejectBlock)reject
430
+ {
431
+ @try {
432
+ NSFileManager * fm = [NSFileManager defaultManager];
433
+ NSError * err = nil;
434
+ // check if the folder exists, if not exists, create folders recursively
435
+ // after the folders created, write data into the file
436
+ NSString * folder = [path stringByDeletingLastPathComponent];
437
+
438
+ BOOL isDir = NO;
439
+ BOOL exists = NO;
440
+ exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
441
+
442
+ if (isDir) {
443
+ return reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
444
+ }
445
+
446
+ if(!exists) {
447
+ [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:&err];
448
+ if(err != nil) {
449
+ return reject(@"EUNSPECIFIED", [NSString stringWithFormat:@"Failed to create parent directory of '%@'; error: %@", path, [err description]], nil);
450
+ }
451
+ }
452
+
453
+ NSMutableData * fileContent = [NSMutableData alloc];
454
+ // prevent stack overflow, alloc on heap
455
+ char * bytes = (char*) malloc([data count]);
456
+ for(int i = 0; i < data.count; i++) {
457
+ bytes[i] = [[data objectAtIndex:i] charValue];
458
+ }
459
+ [fileContent appendBytes:bytes length:data.count];
460
+
461
+ if(!exists) {
462
+ if(![fm createFileAtPath:path contents:fileContent attributes:NULL]) {
463
+ return reject(@"ENOENT", [NSString stringWithFormat:@"File '%@' does not exist and could not be created", path], nil);
464
+ }
465
+ }
466
+ // if file exists, write file
467
+ else {
468
+ if(append == YES) {
469
+ NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
470
+ [fileHandle seekToEndOfFile];
471
+ [fileHandle writeData:fileContent];
472
+ [fileHandle closeFile];
473
+ }
474
+ else {
475
+ if (![fileContent writeToFile:path atomically:YES]) {
476
+ free(bytes);
477
+ fm = nil;
478
+ return reject(@"EUNSPECIFIED", [NSString stringWithFormat:@"File '%@' could not be written.", path], nil);
479
+ }
480
+ }
481
+ }
482
+ free(bytes);
483
+ fm = nil;
484
+ resolve([NSNumber numberWithInteger: data.count]);
485
+ }
486
+ @catch (NSException * e)
487
+ {
488
+ reject(@"EUNSPECIFIED", [e description], nil);
489
+ }
490
+ }
491
+
492
+ # pragma mark - read file
493
+
494
+ + (void) readFile:(NSString *)path
495
+ encoding:(NSString *)encoding
496
+ transformFile:(BOOL) transformFile
497
+ onComplete:(void (^)(NSData * content, NSString * codeStr, NSString * errMsg))onComplete
498
+ {
499
+ [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
500
+ __block NSData * fileContent;
501
+ NSError * err;
502
+ __block Byte * buffer;
503
+ if(asset != nil)
504
+ {
505
+ int size = asset.size;
506
+ buffer = (Byte *)malloc(size);
507
+ [asset getBytes:buffer fromOffset:0 length:asset.size error:&err];
508
+ if(err != nil)
509
+ {
510
+ onComplete(nil, @"EUNSPECIFIED", [err description]);
511
+ free(buffer);
512
+ return;
513
+ }
514
+ fileContent = [NSData dataWithBytes:buffer length:size];
515
+ free(buffer);
516
+ }
517
+ else
518
+ {
519
+ BOOL isDir = NO;
520
+ if(![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir]) {
521
+ if (isDir) {
522
+ onComplete(nil, @"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path]);
523
+ } else {
524
+ onComplete(nil, @"ENOENT", [NSString stringWithFormat:@"No such file '%@'", path]);
525
+ }
526
+ return;
527
+ }
528
+ fileContent = [NSData dataWithContentsOfFile:path];
529
+
530
+ }
531
+
532
+ if (transformFile) {
533
+ NSObject<FileTransformer>* fileTransformer = [ReactNativeBlobUtilFileTransformer getFileTransformer];
534
+ if (fileTransformer) {
535
+ @try{
536
+ fileContent = [fileTransformer onReadFile:fileContent];
537
+ } @catch (NSException * ex)
538
+ {
539
+ onComplete(nil, @"EUNSPECIFIED", [NSString stringWithFormat:@"Exception on File Transformer: '%@' ", [ex description]]);
540
+ return;
541
+ }
542
+ } else {
543
+ onComplete(nil, @"EUNSPECIFIED", @"Transform specified but transformer not set");
544
+ return;
545
+ }
546
+ }
547
+
548
+ if(encoding != nil)
549
+ {
550
+ if([[encoding lowercaseString] isEqualToString:@"utf8"])
551
+ {
552
+ NSString * utf8 = [[NSString alloc] initWithData:fileContent encoding:NSUTF8StringEncoding];
553
+ if(utf8 == nil) {
554
+ NSString * latin1 = [[NSString alloc] initWithData:fileContent encoding:NSISOLatin1StringEncoding];
555
+ NSData * latin1Data = [latin1 dataUsingEncoding:NSISOLatin1StringEncoding];
556
+ onComplete(latin1Data, nil, nil);
557
+ } else {
558
+ onComplete(fileContent, nil, nil);
559
+ }
560
+ }
561
+ else if ([[encoding lowercaseString] isEqualToString:@"base64"]) {
562
+ NSString * base64String = [fileContent base64EncodedStringWithOptions:0];
563
+ onComplete([[NSData alloc] initWithBase64EncodedString:base64String options:0], nil, nil);
564
+ }
565
+ else if ([[encoding lowercaseString] isEqualToString:@"ascii"]) {
566
+ NSMutableArray * resultArray = [NSMutableArray array];
567
+ char * bytes = (char *)[fileContent bytes];
568
+ for(int i=0;i<[fileContent length];i++) {
569
+ [resultArray addObject:[NSNumber numberWithChar:bytes[i]]];
570
+ }
571
+ onComplete((NSData *)resultArray, nil, nil);
572
+ }
573
+ }
574
+ else
575
+ {
576
+ onComplete(fileContent, nil, nil);
577
+ }
578
+
579
+ }];
580
+ }
581
+
582
+ # pragma mark - hash
583
+
584
+ typedef enum {
585
+ HashAlgorithmMD5,
586
+ HashAlgorithmSHA1,
587
+ HashAlgorithmSHA224,
588
+ HashAlgorithmSHA256,
589
+ HashAlgorithmSHA384,
590
+ HashAlgorithmSHA512,
591
+ } HashAlgorithm;
592
+
593
+ + (void) hash:(NSString *)path
594
+ algorithm:(NSString *)algorithm
595
+ resolver:(RCTPromiseResolveBlock)resolve
596
+ rejecter:(RCTPromiseRejectBlock)reject
597
+ {
598
+ BOOL isDir = NO;
599
+ BOOL exists = NO;
600
+ exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
601
+
602
+ if (isDir) {
603
+ return reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
604
+ }
605
+ if (!exists) {
606
+ return reject(@"ENOENT", [NSString stringWithFormat:@"No such file '%@'", path], nil);
607
+ }
608
+
609
+ NSError *error = nil;
610
+
611
+ NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
612
+
613
+ if (error) {
614
+ reject(@"EUNKNOWN", [error description], nil);
615
+ return;
616
+ }
617
+
618
+ if ([attributes objectForKey:NSFileType] == NSFileTypeDirectory) {
619
+ reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
620
+ return;
621
+ }
622
+
623
+ NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
624
+ if (fileHandle == nil) {
625
+ reject(@"EUNKNOWN", [NSString stringWithFormat:@"Error opening '%@' for reading", path], error);
626
+ return;
627
+ }
628
+
629
+ NSArray *keys = [NSArray arrayWithObjects:@"md5", @"sha1", @"sha224", @"sha256", @"sha384", @"sha512", nil];
630
+
631
+ NSArray *digestLengths = [NSArray arrayWithObjects:
632
+ @CC_MD5_DIGEST_LENGTH,
633
+ @CC_SHA1_DIGEST_LENGTH,
634
+ @CC_SHA224_DIGEST_LENGTH,
635
+ @CC_SHA256_DIGEST_LENGTH,
636
+ @CC_SHA384_DIGEST_LENGTH,
637
+ @CC_SHA512_DIGEST_LENGTH,
638
+ nil];
639
+
640
+ NSDictionary *keysToDigestLengths = [NSDictionary dictionaryWithObjects:digestLengths forKeys:keys];
641
+
642
+ int digestLength = [[keysToDigestLengths objectForKey:algorithm] intValue];
643
+
644
+ if (!digestLength) {
645
+ return reject(@"EINVAL", [NSString stringWithFormat:@"Invalid algorithm '%@', must be one of md5, sha1, sha224, sha256, sha384, sha512", algorithm], nil);
646
+ }
647
+
648
+ unsigned char buffer[digestLength];
649
+
650
+ const NSUInteger chunkSize = 1024 * 1024; // 1 Megabyte
651
+ NSData *dataChunk;
652
+
653
+ CC_MD5_CTX md5Context;
654
+ CC_SHA1_CTX sha1Context;
655
+ CC_SHA256_CTX sha256Context;
656
+ CC_SHA512_CTX sha512Context;
657
+ HashAlgorithm hashAlgorithm;
658
+
659
+ if ([algorithm isEqualToString:@"md5"]) {
660
+ CC_MD5_Init(&md5Context);
661
+ hashAlgorithm = HashAlgorithmMD5;
662
+ } else if ([algorithm isEqualToString:@"sha1"]) {
663
+ CC_SHA1_Init(&sha1Context);
664
+ hashAlgorithm = HashAlgorithmSHA1;
665
+ } else if ([algorithm isEqualToString:@"sha224"]) {
666
+ CC_SHA224_Init(&sha256Context);
667
+ hashAlgorithm = HashAlgorithmSHA224;
668
+ } else if ([algorithm isEqualToString:@"sha256"]) {
669
+ CC_SHA256_Init(&sha256Context);
670
+ hashAlgorithm = HashAlgorithmSHA256;
671
+ } else if ([algorithm isEqualToString:@"sha384"]) {
672
+ CC_SHA384_Init(&sha512Context);
673
+ hashAlgorithm = HashAlgorithmSHA384;
674
+ } else if ([algorithm isEqualToString:@"sha512"]) {
675
+ CC_SHA512_Init(&sha512Context);
676
+ hashAlgorithm = HashAlgorithmSHA512;
677
+ } else {
678
+ reject(@"EINVAL", [NSString stringWithFormat:@"Invalid algorithm '%@', must be one of md5, sha1, sha224, sha256, sha384, sha512", algorithm], nil);
679
+ return;
680
+ }
681
+
682
+ while (true) {
683
+ @autoreleasepool {
684
+ dataChunk = [fileHandle readDataUpToLength:chunkSize error:&error];
685
+
686
+ if (error) {
687
+ return reject(@"EREAD", [NSString stringWithFormat:@"Error reading file '%@'", path], error);
688
+ break;
689
+ }
690
+
691
+ if (dataChunk == nil || dataChunk.length == 0) {
692
+ break;
693
+ }
694
+
695
+ switch(hashAlgorithm) {
696
+ case HashAlgorithmMD5:
697
+ CC_MD5_Update(&md5Context, [dataChunk bytes], CC_LONG([dataChunk length]));
698
+ break;
699
+ case HashAlgorithmSHA1:
700
+ CC_SHA1_Update(&sha1Context, [dataChunk bytes], CC_LONG([dataChunk length]));
701
+ break;
702
+ case HashAlgorithmSHA224:
703
+ CC_SHA224_Update(&sha256Context, [dataChunk bytes], CC_LONG([dataChunk length]));
704
+ break;
705
+ case HashAlgorithmSHA256:
706
+ CC_SHA256_Update(&sha256Context, [dataChunk bytes], CC_LONG([dataChunk length]));
707
+ break;
708
+ case HashAlgorithmSHA384:
709
+ CC_SHA384_Update(&sha512Context, [dataChunk bytes], CC_LONG([dataChunk length]));
710
+ break;
711
+ case HashAlgorithmSHA512:
712
+ CC_SHA512_Update(&sha512Context, [dataChunk bytes], CC_LONG([dataChunk length]));
713
+ break;
714
+ }
715
+
716
+ dataChunk = nil;
717
+ }
718
+ }
719
+
720
+ switch(hashAlgorithm) {
721
+ case HashAlgorithmMD5:
722
+ CC_MD5_Final(buffer, &md5Context);
723
+ break;
724
+ case HashAlgorithmSHA1:
725
+ CC_SHA1_Final(buffer, &sha1Context);
726
+ break;
727
+ case HashAlgorithmSHA224:
728
+ CC_SHA224_Final(buffer, &sha256Context);
729
+ break;
730
+ case HashAlgorithmSHA256:
731
+ CC_SHA256_Final(buffer, &sha256Context);
732
+ break;
733
+ case HashAlgorithmSHA384:
734
+ CC_SHA384_Final(buffer, &sha512Context);
735
+ break;
736
+ case HashAlgorithmSHA512:
737
+ CC_SHA512_Final(buffer, &sha512Context);
738
+ break;
739
+ }
740
+
741
+ NSMutableString *output = [NSMutableString stringWithCapacity:digestLength * 2];
742
+ for(int i = 0; i < digestLength; i++)
743
+ [output appendFormat:@"%02x",buffer[i]];
744
+
745
+ resolve(output);
746
+ }
747
+
748
+ # pragma mark - mkdir
749
+
750
+ + (void) mkdir:(NSString *) path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
751
+ {
752
+ BOOL isDir = NO;
753
+ NSError * err = nil;
754
+ if([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
755
+ reject(@"EEXIST", [NSString stringWithFormat:@"%@ '%@' already exists", isDir ? @"Directory" : @"File", path], nil);
756
+ return;
757
+ }
758
+ else {
759
+ [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&err];
760
+ }
761
+ if(err == nil) {
762
+ resolve(@YES);
763
+ }
764
+ else {
765
+ reject(@"EUNSPECIFIED", [NSString stringWithFormat:@"Error creating folder '%@', error: %@", path, [err description]], nil);
766
+ }
767
+ }
768
+
769
+ # pragma mark - stat
770
+
771
+ + (NSDictionary *) stat:(NSString *) path error:(NSError **) error {
772
+
773
+
774
+ BOOL isDir = NO;
775
+ NSFileManager * fm = [NSFileManager defaultManager];
776
+ if([fm fileExistsAtPath:path isDirectory:&isDir] == NO) {
777
+ return nil;
778
+ }
779
+ NSDictionary * info = [fm attributesOfItemAtPath:path error:error];
780
+ NSString * size = [NSString stringWithFormat:@"%llu", [info fileSize]];
781
+ NSString * filename = [path lastPathComponent];
782
+ NSDate * lastModified;
783
+ [[NSURL fileURLWithPath:path] getResourceValue:&lastModified forKey:NSURLContentModificationDateKey error:error];
784
+ return @{
785
+ @"size" : size,
786
+ @"filename" : filename,
787
+ @"path" : path,
788
+ @"lastModified" : [NSNumber numberWithLong:(time_t) [lastModified timeIntervalSince1970]*1000],
789
+ @"type" : isDir ? @"directory" : @"file"
790
+ };
791
+
792
+ }
793
+
794
+ # pragma mark - exists
795
+
796
+ + (void) exists:(NSString *) path callback:(RCTResponseSenderBlock)callback
797
+ {
798
+ [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
799
+ if(path != nil)
800
+ {
801
+ BOOL isDir = NO;
802
+ BOOL exists = NO;
803
+ exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
804
+ callback(@[@(exists), @(isDir)]);
805
+ }
806
+ else if(asset != nil)
807
+ {
808
+ callback(@[@YES, @NO]);
809
+ }
810
+ else
811
+ {
812
+ callback(@[@NO, @NO]);
813
+ }
814
+ }];
815
+ }
816
+
817
+ # pragma mark - open file stream
818
+
819
+ // Create file stream for write data
820
+ - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append {
821
+ self.outStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:append];
822
+ self.encoding = encode;
823
+ [self.outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
824
+ [self.outStream open];
825
+ NSString *uuid = [[NSUUID UUID] UUIDString];
826
+ self.streamId = uuid;
827
+ [ReactNativeBlobUtilFS setFileStream:self withId:uuid];
828
+ return uuid;
829
+ }
830
+
831
+ # pragma mark - file stream write chunk
832
+
833
+ // Write file chunk into an opened stream
834
+ - (void)writeEncodeChunk:(NSString *) chunk {
835
+ NSData * decodedData = nil;
836
+ if([[self.encoding lowercaseString] isEqualToString:@"base64"]) {
837
+ decodedData = [[NSData alloc] initWithBase64EncodedString:chunk options: NSDataBase64DecodingIgnoreUnknownCharacters];
838
+ }
839
+ else if([[self.encoding lowercaseString] isEqualToString:@"utf8"]) {
840
+ decodedData = [chunk dataUsingEncoding:NSUTF8StringEncoding];
841
+ }
842
+ else if([[self.encoding lowercaseString] isEqualToString:@"ascii"]) {
843
+ decodedData = [chunk dataUsingEncoding:NSASCIIStringEncoding];
844
+ }
845
+ NSUInteger left = [decodedData length];
846
+ NSUInteger nwr = 0;
847
+ do {
848
+ nwr = [self.outStream write:(const uint8_t *)[decodedData bytes] maxLength:left];
849
+ if (-1 == nwr) break;
850
+ left -= nwr;
851
+ } while (left > 0);
852
+ if (left) {
853
+ NSLog(@"stream error: %@", [self.outStream streamError]);
854
+ }
855
+ }
856
+
857
+ // Write file chunk into an opened stream
858
+ - (void)write:(NSData *) chunk {
859
+ NSUInteger left = [chunk length];
860
+ NSUInteger nwr = 0;
861
+ do {
862
+ nwr = [self.outStream write:(const uint8_t *)[chunk bytes] maxLength:left];
863
+ if (-1 == nwr) break;
864
+ left -= nwr;
865
+ } while (left > 0);
866
+ if (left) {
867
+ NSLog(@"stream error: %@", [self.outStream streamError]);
868
+ }
869
+ }
870
+
871
+ // close file write stream
872
+ - (void)closeOutStream {
873
+ if(self.outStream != nil) {
874
+ [self.outStream close];
875
+ self.outStream = nil;
876
+ }
877
+
878
+ }
879
+
880
+ // Slice a file into another file, generally for support Blob implementation.
881
+ + (void)slice:(NSString *)path
882
+ dest:(NSString *)dest
883
+ start:(nonnull NSNumber *)start
884
+ end:(nonnull NSNumber *)end
885
+ encode:(NSString *)encode
886
+ resolver:(RCTPromiseResolveBlock)resolve
887
+ rejecter:(RCTPromiseRejectBlock)reject
888
+ {
889
+ [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset)
890
+ {
891
+ if(path != nil)
892
+ {
893
+ long expected = [end longValue] - [start longValue];
894
+ long read = 0;
895
+ NSFileHandle * handle = [NSFileHandle fileHandleForReadingAtPath:path];
896
+ NSFileManager * fm = [NSFileManager defaultManager];
897
+ NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
898
+ [os open];
899
+
900
+ BOOL isDir = NO;
901
+ BOOL exists = NO;
902
+ exists = [fm fileExistsAtPath:path isDirectory: &isDir];
903
+
904
+ if (isDir) {
905
+ return reject(@"EISDIR", [NSString stringWithFormat:@"Expecting a file but '%@' is a directory", path], nil);
906
+ }
907
+ if(!exists) {
908
+ return reject(@"ENOENT", [NSString stringWithFormat: @"No such file '%@'", path ], nil);
909
+ }
910
+
911
+ long size = [fm attributesOfItemAtPath:path error:nil].fileSize;
912
+ long max = MIN(size, [end longValue]);
913
+
914
+ if(![fm fileExistsAtPath:dest]) {
915
+ if(![fm createFileAtPath:dest contents:[NSData new] attributes:nil]) {
916
+ return reject(@"ENOENT", [NSString stringWithFormat:@"File '%@' does not exist and could not be created", path], nil);
917
+ }
918
+ }
919
+ [handle seekToFileOffset:[start longValue]];
920
+ while(read < expected) {
921
+ NSData * chunk;
922
+ long chunkSize = 0;
923
+ if([start longValue] + read + 10240 > max)
924
+ {
925
+ NSLog(@"read chunk %lu", max - read - [start longValue]);
926
+ chunkSize = max - read - [start longValue];
927
+ chunk = [handle readDataOfLength:chunkSize];
928
+ }
929
+ else
930
+ {
931
+ NSLog(@"read chunk %d", 10240);
932
+ chunkSize = 10240;
933
+ chunk = [handle readDataOfLength:10240];
934
+ }
935
+ if([chunk length] <= 0)
936
+ break;
937
+ long remain = expected - read;
938
+
939
+ [os write:(const uint8_t *)[chunk bytes] maxLength:chunkSize];
940
+ read += [chunk length];
941
+ }
942
+ [handle closeFile];
943
+ [os close];
944
+ resolve(dest);
945
+ }
946
+ else if (asset != nil)
947
+ {
948
+ long expected = [end longValue] - [start longValue];
949
+ long read = 0;
950
+ long chunkRead = 0;
951
+ NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
952
+ [os open];
953
+ long size = asset.size;
954
+ long max = MIN(size, [end longValue]);
955
+
956
+ while(read < expected) {
957
+ uint8_t chunk[10240];
958
+ uint8_t * pointerToChunk = &chunk[0];
959
+ long chunkSize = 0;
960
+ if([start longValue] + read + 10240 > max)
961
+ {
962
+ NSLog(@"read chunk %lu", max - read - [start longValue]);
963
+ chunkSize = max - read - [start longValue];
964
+ chunkRead = [asset getBytes:pointerToChunk fromOffset:[start longValue] + read length:chunkSize error:nil];
965
+ }
966
+ else
967
+ {
968
+ NSLog(@"read chunk %lu", 10240);
969
+ chunkSize = 10240;
970
+ chunkRead = [asset getBytes:pointerToChunk fromOffset:[start longValue] + read length:chunkSize error:nil];
971
+ }
972
+ if( chunkRead <= 0)
973
+ break;
974
+ long remain = expected - read;
975
+
976
+ [os write:chunk maxLength:chunkSize];
977
+ read += chunkRead;
978
+ }
979
+ [os close];
980
+ resolve(dest);
981
+ }
982
+ else {
983
+ reject(@"EINVAL", [NSString stringWithFormat: @"Could not resolve URI %@", path ], nil);
984
+ }
985
+
986
+ }];
987
+ }
988
+
989
+ // close file read stream
990
+ - (void)closeInStream
991
+ {
992
+ if(self.inStream != nil) {
993
+ [self.inStream close];
994
+ [self.inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
995
+ [[ReactNativeBlobUtilFS getFileStreams] setValue:nil forKey:self.streamId];
996
+ self.streamId = nil;
997
+ }
998
+
999
+ }
1000
+
1001
+
1002
+ # pragma mark - get absolute path of resource
1003
+
1004
+ + (void) getPathFromUri:(NSString *)uri completionHandler:(void(^)(NSString * path, ALAssetRepresentation *asset)) onComplete
1005
+ {
1006
+ if([uri hasPrefix:AL_PREFIX])
1007
+ {
1008
+ NSURL *asseturl = [NSURL URLWithString:uri];
1009
+ __block ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
1010
+ [assetslibrary assetForURL:asseturl
1011
+ resultBlock:^(ALAsset *asset) {
1012
+ __block ALAssetRepresentation * present = [asset defaultRepresentation];
1013
+ onComplete(nil, present);
1014
+ }
1015
+ failureBlock:^(NSError *error) {
1016
+ onComplete(nil, nil);
1017
+ }];
1018
+ }
1019
+ else
1020
+ {
1021
+ onComplete([[self class] getPathOfAsset:uri], nil);
1022
+ }
1023
+ }
1024
+
1025
+ #pragma mark - get disk space
1026
+
1027
+ +(void) df:(RCTResponseSenderBlock)callback
1028
+ {
1029
+ NSError *error = nil;
1030
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
1031
+ NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];
1032
+
1033
+ if (dictionary) {
1034
+ NSNumber *fileSystemSizeInBytes = [dictionary objectForKey: NSFileSystemSize];
1035
+ NSNumber *freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize];
1036
+
1037
+ callback(@[[NSNull null], @{
1038
+ @"free" : freeFileSystemSizeInBytes,
1039
+ @"total" : fileSystemSizeInBytes,
1040
+ }]);
1041
+ } else {
1042
+ callback(@[@"failed to get storage usage."]);
1043
+ }
1044
+
1045
+ }
1046
+
1047
+ + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest
1048
+ {
1049
+ int read = 0;
1050
+ int cursor = 0;
1051
+ Byte * buffer = (Byte *)malloc(10240);
1052
+ NSOutputStream * ostream = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
1053
+ [ostream open];
1054
+ while((read = [rep getBytes:buffer fromOffset:cursor length:10240 error:nil]) > 0)
1055
+ {
1056
+ cursor+=10240;
1057
+ [ostream write:buffer maxLength:read];
1058
+ }
1059
+ [ostream close];
1060
+ free(buffer);
1061
+ return;
1062
+ }
1063
+
1064
+ @end