|
@@ -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
|