Kaynağa Gözat

#2 Add ios implementation wip commit

Ben Hsieh 8 yıl önce
ebeveyn
işleme
b6b8143c0b
3 değiştirilmiş dosya ile 280 ekleme ve 38 silme
  1. 6
    4
      src/index.js
  2. 37
    3
      src/ios/RNFetchBlob/RNFetchBlob.h
  3. 237
    31
      src/ios/RNFetchBlob/RNFetchBlob.m

+ 6
- 4
src/index.js Dosyayı Görüntüle

@@ -78,7 +78,7 @@ const fetch = function(...args:any) {
78 78
         let respType = 'base64'
79 79
         if(options.fileCache || options.path)
80 80
           respType = 'path'
81
-        resolve(new FetchBlobResponse(taskId, respType,...data))
81
+        resolve(new FetchBlobResponse(taskId, options.path, respType,...data))
82 82
       }
83 83
 
84 84
     })
@@ -100,6 +100,7 @@ const fetch = function(...args:any) {
100 100
 class FetchBlobResponse {
101 101
 
102 102
   taskId : string;
103
+  path : string;
103 104
   type : 'base64' | 'path';
104 105
   data : any;
105 106
   blob : (contentType:string, sliceSize:number) => null;
@@ -112,8 +113,9 @@ class FetchBlobResponse {
112 113
     fn:(event : 'data' | 'end', chunk:any) => void
113 114
   ) => void;
114 115
 
115
-  constructor(taskId:string, type:'base64' | 'path',data:any) {
116
+  constructor(taskId:string, path:string, type:'base64' | 'path',data:any) {
116 117
     this.data = data
118
+    this.path = path
117 119
     this.taskId = taskId
118 120
     /**
119 121
      * Convert result to javascript Blob object.
@@ -151,7 +153,7 @@ class FetchBlobResponse {
151 153
      * @return {void}
152 154
      */
153 155
     this.flush = () => {
154
-      RNFetchBlob.flush(this.taskId)
156
+      RNFetchBlob.flush(this.taskId, this.path)
155 157
     }
156 158
 
157 159
     /**
@@ -170,7 +172,7 @@ class FetchBlobResponse {
170 172
           subscription()
171 173
       })
172 174
 
173
-      RNFetchBlob.readStream(this.taskId, encode)
175
+      RNFetchBlob.readStream(this.taskId, this.path, encode)
174 176
     }
175 177
 
176 178
   }

+ 37
- 3
src/ios/RNFetchBlob/RNFetchBlob.h Dosyayı Görüntüle

@@ -9,18 +9,47 @@
9 9
 #import <Foundation/Foundation.h>
10 10
 #import "RCTBridgeModule.h"
11 11
 
12
-@interface RNFetchBlob : NSObject <RCTBridgeModule> 
12
+@interface FetchBlobFS : NSObject <NSStreamDelegate>  {
13
+    NSOutputStream * outStream;
14
+    NSInputStream * inStream;
15
+    RCTResponseSenderBlock callback;
16
+    RCTBridge * bridge;
17
+    Boolean isOpen;
18
+    NSString * encoding;
19
+    NSString * taskId;
20
+    NSString * path;
21
+}
22
+
23
+@property (nonatomic) NSOutputStream * outStream;
24
+@property (nonatomic) NSInputStream * inStream;
25
+@property (nonatomic) RCTResponseSenderBlock callback;
26
+@property (nonatomic) RCTBridge * bridge;
27
+@property (nonatomic) NSString * encoding;
28
+@property (nonatomic) NSString * taskId;
29
+@property (nonatomic) NSString * path;
30
+
31
++ (NSString *) getTempPath;
32
+- (void) initWithCallback;
33
+- (void) initWithBridgeRef;
34
+- (void) openWithDestination;
35
+- (void) openWithId;
36
+- (void) write;
37
+- (void) read;
38
+- (void) closeInStream;
39
+- (void) closeOutStream;
13 40
 
14 41
 @end
15 42
 
16 43
 @interface FetchBlobUtils : NSObject  <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
17
-
44
+    
18 45
     NSString * taskId;
19 46
     int expectedBytes;
20 47
     int receivedBytes;
21 48
     NSMutableData * respData;
22 49
     RCTResponseSenderBlock callback;
23 50
     RCTBridge * bridge;
51
+    NSDictionary * options;
52
+    FetchBlobFS * fileStream;
24 53
 }
25 54
 @property (nonatomic) NSString * taskId;
26 55
 @property (nonatomic) int expectedBytes;
@@ -28,7 +57,8 @@
28 57
 @property (nonatomic) NSMutableData * respData;
29 58
 @property (nonatomic) RCTResponseSenderBlock callback;
30 59
 @property (nonatomic) RCTBridge * bridge;
31
-
60
+@property (nonatomic) NSDictionary * options;
61
+@property (nonatomic) FetchBlobFS * fileStream;
32 62
 
33 63
 - (id) init;
34 64
 - (id) delegate;
@@ -40,4 +70,8 @@
40 70
 @end
41 71
 
42 72
 
73
+@interface RNFetchBlob : NSObject <RCTBridgeModule>
74
+
75
+@end
76
+
43 77
 #endif /* RNFetchBlob_h */

+ 237
- 31
src/ios/RNFetchBlob/RNFetchBlob.m Dosyayı Görüntüle

@@ -14,7 +14,172 @@
14 14
 
15 15
 ////////////////////////////////////////
16 16
 //
17
-//  Util functions
17
+//  File system access methods
18
+//
19
+////////////////////////////////////////
20
+
21
+@implementation FetchBlobFS
22
+
23
+@synthesize outStream;
24
+@synthesize inStream;
25
+@synthesize encoding;
26
+@synthesize callback;
27
+@synthesize taskId;
28
+@synthesize path;
29
+
30
++ (NSString *) getTempPath:(NSString*)taskId {
31
+
32
+    NSString * documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
33
+    NSString * filename = [NSString stringWithFormat:@"RNFetchBlobTmp_%s", taskId];
34
+    NSString * tempPath = [documentDir stringByAppendingString: filename];
35
+    return tempPath;
36
+}
37
+
38
+- (id)initWithCallback:(RCTResponseSenderBlock)callback {
39
+    self = [super init];
40
+    self.callback = callback;
41
+    return self;
42
+}
43
+
44
+- (id)initWithBridgeRef:(RCTBridge *)bridgeRef {
45
+    self = [super init];
46
+    self.callback = callback;
47
+    return self;
48
+}
49
+
50
+- (void)openWithPath:(NSString *)destPath {
51
+    self.outStream = [[NSOutputStream alloc]init];
52
+    [self.outStream initToFileAtPath:destPath append:NO];
53
+}
54
+
55
+
56
+- (void)openWithId:(NSString *)taskId {
57
+    
58
+    NSString * tmpPath = [[self class ]getTempPath: taskId];
59
+    // create a file stream
60
+    [self openWithPath:tmpPath];
61
+    
62
+}
63
+
64
+// Write file chunk into an opened stream
65
+- (void)write:(NSString *) chunk {
66
+    [self.outStream write:[chunk cStringUsingEncoding:NSASCIIStringEncoding] maxLength:chunk.length];
67
+}
68
+
69
+- (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding {
70
+    self.inStream = [[NSInputStream alloc]init];
71
+    self.encoding = encoding;
72
+    [self.inStream setDelegate:self];
73
+    
74
+}
75
+
76
+- (void)readWithTaskId:(NSString *)taskId withPath:(NSString *)path useEncoding:(NSString *)encoding {
77
+    self.taskId = taskId;
78
+    self.path = path;
79
+    if(path == nil)
80
+        [self readWithPath:[[self class]getTempPath:taskId] useEncoding:encoding];
81
+    else
82
+        [self readWithPath:path useEncoding:encoding];
83
+}
84
+
85
+// close file stream
86
+- (void)closeOutStream {
87
+    if(self.outStream != nil) {
88
+        [self.outStream close];
89
+        self.outStream = nil;
90
+    }
91
+
92
+}
93
+
94
+- (void)closeInStream {
95
+    if(self.inStream != nil) {
96
+        [self.inStream close];
97
+        [self.inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
98
+    }
99
+    
100
+}
101
+
102
+- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
103
+
104
+    switch(eventCode) {
105
+    
106
+        case NSStreamEventHasBytesAvailable:
107
+        {
108
+            
109
+            
110
+            NSMutableData * chunkData = [[NSMutableData data] init];
111
+            
112
+            uint8_t buf[1024];
113
+            unsigned int len = 0;
114
+            len = [(NSInputStream *)stream read:buf maxLength:1024];
115
+            // still have data in stream
116
+            if(len) {
117
+                [chunkData appendBytes:(const void *)buf length:len];
118
+                // TODO : read file progress ?
119
+//                [bytesRead setIntValue:[bytesRead intValue]+len];
120
+                
121
+                // dispatch data event
122
+                NSString * encodedChunk = [NSString alloc];
123
+                if( [self.encoding caseInsensitiveCompare:@"utf8"] ) {
124
+                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
125
+                }
126
+                else if ( [self.encoding caseInsensitiveCompare:@"ascii"] ) {
127
+                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSASCIIStringEncoding];
128
+                }
129
+                else if ( [self.encoding caseInsensitiveCompare:@"base64"] ) {
130
+                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
131
+                }
132
+                else {
133
+                    [self.bridge.eventDispatcher
134
+                     sendAppEventWithName: [NSString stringWithFormat:@"RNFetchBlobStream%s", self.taskId]
135
+                     body:@{
136
+                            @"event": @"error",
137
+                            @"detail": @"unrecognized encoding"
138
+                        }
139
+                     ];
140
+                    return;
141
+                }
142
+                [self.bridge.eventDispatcher
143
+                 sendAppEventWithName: [NSString stringWithFormat:@"RNFetchBlobStream%s", self.taskId]
144
+                 body:@{
145
+                        @"event": @"data",
146
+                        @"detail": encodedChunk
147
+                    }
148
+                 ];
149
+            }
150
+            // end of stream
151
+            else {
152
+                [self.bridge.eventDispatcher
153
+                sendAppEventWithName: [NSString stringWithFormat:@"RNFetchBlobStream%s", self.taskId]
154
+                body:@{
155
+                       @"event": @"end",
156
+                       @"detail": @""
157
+                    }
158
+                ];
159
+            }
160
+            break;
161
+        }
162
+        case NSStreamEventErrorOccurred:
163
+        {
164
+            [self.bridge.eventDispatcher
165
+             sendAppEventWithName: [NSString stringWithFormat:@"RNFetchBlobStream%s", self.taskId]
166
+             body:@{
167
+                    @"event": @"error",
168
+                    @"detail": @"error when read file with stream"
169
+                    }
170
+             ];
171
+            break;
172
+        }
173
+    
174
+    }
175
+
176
+}
177
+
178
+@end
179
+
180
+////////////////////////////////////////
181
+//
182
+//  HTTP request handler
18 183
 //
19 184
 ////////////////////////////////////////
20 185
 
@@ -27,9 +192,10 @@
27 192
 @synthesize respData;
28 193
 @synthesize callback;
29 194
 @synthesize bridge;
195
+@synthesize options;
30 196
 
31 197
 
32
-// removing case of headers
198
+// removing case from headers
33 199
 + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers {
34 200
     
35 201
     NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init];
@@ -45,18 +211,28 @@
45 211
     return self;
46 212
 }
47 213
 
48
-- (id)delegate:(id)delegate {
49
-    return delegate;
50
-}
51 214
 
52
-- (void) sendRequest:(RCTBridge *)bridgeRef taskId:(NSString *)taskId withRequest:(NSURLRequest *)req callback:(RCTResponseSenderBlock) callback {
215
+- (void) sendRequest:(NSDictionary *)options bridge:(RCTBridge *)bridgeRef taskId:(NSString *)taskId withRequest:(NSURLRequest *)req callback:(RCTResponseSenderBlock) callback {
53 216
     self.taskId = taskId;
54 217
     self.respData = [[NSMutableData alloc] initWithLength:0];
55 218
     self.callback = callback;
56 219
     self.bridge = bridgeRef;
57 220
     self.expectedBytes = 0;
58 221
     self.receivedBytes = 0;
59
-    // Call long-running code on background thread
222
+    self.options = options;
223
+    
224
+    NSString * path = [self.options valueForKey:@"path"];
225
+    
226
+    // open file stream for write
227
+    if( path != nil) {
228
+        self.fileStream = [[FetchBlobFS alloc]initWithCallback:self.callback];
229
+        [self.fileStream openWithPath:path];
230
+    }
231
+    else if ( [self.options valueForKey:@"fileCache"] == YES ) {
232
+        self.fileStream = [[FetchBlobFS alloc]initWithCallback:self.callback];
233
+        [self.fileStream openWithId:taskId];
234
+    }
235
+    
60 236
     NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:NO];
61 237
     [conn scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
62 238
     [conn start];
@@ -75,9 +251,21 @@
75 251
     expectedBytes = [response expectedContentLength];
76 252
 }
77 253
 
254
+
78 255
 - (void) connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data {
79 256
     receivedBytes += data.length;
80
-    [respData appendData:data];
257
+    
258
+    Boolean fileCache = [self.options valueForKey:@"fileCache"];
259
+    NSString * path = [self.options valueForKey:@"path"];
260
+    
261
+    // write to tmp file
262
+    if( fileCache == YES || path != nil ) {
263
+        [self.fileStream write:data];
264
+    }
265
+    // cache data in memory
266
+    else {
267
+        [respData appendData:data];
268
+    }
81 269
     
82 270
     [self.bridge.eventDispatcher
83 271
         sendAppEventWithName:@"RNFetchBlobProgress"
@@ -105,7 +293,12 @@
105 293
 }
106 294
 
107 295
 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
296
+    
108 297
     [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
298
+    
299
+    [self.fileStream closeInStream];
300
+    [self.fileStream closeOutStream];
301
+    
109 302
     callback(@[[error localizedDescription], [NSNull null]]);
110 303
 }
111 304
 
@@ -116,7 +309,6 @@
116 309
 
117 310
 // handle 301 and 302 responses
118 311
 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:response {
119
-    
120 312
     return request;
121 313
 }
122 314
 
@@ -129,7 +321,23 @@
129 321
     else
130 322
         data = [[NSData alloc] init];
131 323
     
132
-    callback(@[[NSNull null], [data base64EncodedStringWithOptions:0]]);
324
+    NSString * path = [NSString stringWithString:[self.options valueForKey:@"path"]];
325
+    
326
+    [self.fileStream closeInStream];
327
+    
328
+    // if fileCache is true or file path is given, return a path
329
+    if( path != nil ) {
330
+        callback(@[[NSNull null], path]);
331
+    }
332
+    // when fileCache option is set but no path specified, save to tmp path
333
+    else if( [self.options valueForKey:@"fileCache"] == YES || path != nil ) {
334
+        NSString * tmpPath = [FetchBlobFS getTempPath:taskId];
335
+        callback(@[[NSNull null], tmpPath]);
336
+    }
337
+    // otherwise return base64 string
338
+    else {
339
+        callback(@[[NSNull null], [data base64EncodedStringWithOptions:0]]);
340
+    }
133 341
 }
134 342
 
135 343
 @end
@@ -141,6 +349,8 @@
141 349
 //
142 350
 ////////////////////////////////////////
143 351
 
352
+#pragma mark RNFetchBlob exported methods
353
+
144 354
 @implementation RNFetchBlob
145 355
 
146 356
 @synthesize bridge = _bridge;
@@ -148,7 +358,7 @@
148 358
 RCT_EXPORT_MODULE();
149 359
 
150 360
 // Fetch blob data request
151
-RCT_EXPORT_METHOD(fetchBlobForm:(NSString *)taskId method:(NSString *)method url:(NSString *)url headers:(NSDictionary *)headers form:(NSArray *)form callback:(RCTResponseSenderBlock)callback)
361
+RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options taskId:(NSString *)taskId method:(NSString *)method url:(NSString *)url headers:(NSDictionary *)headers form:(NSArray *)form callback:(RCTResponseSenderBlock)callback)
152 362
 {
153 363
     
154 364
     // send request
@@ -206,20 +416,15 @@ RCT_EXPORT_METHOD(fetchBlobForm:(NSString *)taskId method:(NSString *)method url
206 416
     [request setHTTPMethod: method];
207 417
     [request setAllHTTPHeaderFields:mheaders];
208 418
     
209
-    [[[FetchBlobUtils alloc] init] sendRequest:self.bridge taskId:taskId withRequest:request callback:callback];
210 419
     
211
-    // create thread for http request
212
-//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
213
-//    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
214
-//        
215
-//        [FetchBlobUtils onBlobResponse:response withData:data withError: connectionError withCallback: callback];
216
-//        
217
-//    }];
420
+    // send HTTP request
421
+    FetchBlobUtils * utils = [[FetchBlobUtils alloc] init];
422
+    [utils sendRequest:options bridge:self.bridge taskId:taskId withRequest:request callback:callback];
218 423
     
219 424
 }
220 425
 
221 426
 // Fetch blob data request
222
-RCT_EXPORT_METHOD(fetchBlob:(NSString *)taskId method:(NSString *)method url:(NSString *)url headers:(NSDictionary *)headers body:(NSString *)body callback:(RCTResponseSenderBlock)callback)
427
+RCT_EXPORT_METHOD(fetchBlob:(NSDictionary *)options taskId:(NSString *)taskId method:(NSString *)method url:(NSString *)url headers:(NSDictionary *)headers body:(NSString *)body callback:(RCTResponseSenderBlock)callback)
223 428
 {
224 429
     // send request
225 430
     NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
@@ -244,18 +449,19 @@ RCT_EXPORT_METHOD(fetchBlob:(NSString *)taskId method:(NSString *)method url:(NS
244 449
     [request setHTTPMethod: method];
245 450
     [request setAllHTTPHeaderFields:mheaders];
246 451
     
247
-    [[[FetchBlobUtils alloc] init] sendRequest:self.bridge taskId:taskId withRequest:request callback:callback];
248
-    
249
-    // create thread for http request
250
-//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
251
-//    
252
-//    
253
-//    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
254
-//        
255
-//        [FetchBlobUtils onBlobResponse:response withData:data withError: connectionError withCallback: callback];
256
-//        
257
-//    }];
452
+    // send HTTP request
453
+    FetchBlobUtils * utils = [[FetchBlobUtils alloc] init];
454
+    [utils sendRequest:options bridge:self.bridge taskId:taskId withRequest:request callback:callback];
258 455
     
259 456
 }
260 457
 
458
+RCT_EXPORT_METHOD(readStream:(NSString *)taskId withPath:(NSString *)path withEncoding:(NSString *)encoding) {
459
+    FetchBlobFS *fileStream = [[FetchBlobFS alloc] initWithBridgeRef:self.bridge];
460
+    [fileStream readWithTaskId:taskId withPath:path useEncoding:encoding];
461
+}
462
+
463
+RCT_EXPORT_METHOD(flush:(NSString *)taskId withPath:(NSString *)path) {
464
+    // TODO : remove file
465
+}
466
+
261 467
 @end