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.
- package/.eslintrc.js +56 -56
- package/LICENSE +21 -21
- package/Migration.md +41 -41
- package/NuGet.config +11 -11
- package/README.md +1109 -1104
- package/android/gradle.properties +1 -1
- package/android/src/main/AndroidManifest.xml +37 -37
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +57 -57
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConst.java +20 -20
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +9 -9
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilImpl.java +431 -431
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilProgressConfig.java +39 -39
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +1014 -1014
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilDefaultResp.java +98 -98
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +166 -166
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileDescription.java +19 -19
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileProvider.java +4 -4
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/MimeType.java +70 -70
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/PathResolver.java +223 -223
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/Tls12SocketFactory.java +68 -68
- package/android/src/main/res/values/strings.xml +3 -3
- package/android/src/main/res/xml/provider_paths.xml +12 -12
- package/android.js +58 -58
- package/class/ReactNativeBlobUtilCanceledFetchError.js +9 -9
- package/class/ReactNativeBlobUtilFile.js +5 -5
- package/class/ReactNativeBlobUtilReadStream.js +84 -84
- package/class/ReactNativeBlobUtilSession.js +68 -68
- package/class/ReactNativeBlobUtilWriteStream.js +50 -50
- package/class/StatefulPromise.js +7 -7
- package/codegenSpecs/NativeBlobUtils.js +86 -86
- package/components/Fetch.onPress.js +10 -10
- package/components/Fetch.when.js +15 -15
- package/fetch.js +350 -350
- package/fs.js +480 -480
- package/index.js +64 -64
- package/index.js.flow +192 -192
- package/index.web.js +1 -1
- package/ios/PrivacyInfo.xcprivacy +28 -28
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.h +55 -55
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.mm +992 -992
- package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +405 -405
- package/ios/ReactNativeBlobUtilConst.h +58 -58
- package/ios/ReactNativeBlobUtilConst.mm +47 -47
- package/ios/ReactNativeBlobUtilFS.h +108 -108
- package/ios/ReactNativeBlobUtilFS.mm +1064 -1064
- package/ios/ReactNativeBlobUtilFileTransformer.h +23 -23
- package/ios/ReactNativeBlobUtilFileTransformer.mm +20 -20
- package/ios/ReactNativeBlobUtilNetwork.h +49 -49
- package/ios/ReactNativeBlobUtilNetwork.mm +159 -159
- package/ios/ReactNativeBlobUtilProgress.h +32 -32
- package/ios/ReactNativeBlobUtilProgress.mm +57 -57
- package/ios/ReactNativeBlobUtilReqBuilder.h +37 -37
- package/ios/ReactNativeBlobUtilReqBuilder.mm +292 -292
- package/ios/ReactNativeBlobUtilRequest.h +48 -48
- package/ios/ReactNativeBlobUtilRequest.mm +584 -584
- package/ios.js +61 -61
- package/json-stream.js +42 -42
- package/lib/oboe-browser.js +2703 -2703
- package/mediacollection.js +36 -36
- package/package.json +68 -68
- package/polyfill/Blob.js +362 -362
- package/polyfill/Event.js +11 -11
- package/polyfill/EventTarget.js +78 -78
- package/polyfill/Fetch.js +219 -219
- package/polyfill/File.js +27 -27
- package/polyfill/FileReader.js +91 -91
- package/polyfill/ProgressEvent.js +32 -32
- package/polyfill/XMLHttpRequest.js +466 -466
- package/polyfill/XMLHttpRequestEventTarget.js +126 -126
- package/polyfill/index.js +11 -11
- package/react-native-blob-util.podspec +48 -48
- package/types.js +69 -69
- package/utils/log.js +40 -40
- package/utils/unicode.js +7 -7
- package/utils/uri.js +33 -33
- package/utils/uuid.js +4 -4
- package/windows/ExperimentalFeatures.props +33 -33
- package/windows/README.md +2 -2
- package/windows/ReactNativeBlobUtil/PropertySheet.props +15 -15
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.cpp +1836 -1836
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.def +3 -3
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.h +372 -372
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj +139 -139
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj.filters +43 -43
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.cpp +20 -20
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.h +24 -24
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.idl +9 -9
- package/windows/ReactNativeBlobUtil/codegen/.clang-format +1 -1
- package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsDataTypes.g.h +42 -42
- package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsSpec.g.h +353 -353
- package/windows/ReactNativeBlobUtil/packages.config +3 -3
- package/windows/ReactNativeBlobUtil/packages.lock.json +59 -59
- package/windows/ReactNativeBlobUtil/pch.cpp +1 -1
- package/windows/ReactNativeBlobUtil/pch.h +30 -30
- package/windows/ReactNativeBlobUtil/resource.h +5 -5
- package/windows/ReactNativeBlobUtil/targetver.h +8 -8
- 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
|