Browse Source

refactor IOS native code

Ben Hsieh 8 years ago
parent
commit
7d42a751de

+ 18
- 0
src/ios/RNFetchBlob.xcodeproj/project.pbxproj View File

@@ -7,6 +7,9 @@
7 7
 	objects = {
8 8
 
9 9
 /* Begin PBXBuildFile section */
10
+		A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */; };
11
+		A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */; };
12
+		A158F4301D0539DB006FFD38 /* RNFetchBlobResp.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobResp.m */; };
10 13
 		A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = A15C30131CD25C330074CB35 /* RNFetchBlob.m */; };
11 14
 		A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */ = {isa = PBXBuildFile; fileRef = A15C30111CD25C330074CB35 /* RNFetchBlob.h */; };
12 15
 /* End PBXBuildFile section */
@@ -24,6 +27,12 @@
24 27
 /* End PBXCopyFilesBuildPhase section */
25 28
 
26 29
 /* Begin PBXFileReference section */
30
+		A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobFS.m; sourceTree = "<group>"; };
31
+		A158F4281D052E57006FFD38 /* RNFetchBlobFS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobFS.h; sourceTree = "<group>"; };
32
+		A158F4291D0534A9006FFD38 /* RNFetchBlobConst.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobConst.h; sourceTree = "<group>"; };
33
+		A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobConst.m; sourceTree = "<group>"; };
34
+		A158F42E1D0539CE006FFD38 /* RNFetchBlobResp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobResp.h; sourceTree = "<group>"; };
35
+		A158F42F1D0539DB006FFD38 /* RNFetchBlobResp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobResp.m; sourceTree = "<group>"; };
27 36
 		A15C300E1CD25C330074CB35 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFetchBlob.a; sourceTree = BUILT_PRODUCTS_DIR; };
28 37
 		A15C30111CD25C330074CB35 /* RNFetchBlob.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFetchBlob.h; path = RNFetchBlob/RNFetchBlob.h; sourceTree = "<group>"; };
29 38
 		A15C30131CD25C330074CB35 /* RNFetchBlob.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFetchBlob.m; path = RNFetchBlob/RNFetchBlob.m; sourceTree = "<group>"; };
@@ -50,6 +59,12 @@
50 59
 		A15C30051CD25C330074CB35 = {
51 60
 			isa = PBXGroup;
52 61
 			children = (
62
+				A158F42F1D0539DB006FFD38 /* RNFetchBlobResp.m */,
63
+				A158F42E1D0539CE006FFD38 /* RNFetchBlobResp.h */,
64
+				A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */,
65
+				A158F4291D0534A9006FFD38 /* RNFetchBlobConst.h */,
66
+				A158F4281D052E57006FFD38 /* RNFetchBlobFS.h */,
67
+				A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */,
53 68
 				A15C30111CD25C330074CB35 /* RNFetchBlob.h */,
54 69
 				A15C30131CD25C330074CB35 /* RNFetchBlob.m */,
55 70
 				A15C300F1CD25C330074CB35 /* Products */,
@@ -122,6 +137,9 @@
122 137
 			buildActionMask = 2147483647;
123 138
 			files = (
124 139
 				A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */,
140
+				A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */,
141
+				A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */,
142
+				A158F4301D0539DB006FFD38 /* RNFetchBlobResp.m in Sources */,
125 143
 				A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */,
126 144
 			);
127 145
 			runOnlyForDeploymentPostprocessing = 0;

+ 0
- 89
src/ios/RNFetchBlob/RNFetchBlob.h View File

@@ -6,96 +6,8 @@
6 6
 
7 7
 #ifndef RNFetchBlob_h
8 8
 #define RNFetchBlob_h
9
-#import <Foundation/Foundation.h>
10 9
 #import "RCTBridgeModule.h"
11 10
 
12
-// lib event
13
-extern NSString *const MSG_EVENT;
14
-extern NSString *const MSG_EVENT_LOG;
15
-extern NSString *const MSG_EVENT_WARN;
16
-extern NSString *const MSG_EVENT_ERROR;
17
-extern NSString *const FILE_PREFIX;
18
-
19
-// config
20
-extern NSString *const CONFIG_USE_TEMP;
21
-extern NSString *const CONFIG_FILE_PATH;
22
-extern NSString *const CONFIG_FILE_EXT;
23
-
24
-// fs events
25
-extern NSString *const FS_EVENT_DATA;
26
-extern NSString *const FS_EVENT_END;
27
-extern NSString *const FS_EVENT_WARN;
28
-extern NSString *const FS_EVENT_ERROR;
29
-
30
-@interface FetchBlobFS : NSObject <NSStreamDelegate>  {
31
-    NSOutputStream * outStream;
32
-    NSInputStream * inStream;
33
-    RCTResponseSenderBlock callback;
34
-    RCTBridge * bridge;
35
-    Boolean isOpen;
36
-    NSString * encoding;
37
-    int bufferSize;
38
-    BOOL appendData;
39
-    NSString * taskId;
40
-    NSString * path;
41
-    NSString * streamId;
42
-}
43
-
44
-@property (nonatomic) NSOutputStream * outStream;
45
-@property (nonatomic) NSInputStream * inStream;
46
-@property (nonatomic) RCTResponseSenderBlock callback;
47
-@property (nonatomic) RCTBridge * bridge;
48
-@property (nonatomic) NSString * encoding;
49
-@property (nonatomic) NSString * taskId;
50
-@property (nonatomic) NSString * path;
51
-@property (nonatomic) int bufferSize;
52
-@property (nonatomic) NSString * streamId;
53
-@property (nonatomic) BOOL appendData;
54
-
55
-+ (NSString *) getTempPath;
56
-+ (FetchBlobFS *) getFileStreams;
57
-- (id) init;
58
-- (void) initWithCallback;
59
-- (void) initWithBridgeRef;
60
-- (void) openWithDestination;
61
-- (void) openWithId;
62
-- (NSString *) openWithPath;
63
-- (void) write;
64
-- (void) read;
65
-- (void) closeInStream;
66
-- (void) closeOutStream;
67
-
68
-@end
69
-
70
-@interface FetchBlobUtils : NSObject  <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
71
-    
72
-    NSString * taskId;
73
-    int expectedBytes;
74
-    int receivedBytes;
75
-    NSMutableData * respData;
76
-    RCTResponseSenderBlock callback;
77
-    RCTBridge * bridge;
78
-    NSDictionary * options;
79
-    FetchBlobFS * fileStream;
80
-}
81
-@property (nonatomic) NSString * taskId;
82
-@property (nonatomic) int expectedBytes;
83
-@property (nonatomic) int receivedBytes;
84
-@property (nonatomic) NSMutableData * respData;
85
-@property (nonatomic) RCTResponseSenderBlock callback;
86
-@property (nonatomic) RCTBridge * bridge;
87
-@property (nonatomic) NSDictionary * options;
88
-@property (nonatomic) FetchBlobFS * fileStream;
89
-
90
-
91
-- (id) init;
92
-- (void) sendRequest;
93
-
94
-+ (NSMutableDictionary *) normalizeHeaders;
95
-
96
-
97
-@end
98
-
99 11
 
100 12
 @interface RNFetchBlob : NSObject <RCTBridgeModule> {
101 13
 
@@ -106,7 +18,6 @@ extern NSString *const FS_EVENT_ERROR;
106 18
 @property (nonatomic) NSString * filePathPrefix;
107 19
 
108 20
 
109
-
110 21
 @end
111 22
 
112 23
 #endif /* RNFetchBlob_h */

+ 28
- 558
src/ios/RNFetchBlob/RNFetchBlob.m View File

@@ -7,541 +7,11 @@
7 7
 #import "RNFetchBlob.h"
8 8
 #import "RCTConvert.h"
9 9
 #import "RCTLog.h"
10
-#import <Foundation/Foundation.h>
11 10
 #import "RCTBridge.h"
12 11
 #import "RCTEventDispatcher.h"
13
-
14
-NSString *const FILE_PREFIX = @"RNFetchBlob-file://";
15
-
16
-// fetch configs
17
-NSString *const CONFIG_USE_TEMP = @"fileCache";
18
-NSString *const CONFIG_FILE_PATH = @"path";
19
-NSString *const CONFIG_FILE_EXT = @"appendExt";
20
-
21
-NSString *const MSG_EVENT = @"RNFetchBlobMessage";
22
-NSString *const MSG_EVENT_LOG = @"log";
23
-NSString *const MSG_EVENT_WARN = @"warn";
24
-NSString *const MSG_EVENT_ERROR = @"error";
25
-NSString *const FS_EVENT_DATA = @"data";
26
-NSString *const FS_EVENT_END = @"end";
27
-NSString *const FS_EVENT_WARN = @"warn";
28
-NSString *const FS_EVENT_ERROR = @"error";
29
-NSMutableDictionary *fileStreams = nil;
30
-
31
-////////////////////////////////////////
32
-//
33
-//  File system access methods
34
-//
35
-////////////////////////////////////////
36
-
37
-@implementation FetchBlobFS
38
-
39
-
40
-@synthesize outStream;
41
-@synthesize inStream;
42
-@synthesize encoding;
43
-@synthesize callback;
44
-@synthesize taskId;
45
-@synthesize path;
46
-@synthesize appendData;
47
-@synthesize bufferSize;
48
-
49
-// static member getter
50
-+ (NSArray *) getFileStreams {
51
-    
52
-    if(fileStreams == nil)
53
-        fileStreams = [[NSMutableDictionary alloc] init];
54
-    return fileStreams;
55
-}
56
-
57
-+(void) setFileStream:(FetchBlobFS *) instance withId:(NSString *) uuid {
58
-    if(fileStreams == nil)
59
-        fileStreams = [[NSMutableDictionary alloc] init];
60
-    [fileStreams setValue:instance forKey:uuid];
61
-}
62
-
63
-+ (NSString *) getCacheDir {
64
-    return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
65
-}
66
-
67
-+ (NSString *) getDocumentDir {
68
-    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
69
-}
70
-
71
-+ (NSString *) getMusicDir {
72
-    return [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) firstObject];
73
-}
74
-
75
-+ (NSString *) getMovieDir {
76
-    return [NSSearchPathForDirectoriesInDomains(NSMoviesDirectory, NSUserDomainMask, YES) firstObject];
77
-}
78
-
79
-+ (NSString *) getPictureDir {
80
-    return [NSSearchPathForDirectoriesInDomains(NSPicturesDirectory, NSUserDomainMask, YES) firstObject];
81
-}
82
-
83
-
84
-+ (NSString *) getTempPath {
85
-    
86
-    return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingString:@"/RNFetchBlob_tmp"];
87
-}
88
-
89
-+ (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext {
90
-    
91
-    NSString * documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
92
-    NSString * filename = [NSString stringWithFormat:@"/RNFetchBlob_tmp/RNFetchBlobTmp_%@", taskId];
93
-    if(ext != nil)
94
-        filename = [filename stringByAppendingString: [NSString stringWithFormat:@".%@", ext]];
95
-    NSString * tempPath = [documentDir stringByAppendingString: filename];
96
-    return tempPath;
97
-}
98
-
99
-+ (BOOL) mkdir:(NSString *) path {
100
-    BOOL isDir;
101
-    NSError * err = nil;
102
-    // if temp folder not exists, create one
103
-    if(![[NSFileManager defaultManager] fileExistsAtPath: path isDirectory:&isDir]) {
104
-        [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&err];
105
-    }
106
-    return err == nil;
107
-}
108
-
109
-+ (NSDictionary *) stat:(NSString *) path error:(NSError **) error{
110
-    NSMutableDictionary *stat = [[NSMutableDictionary alloc] init];
111
-    BOOL isDir = NO;
112
-    NSFileManager * fm = [NSFileManager defaultManager];
113
-    if([fm fileExistsAtPath:path isDirectory:&isDir] == NO) {
114
-        return nil;
115
-    }
116
-    NSDictionary * info = [fm attributesOfItemAtPath:path error:&error];
117
-    NSString * size = [NSString stringWithFormat:@"%d", [info fileSize]];
118
-    NSString * filename = [path lastPathComponent];
119
-    NSDate * lastModified;
120
-    [[NSURL fileURLWithPath:path] getResourceValue:&lastModified forKey:NSURLContentModificationDateKey error:&error];
121
-    return @{
122
-             @"size" : size,
123
-             @"filename" : filename,
124
-             @"path" : path,
125
-             @"lastModified" : [NSString stringWithFormat:@"%d", [lastModified timeIntervalSince1970]],
126
-             @"type" : isDir ? @"directory" : @"file"
127
-        };
128
-}
129
-
130
-+ (BOOL) exists:(NSString *) path {
131
-    return [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:NULL];
132
-}
133
-
134
-- (id)init {
135
-    self = [super init];
136
-    return self;
137
-}
138
-
139
-- (id)initWithCallback:(RCTResponseSenderBlock)callback {
140
-    self = [super init];
141
-    self.callback = callback;
142
-    return self;
143
-}
144
-
145
-- (id)initWithBridgeRef:(RCTBridge *)bridgeRef {
146
-    self = [super init];
147
-    self.bridge = bridgeRef;
148
-    return self;
149
-}
150
-
151
-// Create file stream for write data
152
-- (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append {
153
-    self.outStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:append];
154
-    self.encoding = encode;
155
-    [self.outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
156
-    [self.outStream open];
157
-    NSString *uuid = [[NSUUID UUID] UUIDString];
158
-    self.streamId = uuid;
159
-    [FetchBlobFS setFileStream:self withId:uuid];
160
-    return uuid;
161
-}
162
-
163
-// Write file chunk into an opened stream
164
-- (void)writeEncodeChunk:(NSString *) chunk {
165
-    NSMutableData * decodedData = [NSData alloc];
166
-    if([[self.encoding lowercaseString] isEqualToString:@"base64"]) {
167
-        decodedData = [[NSData alloc] initWithBase64EncodedData:chunk options:0];
168
-    }
169
-    if([[self.encoding lowercaseString] isEqualToString:@"utf8"]) {
170
-        decodedData = [chunk dataUsingEncoding:NSUTF8StringEncoding];
171
-    }
172
-    else if([[self.encoding lowercaseString] isEqualToString:@"ascii"]) {
173
-        decodedData = [chunk dataUsingEncoding:NSASCIIStringEncoding];
174
-    }
175
-    NSUInteger left = [decodedData length];
176
-    NSUInteger nwr = 0;
177
-    do {
178
-        nwr = [self.outStream write:[decodedData bytes] maxLength:left];
179
-        if (-1 == nwr) break;
180
-        left -= nwr;
181
-    } while (left > 0);
182
-    if (left) {
183
-        NSLog(@"stream error: %@", [self.outStream streamError]);
184
-    }
185
-}
186
-
187
-// Write file chunk into an opened stream
188
-- (void)write:(NSData *) chunk {
189
-    NSUInteger left = [chunk length];
190
-    NSUInteger nwr = 0;
191
-    do {
192
-        nwr = [self.outStream write:[chunk bytes] maxLength:left];
193
-        if (-1 == nwr) break;
194
-        left -= nwr;
195
-    } while (left > 0);
196
-    if (left) {
197
-        NSLog(@"stream error: %@", [self.outStream streamError]);
198
-    }
199
-}
200
-
201
-// close file write stream
202
-- (void)closeOutStream {
203
-    if(self.outStream != nil) {
204
-        [self.outStream close];
205
-        self.outStream = nil;
206
-    }
207
-
208
-}
209
-
210
-- (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize{
211
-    
212
-    self.inStream = [[NSInputStream alloc] initWithFileAtPath:path];
213
-    self.inStream.delegate = self;
214
-    self.encoding = encoding;
215
-    self.path = path;
216
-    self.bufferSize = bufferSize;
217
-    
218
-    // NSStream needs a runloop so let's create a run loop for it
219
-    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
220
-    // start NSStream is a runloop
221
-    dispatch_async(queue, ^ {
222
-        [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
223
-                            forMode:NSDefaultRunLoopMode];
224
-        [inStream open];
225
-        [[NSRunLoop currentRunLoop] run];
226
-        
227
-    });
228
-}
229
-
230
-// close file read stream
231
-- (void)closeInStream {
232
-    if(self.inStream != nil) {
233
-        [self.inStream close];
234
-        [self.inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
235
-        [[FetchBlobFS getFileStreams] setValue:nil forKey:self.streamId];
236
-        self.streamId = nil;
237
-    }
238
-    
239
-}
240
-
241
-void runOnMainQueueWithoutDeadlocking(void (^block)(void))
242
-{
243
-    if ([NSThread isMainThread])
244
-    {
245
-        block();
246
-    }
247
-    else
248
-    {
249
-        dispatch_sync(dispatch_get_main_queue(), block);
250
-    }
251
-}
252
-
253
-
254
-#pragma mark RNFetchBlobFS read stream delegate
255
-
256
-- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
257
-
258
-    NSString * streamEventCode = [NSString stringWithFormat:@"RNFetchBlobStream+%@", self.path];
259
-    
260
-    switch(eventCode) {
261
-            
262
-        // write stream event
263
-        case NSStreamEventHasSpaceAvailable:
264
-        {
265
-            
266
-        
267
-        }
268
-        
269
-        // read stream incoming chunk
270
-        case NSStreamEventHasBytesAvailable:
271
-        {
272
-            NSMutableData * chunkData = [[NSMutableData data] init];
273
-            NSInteger chunkSize = 4096;
274
-            if([[self.encoding lowercaseString] isEqualToString:@"base64"])
275
-                chunkSize = 4095;
276
-            if(self.bufferSize > 0)
277
-                chunkSize = self.bufferSize;
278
-            uint8_t buf[chunkSize];
279
-            unsigned int len = 0;
280
-
281
-            len = [(NSInputStream *)stream read:buf maxLength:chunkSize];
282
-            // still have data in stream
283
-            if(len) {
284
-                [chunkData appendBytes:(const void *)buf length:len];
285
-                // dispatch data event
286
-                NSString * encodedChunk = [NSString alloc];
287
-                if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
288
-                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
289
-                }
290
-                // when encoding is ASCII, send byte array data
291
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"ascii"] ) {
292
-                    // RCTBridge only emits string data, so we have to create JSON byte array string
293
-                    NSString * asciiStr = @"[";
294
-                    if (chunkData.length > 0)
295
-                    {
296
-                        unsigned char *bytePtr = (unsigned char *)[chunkData bytes];
297
-                        NSInteger byteLen = chunkData.length/sizeof(uint8_t);
298
-                        for (int i = 0; i < byteLen; i++)
299
-                        {
300
-                            uint8_t * byteFromArray = chunkData.bytes;
301
-                            NSInteger val = bytePtr[i];
302
-                            if(i+1 < byteLen)
303
-                                asciiStr = [asciiStr stringByAppendingFormat:@"%d,", val];
304
-                            else
305
-                                asciiStr = [asciiStr stringByAppendingFormat:@"%d", val];
306
-                        }
307
-                    }
308
-                    asciiStr = [asciiStr stringByAppendingString:@"]"];
309
-                    [self.bridge.eventDispatcher
310
-                     sendDeviceEventWithName:streamEventCode
311
-                     body:@{
312
-                            @"event": FS_EVENT_DATA,
313
-                            @"detail": asciiStr
314
-                        }
315
-                     ];
316
-                    return;
317
-                }
318
-                // convert byte array to base64 data chunks
319
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"base64"] ) {
320
-                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
321
-                }
322
-                // unknown encoding, send erro event
323
-                else {
324
-                    [self.bridge.eventDispatcher
325
-                        sendDeviceEventWithName:streamEventCode
326
-                        body:@{
327
-                            @"event": FS_EVENT_ERROR,
328
-                            @"detail": @"unrecognized encoding"
329
-                        }
330
-                     ];
331
-                    return;
332
-                }
333
-
334
-                [self.bridge.eventDispatcher
335
-                 sendDeviceEventWithName:streamEventCode
336
-                 body:@{
337
-                        @"event": FS_EVENT_DATA,
338
-                        @"detail": encodedChunk
339
-                        }
340
-                 ];
341
-
342
-            }
343
-            // end of stream
344
-            else {
345
-                [self.bridge.eventDispatcher
346
-                sendDeviceEventWithName:streamEventCode
347
-                body:@{
348
-                       @"event": FS_EVENT_END,
349
-                       @"detail": @""
350
-                    }
351
-                ];
352
-            }
353
-            break;
354
-        }
355
-            
356
-        // stream error
357
-        case NSStreamEventErrorOccurred:
358
-        {
359
-            [self.bridge.eventDispatcher
360
-             sendDeviceEventWithName:streamEventCode
361
-             body:@{
362
-                    @"event": FS_EVENT_ERROR,
363
-                    @"detail": @"RNFetchBlob error when read file with stream, file may not exists"
364
-                }
365
-             ];
366
-            break;
367
-        }
368
-    
369
-    }
370
-
371
-}
372
-
373
-@end
374
-
375
-////////////////////////////////////////
376
-//
377
-//  HTTP request handler
378
-//
379
-////////////////////////////////////////
380
-
381
-@implementation FetchBlobUtils
382
-
383
-
384
-@synthesize taskId;
385
-@synthesize expectedBytes;
386
-@synthesize receivedBytes;
387
-@synthesize respData;
388
-@synthesize callback;
389
-@synthesize bridge;
390
-@synthesize options;
391
-
392
-
393
-// removing case from headers
394
-+ (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers {
395
-    
396
-    NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init];
397
-    for(NSString * key in headers) {
398
-        [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]];
399
-    }
400
-    
401
-    return mheaders;
402
-}
403
-
404
-- (id)init {
405
-    self = [super init];
406
-    return self;
407
-}
408
-
409
-
410
-- (void) sendRequest:(NSDictionary *)options bridge:(RCTBridge *)bridgeRef taskId:(NSString *)taskId withRequest:(NSURLRequest *)req callback:(RCTResponseSenderBlock) callback {
411
-    self.taskId = taskId;
412
-    self.respData = [[NSMutableData alloc] initWithLength:0];
413
-    self.callback = callback;
414
-    self.bridge = bridgeRef;
415
-    self.expectedBytes = 0;
416
-    self.receivedBytes = 0;
417
-    self.options = options;
418
-    
419
-    NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
420
-    NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
421
-    
422
-    // open file stream for write
423
-    if( path != nil) {
424
-        self.fileStream = [[FetchBlobFS alloc]initWithCallback:self.callback];
425
-        [self.fileStream openWithPath:path encode:@"ascii" appendData:YES ];
426
-    }
427
-    else if ( [self.options valueForKey:CONFIG_USE_TEMP]!= nil ) {
428
-        self.fileStream = [[FetchBlobFS alloc]initWithCallback:self.callback];
429
-        [self.fileStream openWithPath:[FetchBlobFS getTempPath:taskId withExtension:ext] encode:@"ascii" appendData:YES];
430
-    }
431
-    
432
-    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:NO];
433
-    [conn scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
434
-    [conn start];
435
-    
436
-    if(!conn) {
437
-        callback(@[[NSString stringWithFormat:@"RNFetchBlob could not initialize connection"], [NSNull null]]);
438
-    }
439
-}
440
-
441
-
442
-#pragma mark NSURLConnection delegate methods
443
-
444
-
445
-- (void) connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response {
446
-//    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
447
-    expectedBytes = [response expectedContentLength];
448
-}
449
-
450
-
451
-- (void) connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data {
452
-    receivedBytes += [data length];
453
-    
454
-    Boolean fileCache = [self.options valueForKey:CONFIG_USE_TEMP];
455
-    NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
456
-    if(path != nil) {
457
-        [self.fileStream write:data];
458
-    }
459
-    // write to tmp file
460
-    else if( fileCache != nil) {
461
-        NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
462
-        [self.fileStream write:data];
463
-    }
464
-    // cache data in memory
465
-    else {
466
-        [respData appendData:data];
467
-    }
468
-    
469
-    [self.bridge.eventDispatcher
470
-        sendDeviceEventWithName:@"RNFetchBlobProgress"
471
-        body:@{
472
-            @"taskId": taskId,
473
-            @"written": [NSString stringWithFormat:@"%d", receivedBytes],
474
-            @"total": [NSString stringWithFormat:@"%d", expectedBytes]
475
-        }
476
-     ];
477
-}
478
-
479
-- (void) connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
480
-    
481
-    expectedBytes = totalBytesExpectedToWrite;
482
-    receivedBytes += totalBytesWritten;
483
-    [self.bridge.eventDispatcher
484
-        sendDeviceEventWithName:@"RNFetchBlobProgress"
485
-            body:@{
486
-                    @"taskId": taskId,
487
-                    @"written": [NSString stringWithFormat:@"%d", receivedBytes],
488
-                    @"total": [NSString stringWithFormat:@"%d", expectedBytes]
489
-                }
490
-     ];
491
-    
492
-}
493
-
494
-- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
495
-    
496
-//    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
497
-    
498
-    [self.fileStream closeInStream];
499
-    [self.fileStream closeOutStream];
500
-    
501
-    callback(@[[error localizedDescription], [NSNull null]]);
502
-}
503
-
504
-- (NSCachedURLResponse *) connection:(NSURLConnection *)connection willCacheResponse: (NSCachedURLResponse *)cachedResponse {
505
-    return nil;
506
-}
507
-
508
-
509
-// handle 301 and 302 responses
510
-- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:response {
511
-    return request;
512
-}
513
-
514
-// request complete
515
-- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
516
-    
517
-    NSData * data;
518
-    if(respData != nil)
519
-        data = [NSData dataWithData:respData];
520
-    else
521
-        data = [[NSData alloc] init];
522
-    
523
-    NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
524
-    NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
525
-    Boolean useCache = [self.options valueForKey:CONFIG_USE_TEMP];
526
-    
527
-    [self.fileStream closeInStream];
528
-    
529
-    // if fileCache is true or file path is given, return a path
530
-    if( path != nil ) {
531
-        callback(@[[NSNull null], path]);
532
-    }
533
-    // when fileCache option is set but no path specified, save to tmp path
534
-    else if( [self.options valueForKey:CONFIG_USE_TEMP] != nil) {
535
-        NSString * tmpPath = [FetchBlobFS getTempPath:taskId withExtension:ext];
536
-        callback(@[[NSNull null], tmpPath]);
537
-    }
538
-    // otherwise return base64 string
539
-    else {
540
-        callback(@[[NSNull null], [data base64EncodedStringWithOptions:0]]);
541
-    }
542
-}
543
-
544
-@end
12
+#import "RNFetchBlobFS.h"
13
+#import "RNFetchBlobResp.h"
14
+#import "RNFetchBlobConst.h"
545 15
 
546 16
 
547 17
 ////////////////////////////////////////
@@ -568,8 +38,8 @@ RCT_EXPORT_MODULE();
568 38
     self.filePathPrefix = FILE_PREFIX;
569 39
     BOOL isDir;
570 40
     // if temp folder not exists, create one
571
-    if(![[NSFileManager defaultManager] fileExistsAtPath: [FetchBlobFS getTempPath] isDirectory:&isDir]) {
572
-        [[NSFileManager defaultManager] createDirectoryAtPath:[FetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
41
+    if(![[NSFileManager defaultManager] fileExistsAtPath: [RNFetchBlobFS getTempPath] isDirectory:&isDir]) {
42
+        [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
573 43
     }
574 44
     return self;
575 45
 }
@@ -577,9 +47,9 @@ RCT_EXPORT_MODULE();
577 47
 - (NSDictionary *)constantsToExport
578 48
 {
579 49
     return @{
580
-             @"DocumentDir": [FetchBlobFS getDocumentDir],
581
-             @"CacheDir" : [FetchBlobFS getCacheDir]
582
-            };
50
+             @"DocumentDir": [RNFetchBlobFS getDocumentDir],
51
+             @"CacheDir" : [RNFetchBlobFS getCacheDir]
52
+             };
583 53
 }
584 54
 
585 55
 // Fetch blob data request
@@ -608,8 +78,8 @@ RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options
608 78
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
609 79
         // if method is POST or PUT, convert data string format
610 80
         if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"]) {
611
-        NSMutableData * postData = [[NSMutableData alloc] init];
612
-        
81
+            NSMutableData * postData = [[NSMutableData alloc] init];
82
+            
613 83
             // combine multipart/form-data body
614 84
             for(id field in form) {
615 85
                 NSString * name = [field valueForKey:@"name"];
@@ -641,7 +111,7 @@ RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options
641 111
                 }
642 112
                 
643 113
             }
644
-        
114
+            
645 115
             // close form data
646 116
             [postData appendData: [[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
647 117
             [request setHTTPBody:postData];
@@ -650,7 +120,7 @@ RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options
650 120
             [mheaders setValue:[NSString stringWithFormat:@"100-continue",[postData length]] forKey:@"Expect"];
651 121
             // appaned boundary to content-type
652 122
             [mheaders setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forKey:@"content-type"];
653
-        
123
+            
654 124
         }
655 125
         
656 126
         [request setHTTPMethod: method];
@@ -728,7 +198,7 @@ RCT_EXPORT_METHOD(createFile:(NSString *)path data:(NSString *)data encoding:(NS
728 198
         callback(@[[NSNull null]]);
729 199
     else
730 200
         callback(@[[NSString stringWithFormat:@"failed to create new file at path %@ please ensure the folder exists"]]);
731
-
201
+    
732 202
 }
733 203
 
734 204
 // method for create file with ASCII content
@@ -757,11 +227,11 @@ RCT_EXPORT_METHOD(exists:(NSString *)path callback:(RCTResponseSenderBlock)callb
757 227
     BOOL exists = NO;
758 228
     exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
759 229
     callback(@[@(exists), @(isDir)]);
760
-
230
+    
761 231
 }
762 232
 
763 233
 RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
764
-    FetchBlobFS *fileStream = [[FetchBlobFS alloc] initWithBridgeRef:self.bridge];
234
+    RNFetchBlobFS *fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
765 235
     if(bufferSize == nil) {
766 236
         if([[encoding lowercaseString] isEqualToString:@"base64"])
767 237
             bufferSize = 4095;
@@ -772,7 +242,7 @@ RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding
772 242
 }
773 243
 
774 244
 RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding appendData:(BOOL)append callback:(RCTResponseSenderBlock)callback) {
775
-    FetchBlobFS * fileStream = [[FetchBlobFS alloc] initWithBridgeRef:self.bridge];
245
+    RNFetchBlobFS * fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
776 246
     NSFileManager * fm = [NSFileManager defaultManager];
777 247
     BOOL isDir = nil;
778 248
     BOOL exist = [fm fileExistsAtPath:path isDirectory:&isDir];
@@ -785,7 +255,7 @@ RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding
785 255
 }
786 256
 
787 257
 RCT_EXPORT_METHOD(writeArrayChunk:(NSString *)streamId withArray:(NSArray *)dataArray callback:(RCTResponseSenderBlock) callback) {
788
-    FetchBlobFS *fs = [[FetchBlobFS getFileStreams] valueForKey:streamId];
258
+    RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
789 259
     char bytes[[dataArray count]];
790 260
     for(int i = 0; i < dataArray.count; i++) {
791 261
         bytes[i] = [[dataArray objectAtIndex:i] charValue];
@@ -797,13 +267,13 @@ RCT_EXPORT_METHOD(writeArrayChunk:(NSString *)streamId withArray:(NSArray *)data
797 267
 }
798 268
 
799 269
 RCT_EXPORT_METHOD(writeChunk:(NSString *)streamId withData:(NSString *)data callback:(RCTResponseSenderBlock) callback) {
800
-    FetchBlobFS *fs = [[FetchBlobFS getFileStreams] valueForKey:streamId];
270
+    RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
801 271
     [fs writeEncodeChunk:data];
802 272
     callback(@[[NSNull null]]);
803 273
 }
804 274
 
805 275
 RCT_EXPORT_METHOD(closeStream:(NSString *)streamId callback:(RCTResponseSenderBlock) callback) {
806
-    FetchBlobFS *fs = [[FetchBlobFS getFileStreams] valueForKey:streamId];
276
+    RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
807 277
     [fs closeOutStream];
808 278
     callback(@[[NSNull null], @YES]);
809 279
 }
@@ -862,7 +332,7 @@ RCT_EXPORT_METHOD(stat:(NSString *)path callback:(RCTResponseSenderBlock) callba
862 332
         callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not exist", path]]);
863 333
         return ;
864 334
     }
865
-    NSData * res = [FetchBlobFS stat:path error:&error];
335
+    NSData * res = [RNFetchBlobFS stat:path error:&error];
866 336
     
867 337
     if(error == nil)
868 338
         callback(@[[NSNull null], res]);
@@ -882,16 +352,16 @@ RCT_EXPORT_METHOD(lstat:(NSString *)path callback:(RCTResponseSenderBlock) callb
882 352
     }
883 353
     NSError * error = nil;
884 354
     NSArray * files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error];
885
-
355
+    
886 356
     NSMutableArray * res = [[NSMutableArray alloc] init];
887 357
     if(isDir == YES) {
888 358
         for(NSString * p in files) {
889 359
             NSString * filePath = [NSString stringWithFormat:@"%@/%@", path, p];
890
-            [res addObject:[FetchBlobFS stat:filePath error:&error]];
360
+            [res addObject:[RNFetchBlobFS stat:filePath error:&error]];
891 361
         }
892 362
     }
893 363
     else {
894
-        [res addObject:[FetchBlobFS stat:path error:&error]];
364
+        [res addObject:[RNFetchBlobFS stat:path error:&error]];
895 365
     }
896 366
     
897 367
     if(error == nil)
@@ -924,21 +394,21 @@ RCT_EXPORT_METHOD(mv:(NSString *)path toPath:(NSString *)dest callback:(RCTRespo
924 394
 }
925 395
 
926 396
 RCT_EXPORT_METHOD(mkdir:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
927
-    if([FetchBlobFS exists:path]) {
397
+    if([RNFetchBlobFS exists:path]) {
928 398
         callback(@[@"mkdir failed, folder already exists"]);
929 399
         return;
930 400
     }
931 401
     else
932
-        [FetchBlobFS mkdir:path];
402
+        [RNFetchBlobFS mkdir:path];
933 403
     callback(@[[NSNull null]]);
934 404
 }
935 405
 
936 406
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
937 407
     
938 408
     callback(@[
939
-               [FetchBlobFS getDocumentDir],
940
-               [FetchBlobFS getCacheDir],
941
-            ]);
409
+               [RNFetchBlobFS getDocumentDir],
410
+               [RNFetchBlobFS getCacheDir],
411
+               ]);
942 412
 }
943 413
 
944 414
 #pragma mark RNFetchBlob private methods

+ 34
- 0
src/ios/RNFetchBlobConst.h View File

@@ -0,0 +1,34 @@
1
+//
2
+//  RNFetchBlobConst.h
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/6/6.
6
+//  Copyright © 2016年 suzuri04x2. All rights reserved.
7
+//
8
+
9
+#ifndef RNFetchBlobConst_h
10
+#define RNFetchBlobConst_h
11
+
12
+#import <Foundation/Foundation.h>
13
+
14
+// lib event
15
+extern NSString *const MSG_EVENT;
16
+extern NSString *const MSG_EVENT_LOG;
17
+extern NSString *const MSG_EVENT_WARN;
18
+extern NSString *const MSG_EVENT_ERROR;
19
+extern NSString *const FILE_PREFIX;
20
+
21
+// config
22
+extern NSString *const CONFIG_USE_TEMP;
23
+extern NSString *const CONFIG_FILE_PATH;
24
+extern NSString *const CONFIG_FILE_EXT;
25
+
26
+// fs events
27
+extern NSString *const FS_EVENT_DATA;
28
+extern NSString *const FS_EVENT_END;
29
+extern NSString *const FS_EVENT_WARN;
30
+extern NSString *const FS_EVENT_ERROR;
31
+
32
+
33
+
34
+#endif /* RNFetchBlobConst_h */

+ 24
- 0
src/ios/RNFetchBlobConst.m View File

@@ -0,0 +1,24 @@
1
+//
2
+//  RNFetchBlobConst.m
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/6/6.
6
+//  Copyright © 2016 wkh237. All rights reserved.
7
+//
8
+#import "RNFetchBlobConst.h"
9
+
10
+extern NSString *const FILE_PREFIX = @"RNFetchBlob-file://";
11
+
12
+// fetch configs
13
+extern NSString *const CONFIG_USE_TEMP = @"fileCache";
14
+extern NSString *const CONFIG_FILE_PATH = @"path";
15
+extern NSString *const CONFIG_FILE_EXT = @"appendExt";
16
+
17
+extern NSString *const MSG_EVENT = @"RNFetchBlobMessage";
18
+extern NSString *const MSG_EVENT_LOG = @"log";
19
+extern NSString *const MSG_EVENT_WARN = @"warn";
20
+extern NSString *const MSG_EVENT_ERROR = @"error";
21
+extern NSString *const FS_EVENT_DATA = @"data";
22
+extern NSString *const FS_EVENT_END = @"end";
23
+extern NSString *const FS_EVENT_WARN = @"warn";
24
+extern NSString *const FS_EVENT_ERROR = @"error";

+ 72
- 0
src/ios/RNFetchBlobFS.h View File

@@ -0,0 +1,72 @@
1
+//
2
+//  RNFetchBlobFS.h
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/6/6.
6
+//  Copyright © 2016年 suzuri04x2. All rights reserved.
7
+//
8
+
9
+#ifndef RNFetchBlobFS_h
10
+#define RNFetchBlobFS_h
11
+
12
+#import <Foundation/Foundation.h>
13
+#import "RCTBridgeModule.h"
14
+
15
+@interface RNFetchBlobFS : NSObject <NSStreamDelegate>  {
16
+    NSOutputStream * outStream;
17
+    NSInputStream * inStream;
18
+    RCTResponseSenderBlock callback;
19
+    RCTBridge * bridge;
20
+    Boolean isOpen;
21
+    NSString * encoding;
22
+    int bufferSize;
23
+    BOOL appendData;
24
+    NSString * taskId;
25
+    NSString * path;
26
+    NSString * streamId;
27
+}
28
+
29
+@property (nonatomic) NSOutputStream * outStream;
30
+@property (nonatomic) NSInputStream * inStream;
31
+@property (nonatomic) RCTResponseSenderBlock callback;
32
+@property (nonatomic) RCTBridge * bridge;
33
+@property (nonatomic) NSString * encoding;
34
+@property (nonatomic) NSString * taskId;
35
+@property (nonatomic) NSString * path;
36
+@property (nonatomic) int bufferSize;
37
+@property (nonatomic) NSString * streamId;
38
+@property (nonatomic) BOOL appendData;
39
+
40
+// get dirs
41
++ (NSString *) getTempPath;
42
++ (NSString *) getCacheDir;
43
++ (NSString *) getDocumentDir;
44
++ (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext;
45
+
46
+// fs methods
47
++ (RNFetchBlobFS *) getFileStreams;
48
++ (BOOL) mkdir:(NSString *) path;
49
++ (NSDictionary *) stat:(NSString *) path error:(NSError **) error;
50
++ (BOOL) exists:(NSString *) path;
51
+
52
+// constructor
53
+- (id) init;
54
+- (id)initWithCallback:(RCTResponseSenderBlock)callback;
55
+- (id)initWithBridgeRef:(RCTBridge *)bridgeRef;
56
+
57
+// file stream
58
+- (void) openWithDestination;
59
+- (void) openWithId;
60
+- (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
61
+
62
+// file stream write data
63
+- (void)write:(NSData *) chunk;
64
+- (void)writeEncodeChunk:(NSString *) chunk;
65
+- (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize;
66
+
67
+- (void) closeInStream;
68
+- (void) closeOutStream;
69
+
70
+@end
71
+
72
+#endif /* RNFetchBlobFS_h */

+ 363
- 0
src/ios/RNFetchBlobFS.m View File

@@ -0,0 +1,363 @@
1
+//
2
+//  RNFetchBlobFS.m
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/6/6.
6
+//  Copyright © 2016年 suzuri04x2. All rights reserved.
7
+//
8
+
9
+
10
+#import "RCTConvert.h"
11
+#import "RCTLog.h"
12
+#import <Foundation/Foundation.h>
13
+#import "RCTBridge.h"
14
+#import "RCTEventDispatcher.h"
15
+#import "RNFetchBlobFS.h"
16
+#import "RNFetchBlobConst.h"
17
+
18
+NSMutableDictionary *fileStreams = nil;
19
+
20
+////////////////////////////////////////
21
+//
22
+//  File system access methods
23
+//
24
+////////////////////////////////////////
25
+
26
+@implementation RNFetchBlobFS
27
+
28
+
29
+@synthesize outStream;
30
+@synthesize inStream;
31
+@synthesize encoding;
32
+@synthesize callback;
33
+@synthesize taskId;
34
+@synthesize path;
35
+@synthesize appendData;
36
+@synthesize bufferSize;
37
+
38
+// static member getter
39
++ (NSArray *) getFileStreams {
40
+    
41
+    if(fileStreams == nil)
42
+        fileStreams = [[NSMutableDictionary alloc] init];
43
+    return fileStreams;
44
+}
45
+
46
++(void) setFileStream:(RNFetchBlobFS *) instance withId:(NSString *) uuid {
47
+    if(fileStreams == nil)
48
+        fileStreams = [[NSMutableDictionary alloc] init];
49
+    [fileStreams setValue:instance forKey:uuid];
50
+}
51
+
52
++ (NSString *) getCacheDir {
53
+    return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
54
+}
55
+
56
++ (NSString *) getDocumentDir {
57
+    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
58
+}
59
+
60
++ (NSString *) getMusicDir {
61
+    return [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) firstObject];
62
+}
63
+
64
++ (NSString *) getMovieDir {
65
+    return [NSSearchPathForDirectoriesInDomains(NSMoviesDirectory, NSUserDomainMask, YES) firstObject];
66
+}
67
+
68
++ (NSString *) getPictureDir {
69
+    return [NSSearchPathForDirectoriesInDomains(NSPicturesDirectory, NSUserDomainMask, YES) firstObject];
70
+}
71
+
72
+
73
++ (NSString *) getTempPath {
74
+    
75
+    return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingString:@"/RNFetchBlob_tmp"];
76
+}
77
+
78
++ (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext {
79
+    
80
+    NSString * documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
81
+    NSString * filename = [NSString stringWithFormat:@"/RNFetchBlob_tmp/RNFetchBlobTmp_%@", taskId];
82
+    if(ext != nil)
83
+        filename = [filename stringByAppendingString: [NSString stringWithFormat:@".%@", ext]];
84
+    NSString * tempPath = [documentDir stringByAppendingString: filename];
85
+    return tempPath;
86
+}
87
+
88
++ (BOOL) mkdir:(NSString *) path {
89
+    BOOL isDir;
90
+    NSError * err = nil;
91
+    // if temp folder not exists, create one
92
+    if(![[NSFileManager defaultManager] fileExistsAtPath: path isDirectory:&isDir]) {
93
+        [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&err];
94
+    }
95
+    return err == nil;
96
+}
97
+
98
++ (NSDictionary *) stat:(NSString *) path error:(NSError **) error {
99
+    NSMutableDictionary *stat = [[NSMutableDictionary alloc] init];
100
+    BOOL isDir = NO;
101
+    NSFileManager * fm = [NSFileManager defaultManager];
102
+    if([fm fileExistsAtPath:path isDirectory:&isDir] == NO) {
103
+        return nil;
104
+    }
105
+    NSDictionary * info = [fm attributesOfItemAtPath:path error:&error];
106
+    NSString * size = [NSString stringWithFormat:@"%d", [info fileSize]];
107
+    NSString * filename = [path lastPathComponent];
108
+    NSDate * lastModified;
109
+    [[NSURL fileURLWithPath:path] getResourceValue:&lastModified forKey:NSURLContentModificationDateKey error:&error];
110
+    return @{
111
+             @"size" : size,
112
+             @"filename" : filename,
113
+             @"path" : path,
114
+             @"lastModified" : [NSString stringWithFormat:@"%d", [lastModified timeIntervalSince1970]],
115
+             @"type" : isDir ? @"directory" : @"file"
116
+             };
117
+}
118
+
119
++ (BOOL) exists:(NSString *) path {
120
+    return [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:NULL];
121
+}
122
+
123
+- (id)init {
124
+    self = [super init];
125
+    return self;
126
+}
127
+
128
+- (id)initWithCallback:(RCTResponseSenderBlock)callback {
129
+    self = [super init];
130
+    self.callback = callback;
131
+    return self;
132
+}
133
+
134
+- (id)initWithBridgeRef:(RCTBridge *)bridgeRef {
135
+    self = [super init];
136
+    self.bridge = bridgeRef;
137
+    return self;
138
+}
139
+
140
+// Create file stream for write data
141
+- (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append {
142
+    self.outStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:append];
143
+    self.encoding = encode;
144
+    [self.outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
145
+    [self.outStream open];
146
+    NSString *uuid = [[NSUUID UUID] UUIDString];
147
+    self.streamId = uuid;
148
+    [RNFetchBlobFS setFileStream:self withId:uuid];
149
+    return uuid;
150
+}
151
+
152
+// Write file chunk into an opened stream
153
+- (void)writeEncodeChunk:(NSString *) chunk {
154
+    NSMutableData * decodedData = [NSData alloc];
155
+    if([[self.encoding lowercaseString] isEqualToString:@"base64"]) {
156
+        decodedData = [[NSData alloc] initWithBase64EncodedData:chunk options:0];
157
+    }
158
+    if([[self.encoding lowercaseString] isEqualToString:@"utf8"]) {
159
+        decodedData = [chunk dataUsingEncoding:NSUTF8StringEncoding];
160
+    }
161
+    else if([[self.encoding lowercaseString] isEqualToString:@"ascii"]) {
162
+        decodedData = [chunk dataUsingEncoding:NSASCIIStringEncoding];
163
+    }
164
+    NSUInteger left = [decodedData length];
165
+    NSUInteger nwr = 0;
166
+    do {
167
+        nwr = [self.outStream write:[decodedData bytes] maxLength:left];
168
+        if (-1 == nwr) break;
169
+        left -= nwr;
170
+    } while (left > 0);
171
+    if (left) {
172
+        NSLog(@"stream error: %@", [self.outStream streamError]);
173
+    }
174
+}
175
+
176
+// Write file chunk into an opened stream
177
+- (void)write:(NSData *) chunk {
178
+    NSUInteger left = [chunk length];
179
+    NSUInteger nwr = 0;
180
+    do {
181
+        nwr = [self.outStream write:[chunk bytes] maxLength:left];
182
+        if (-1 == nwr) break;
183
+        left -= nwr;
184
+    } while (left > 0);
185
+    if (left) {
186
+        NSLog(@"stream error: %@", [self.outStream streamError]);
187
+    }
188
+}
189
+
190
+// close file write stream
191
+- (void)closeOutStream {
192
+    if(self.outStream != nil) {
193
+        [self.outStream close];
194
+        self.outStream = nil;
195
+    }
196
+    
197
+}
198
+
199
+- (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize {
200
+    
201
+    self.inStream = [[NSInputStream alloc] initWithFileAtPath:path];
202
+    self.inStream.delegate = self;
203
+    self.encoding = encoding;
204
+    self.path = path;
205
+    self.bufferSize = bufferSize;
206
+    
207
+    // NSStream needs a runloop so let's create a run loop for it
208
+    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
209
+    // start NSStream is a runloop
210
+    dispatch_async(queue, ^ {
211
+        [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
212
+                            forMode:NSDefaultRunLoopMode];
213
+        [inStream open];
214
+        [[NSRunLoop currentRunLoop] run];
215
+        
216
+    });
217
+}
218
+
219
+// close file read stream
220
+- (void)closeInStream {
221
+    if(self.inStream != nil) {
222
+        [self.inStream close];
223
+        [self.inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
224
+        [[RNFetchBlobFS getFileStreams] setValue:nil forKey:self.streamId];
225
+        self.streamId = nil;
226
+    }
227
+    
228
+}
229
+
230
+void runOnMainQueueWithoutDeadlocking(void (^block)(void))
231
+{
232
+    if ([NSThread isMainThread])
233
+    {
234
+        block();
235
+    }
236
+    else
237
+    {
238
+        dispatch_sync(dispatch_get_main_queue(), block);
239
+    }
240
+}
241
+
242
+
243
+#pragma mark RNFetchBlobFS read stream delegate
244
+
245
+- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
246
+    
247
+    NSString * streamEventCode = [NSString stringWithFormat:@"RNFetchBlobStream+%@", self.path];
248
+    
249
+    switch(eventCode) {
250
+            
251
+            // write stream event
252
+        case NSStreamEventHasSpaceAvailable:
253
+        {
254
+            
255
+            
256
+        }
257
+            
258
+            // read stream incoming chunk
259
+        case NSStreamEventHasBytesAvailable:
260
+        {
261
+            NSMutableData * chunkData = [[NSMutableData data] init];
262
+            NSInteger chunkSize = 4096;
263
+            if([[self.encoding lowercaseString] isEqualToString:@"base64"])
264
+                chunkSize = 4095;
265
+            if(self.bufferSize > 0)
266
+                chunkSize = self.bufferSize;
267
+            uint8_t buf[chunkSize];
268
+            unsigned int len = 0;
269
+            
270
+            len = [(NSInputStream *)stream read:buf maxLength:chunkSize];
271
+            // still have data in stream
272
+            if(len) {
273
+                [chunkData appendBytes:(const void *)buf length:len];
274
+                // dispatch data event
275
+                NSString * encodedChunk = [NSString alloc];
276
+                if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
277
+                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
278
+                }
279
+                // when encoding is ASCII, send byte array data
280
+                else if ( [[self.encoding lowercaseString] isEqualToString:@"ascii"] ) {
281
+                    // RCTBridge only emits string data, so we have to create JSON byte array string
282
+                    NSString * asciiStr = @"[";
283
+                    if (chunkData.length > 0)
284
+                    {
285
+                        unsigned char *bytePtr = (unsigned char *)[chunkData bytes];
286
+                        NSInteger byteLen = chunkData.length/sizeof(uint8_t);
287
+                        for (int i = 0; i < byteLen; i++)
288
+                        {
289
+                            uint8_t * byteFromArray = chunkData.bytes;
290
+                            NSInteger val = bytePtr[i];
291
+                            if(i+1 < byteLen)
292
+                                asciiStr = [asciiStr stringByAppendingFormat:@"%d,", val];
293
+                            else
294
+                                asciiStr = [asciiStr stringByAppendingFormat:@"%d", val];
295
+                        }
296
+                    }
297
+                    asciiStr = [asciiStr stringByAppendingString:@"]"];
298
+                    [self.bridge.eventDispatcher
299
+                     sendDeviceEventWithName:streamEventCode
300
+                     body:@{
301
+                            @"event": FS_EVENT_DATA,
302
+                            @"detail": asciiStr
303
+                            }
304
+                     ];
305
+                    return;
306
+                }
307
+                // convert byte array to base64 data chunks
308
+                else if ( [[self.encoding lowercaseString] isEqualToString:@"base64"] ) {
309
+                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
310
+                }
311
+                // unknown encoding, send erro event
312
+                else {
313
+                    [self.bridge.eventDispatcher
314
+                     sendDeviceEventWithName:streamEventCode
315
+                     body:@{
316
+                            @"event": FS_EVENT_ERROR,
317
+                            @"detail": @"unrecognized encoding"
318
+                            }
319
+                     ];
320
+                    return;
321
+                }
322
+                
323
+                [self.bridge.eventDispatcher
324
+                 sendDeviceEventWithName:streamEventCode
325
+                 body:@{
326
+                        @"event": FS_EVENT_DATA,
327
+                        @"detail": encodedChunk
328
+                        }
329
+                 ];
330
+                
331
+            }
332
+            // end of stream
333
+            else {
334
+                [self.bridge.eventDispatcher
335
+                 sendDeviceEventWithName:streamEventCode
336
+                 body:@{
337
+                        @"event": FS_EVENT_END,
338
+                        @"detail": @""
339
+                        }
340
+                 ];
341
+            }
342
+            break;
343
+        }
344
+            
345
+            // stream error
346
+        case NSStreamEventErrorOccurred:
347
+        {
348
+            [self.bridge.eventDispatcher
349
+             sendDeviceEventWithName:streamEventCode
350
+             body:@{
351
+                    @"event": FS_EVENT_ERROR,
352
+                    @"detail": @"RNFetchBlob error when read file with stream, file may not exists"
353
+                    }
354
+             ];
355
+            break;
356
+        }
357
+            
358
+    }
359
+    
360
+}
361
+
362
+@end
363
+

+ 46
- 0
src/ios/RNFetchBlobResp.h View File

@@ -0,0 +1,46 @@
1
+//
2
+//  RNFetchBlobResp.h
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/6/6.
6
+//  Copyright © 2016年 suzuri04x2. All rights reserved.
7
+//
8
+
9
+#ifndef RNFetchBlobResp_h
10
+#define RNFetchBlobResp_h
11
+
12
+#import <Foundation/Foundation.h>
13
+#import "RCTBridgeModule.h"
14
+
15
+@interface FetchBlobUtils : NSObject  <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
16
+    
17
+    NSString * taskId;
18
+    int expectedBytes;
19
+    int receivedBytes;
20
+    NSMutableData * respData;
21
+    RCTResponseSenderBlock callback;
22
+    RCTBridge * bridge;
23
+    NSDictionary * options;
24
+    RNFetchBlobFS * fileStream;
25
+}
26
+@property (nonatomic) NSString * taskId;
27
+@property (nonatomic) int expectedBytes;
28
+@property (nonatomic) int receivedBytes;
29
+@property (nonatomic) NSMutableData * respData;
30
+@property (nonatomic) RCTResponseSenderBlock callback;
31
+@property (nonatomic) RCTBridge * bridge;
32
+@property (nonatomic) NSDictionary * options;
33
+@property (nonatomic) RNFetchBlobFS * fileStream;
34
+
35
+
36
+- (id) init;
37
+- (void) sendRequest;
38
+
39
++ (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers;
40
+- (void) sendRequest:(NSDictionary *)options bridge:(RCTBridge *)bridgeRef taskId:(NSString *)taskId withRequest:(NSURLRequest *)req callback:(RCTResponseSenderBlock) callback;
41
+
42
+
43
+@end
44
+
45
+
46
+#endif /* RNFetchBlobResp_h */

+ 188
- 0
src/ios/RNFetchBlobResp.m View File

@@ -0,0 +1,188 @@
1
+//
2
+//  RNFetchBlobResp.m
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/6/6.
6
+//  Copyright © 2016年 suzuri04x2. All rights reserved.
7
+//
8
+
9
+#import "RCTConvert.h"
10
+#import "RCTLog.h"
11
+#import <Foundation/Foundation.h>
12
+#import "RCTBridge.h"
13
+#import "RCTEventDispatcher.h"
14
+#import "RNFetchBlobFS.h"
15
+#import "RNFetchBlobResp.h"
16
+#import "RNFetchBlobConst.h"
17
+
18
+////////////////////////////////////////
19
+//
20
+//  HTTP request handler
21
+//
22
+////////////////////////////////////////
23
+
24
+@implementation FetchBlobUtils
25
+
26
+
27
+@synthesize taskId;
28
+@synthesize expectedBytes;
29
+@synthesize receivedBytes;
30
+@synthesize respData;
31
+@synthesize callback;
32
+@synthesize bridge;
33
+@synthesize options;
34
+
35
+
36
+// removing case from headers
37
++ (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers {
38
+    
39
+    NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init];
40
+    for(NSString * key in headers) {
41
+        [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]];
42
+    }
43
+    
44
+    return mheaders;
45
+}
46
+
47
+- (id)init {
48
+    self = [super init];
49
+    return self;
50
+}
51
+
52
+
53
+- (void) sendRequest:(NSDictionary *)options bridge:(RCTBridge *)bridgeRef taskId:(NSString *)taskId withRequest:(NSURLRequest *)req callback:(RCTResponseSenderBlock) callback {
54
+    self.taskId = taskId;
55
+    self.respData = [[NSMutableData alloc] initWithLength:0];
56
+    self.callback = callback;
57
+    self.bridge = bridgeRef;
58
+    self.expectedBytes = 0;
59
+    self.receivedBytes = 0;
60
+    self.options = options;
61
+    
62
+    NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
63
+    NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
64
+    
65
+    // open file stream for write
66
+    if( path != nil) {
67
+        self.fileStream = [[RNFetchBlobFS alloc]initWithCallback:self.callback];
68
+        [self.fileStream openWithPath:path encode:@"ascii" appendData:YES ];
69
+    }
70
+    else if ( [self.options valueForKey:CONFIG_USE_TEMP]!= nil ) {
71
+        self.fileStream = [[RNFetchBlobFS alloc]initWithCallback:self.callback];
72
+        [self.fileStream openWithPath:[RNFetchBlobFS getTempPath:taskId withExtension:ext] encode:@"ascii" appendData:YES];
73
+    }
74
+    
75
+    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:NO];
76
+    [conn scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
77
+    [conn start];
78
+    
79
+    if(!conn) {
80
+        callback(@[[NSString stringWithFormat:@"RNFetchBlob could not initialize connection"], [NSNull null]]);
81
+    }
82
+}
83
+
84
+
85
+#pragma mark NSURLConnection delegate methods
86
+
87
+
88
+- (void) connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response {
89
+    //    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
90
+    expectedBytes = [response expectedContentLength];
91
+}
92
+
93
+
94
+- (void) connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data {
95
+    receivedBytes += [data length];
96
+    
97
+    Boolean fileCache = [self.options valueForKey:CONFIG_USE_TEMP];
98
+    NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
99
+    if(path != nil) {
100
+        
101
+        [self.fileStream write:data];
102
+    }
103
+    // write to tmp file
104
+    else if( fileCache != nil) {
105
+        NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
106
+        [self.fileStream write:data];
107
+    }
108
+    // cache data in memory
109
+    else {
110
+        [respData appendData:data];
111
+    }
112
+    
113
+    [self.bridge.eventDispatcher
114
+     sendDeviceEventWithName:@"RNFetchBlobProgress"
115
+     body:@{
116
+            @"taskId": taskId,
117
+            @"written": [NSString stringWithFormat:@"%d", receivedBytes],
118
+            @"total": [NSString stringWithFormat:@"%d", expectedBytes]
119
+            }
120
+     ];
121
+}
122
+
123
+- (void) connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
124
+    
125
+    expectedBytes = totalBytesExpectedToWrite;
126
+    receivedBytes += totalBytesWritten;
127
+    [self.bridge.eventDispatcher
128
+     sendDeviceEventWithName:@"RNFetchBlobProgress"
129
+     body:@{
130
+            @"taskId": taskId,
131
+            @"written": [NSString stringWithFormat:@"%d", receivedBytes],
132
+            @"total": [NSString stringWithFormat:@"%d", expectedBytes]
133
+            }
134
+     ];
135
+    
136
+}
137
+
138
+- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
139
+    
140
+    //    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
141
+    
142
+    [self.fileStream closeInStream];
143
+    [self.fileStream closeOutStream];
144
+    
145
+    callback(@[[error localizedDescription], [NSNull null]]);
146
+}
147
+
148
+- (NSCachedURLResponse *) connection:(NSURLConnection *)connection willCacheResponse: (NSCachedURLResponse *)cachedResponse {
149
+    return nil;
150
+}
151
+
152
+
153
+// handle 301 and 302 responses
154
+- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:response {
155
+    return request;
156
+}
157
+
158
+// request complete
159
+- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
160
+    
161
+    NSData * data;
162
+    if(respData != nil)
163
+        data = [NSData dataWithData:respData];
164
+    else
165
+        data = [[NSData alloc] init];
166
+    
167
+    NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
168
+    NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
169
+    Boolean useCache = [self.options valueForKey:CONFIG_USE_TEMP];
170
+    
171
+    [self.fileStream closeInStream];
172
+    
173
+    // if fileCache is true or file path is given, return a path
174
+    if( path != nil ) {
175
+        callback(@[[NSNull null], path]);
176
+    }
177
+    // when fileCache option is set but no path specified, save to tmp path
178
+    else if( [self.options valueForKey:CONFIG_USE_TEMP] != nil) {
179
+        NSString * tmpPath = [RNFetchBlobFS getTempPath:taskId withExtension:ext];
180
+        callback(@[[NSNull null], tmpPath]);
181
+    }
182
+    // otherwise return base64 string
183
+    else {
184
+        callback(@[[NSNull null], [data base64EncodedStringWithOptions:0]]);
185
+    }
186
+}
187
+
188
+@end