react-native-blob-util 0.24.5 → 0.24.7

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