Browse Source

#2 ios download to file API complete

Ben Hsieh 8 years ago
parent
commit
7356e6e116
2 changed files with 131 additions and 32 deletions
  1. 65
    10
      src/index.js
  2. 66
    22
      src/ios/RNFetchBlob/RNFetchBlob.m

+ 65
- 10
src/index.js View File

@@ -11,9 +11,37 @@ import {
11 11
   Platform,
12 12
 } from 'react-native'
13 13
 
14
+type RNFetchBlobNative = {
15
+  fetchBlob : (
16
+    options:fetchConfig,
17
+    taskId:string,
18
+    method:string,
19
+    url:string,
20
+    headers:any,
21
+    body:any,
22
+    callback:(err:any, ...data:any) => void
23
+  ) => void,
24
+  fetchBlobForm : (
25
+    options:fetchConfig,
26
+    taskId:string,
27
+    method:string,
28
+    url:string,
29
+    headers:any,
30
+    form:Array<any>,
31
+    callback:(err:any, ...data:any) => void
32
+  ) => void,
33
+  readStream : (
34
+    taskId:string,
35
+    path:string,
36
+    encode: 'utf8' | 'ascii' | 'base64'
37
+  ) => void,
38
+  getEnvironmentDirs : (dirs:any) => void,
39
+  flush : () => void
40
+};
41
+
14 42
 import base64 from 'base-64'
15 43
 const emitter = (Platform.OS === 'android' ? DeviceEventEmitter : NativeAppEventEmitter)
16
-const RNFetchBlob = NativeModules.RNFetchBlob
44
+const RNFetchBlob:RNFetchBlobNative = NativeModules.RNFetchBlob
17 45
 
18 46
 emitter.addListener("RNFetchBlobMessage", (e) => {
19 47
 
@@ -41,14 +69,29 @@ if(!RNFetchBlob || !RNFetchBlob.fetchBlobForm || !RNFetchBlob.fetchBlob) {
41 69
 type fetchConfig = {
42 70
   fileCache : bool,
43 71
   path : string,
72
+};
73
+
74
+function getSystemDirs() {
75
+  return new Promise((resolve, reject) => {
76
+    try {
77
+      RNFetchBlob.getEnvironmentDirs((...dirs) => {
78
+        console.log('##',...dirs)
79
+        let [PictureDir, MovieDir, DocumentDir, CacheDir] = [...dirs]
80
+        resolve({PictureDir, MovieDir, DocumentDir, CacheDir})
81
+      })
82
+    } catch(err) {
83
+      reject(err)
84
+    }
85
+  })
86
+
44 87
 }
45 88
 
46
-const config = function(options) {
89
+function config (options:fetchConfig) {
47 90
   return { fetch : fetch.bind(options) }
48 91
 }
49 92
 
50 93
 // Promise wrapper function
51
-const fetch = function(...args:any) {
94
+function fetch(...args:any) {
52 95
 
53 96
   let options = this || {}
54 97
 
@@ -76,9 +119,9 @@ const fetch = function(...args:any) {
76 119
         reject(new Error(err, ...data))
77 120
       else {
78 121
         let respType = 'base64'
79
-        if(options.fileCache || options.path)
122
+        if(options.path || options.fileCache)
80 123
           respType = 'path'
81
-        resolve(new FetchBlobResponse(taskId, options.path, respType,...data))
124
+        resolve(new FetchBlobResponse(taskId, respType, ...data))
82 125
       }
83 126
 
84 127
     })
@@ -100,7 +143,7 @@ const fetch = function(...args:any) {
100 143
 class FetchBlobResponse {
101 144
 
102 145
   taskId : string;
103
-  path : string;
146
+  path : () => string | null;
104 147
   type : 'base64' | 'path';
105 148
   data : any;
106 149
   blob : (contentType:string, sliceSize:number) => null;
@@ -113,10 +156,10 @@ class FetchBlobResponse {
113 156
     fn:(event : 'data' | 'end', chunk:any) => void
114 157
   ) => void;
115 158
 
116
-  constructor(taskId:string, path:string, type:'base64' | 'path',data:any) {
159
+  constructor(taskId:string, type:'base64' | 'path', data:any) {
117 160
     this.data = data
118
-    this.path = path
119 161
     this.taskId = taskId
162
+    this.type = type
120 163
     /**
121 164
      * Convert result to javascript Blob object.
122 165
      * @param  {string} contentType MIME type of the blob object.
@@ -156,6 +199,12 @@ class FetchBlobResponse {
156 199
       RNFetchBlob.flush(this.taskId, this.path)
157 200
     }
158 201
 
202
+    this.path = () => {
203
+      if(this.type === 'path')
204
+        return this.data
205
+      return null
206
+    }
207
+
159 208
     /**
160 209
      * Start read stream from cached file
161 210
      * @param  {String} encoding Encode type, should be one of `base64`, `ascrii`, `utf8`.
@@ -172,7 +221,13 @@ class FetchBlobResponse {
172 221
           subscription()
173 222
       })
174 223
 
175
-      RNFetchBlob.readStream(this.taskId, this.path, encode)
224
+      if(this.type === 'path') {
225
+        RNFetchBlob.readStream(this.taskId, this.data, encode)
226
+      }
227
+      else {
228
+        console.warn('RNFetchblob', 'this response data does not contains any available stream')
229
+      }
230
+
176 231
     }
177 232
 
178 233
   }
@@ -187,5 +242,5 @@ function getUUID(){
187 242
 }
188 243
 
189 244
 export default {
190
-  fetch, FetchBlobResponse, base64
245
+  fetch, base64, config, getSystemDirs
191 246
 }

+ 66
- 22
src/ios/RNFetchBlob/RNFetchBlob.m View File

@@ -47,25 +47,25 @@ NSString *const FS_EVENT_ERROR = @"error";
47 47
 
48 48
 + (NSString *) getDocumentDir {
49 49
 
50
-    return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
50
+    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
51 51
 }
52 52
 
53 53
 + (NSString *) getMusicDir {
54
-    return NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES);
54
+    return [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) firstObject];
55 55
 }
56 56
 
57 57
 + (NSString *) getMovieDir {
58
-    return NSSearchPathForDirectoriesInDomains(NSMoviesDirectory, NSUserDomainMask, YES);
58
+    return [NSSearchPathForDirectoriesInDomains(NSMoviesDirectory, NSUserDomainMask, YES) firstObject];
59 59
 }
60 60
 
61 61
 + (NSString *) getPictureDir {
62
-    return NSSearchPathForDirectoriesInDomains(NSPicturesDirectory, NSUserDomainMask, YES);
62
+    return [NSSearchPathForDirectoriesInDomains(NSPicturesDirectory, NSUserDomainMask, YES) firstObject];
63 63
 }
64 64
 
65 65
 + (NSString *) getTempPath:(NSString*)taskId {
66 66
 
67
-    NSString * documentDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
68
-    NSString * filename = [NSString stringWithFormat:@"RNFetchBlobTmp_%s", taskId];
67
+    NSString * documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
68
+    NSString * filename = [NSString stringWithFormat:@"/RNFetchBlobTmp_%@", taskId];
69 69
     NSString * tempPath = [documentDir stringByAppendingString: filename];
70 70
     return tempPath;
71 71
 }
@@ -83,8 +83,8 @@ NSString *const FS_EVENT_ERROR = @"error";
83 83
 }
84 84
 
85 85
 - (void)openWithPath:(NSString *)destPath {
86
-    self.outStream = [[NSOutputStream alloc]init];
87
-    [self.outStream initToFileAtPath:destPath append:NO];
86
+    self.outStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:YES];
87
+    [self.outStream open];
88 88
 }
89 89
 
90 90
 
@@ -97,8 +97,17 @@ NSString *const FS_EVENT_ERROR = @"error";
97 97
 }
98 98
 
99 99
 // Write file chunk into an opened stream
100
-- (void)write:(NSString *) chunk {
101
-    [self.outStream write:[chunk cStringUsingEncoding:NSASCIIStringEncoding] maxLength:chunk.length];
100
+- (void)write:(NSData *) chunk toPath:(NSString *) path{
101
+    NSUInteger left = [chunk length];
102
+    NSUInteger nwr = 0;
103
+    do {
104
+        nwr = [self.outStream write:[chunk bytes] maxLength:left];
105
+        if (-1 == nwr) break;
106
+        left -= nwr;
107
+    } while (left > 0);
108
+    if (left) {
109
+        NSLog(@"stream error: %@", [self.outStream streamError]);
110
+    }
102 111
 }
103 112
 
104 113
 - (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding {
@@ -117,7 +126,7 @@ NSString *const FS_EVENT_ERROR = @"error";
117 126
         [self readWithPath:path useEncoding:encoding];
118 127
 }
119 128
 
120
-// close file stream
129
+// close file write stream
121 130
 - (void)closeOutStream {
122 131
     if(self.outStream != nil) {
123 132
         [self.outStream close];
@@ -126,6 +135,7 @@ NSString *const FS_EVENT_ERROR = @"error";
126 135
 
127 136
 }
128 137
 
138
+// close file read stream
129 139
 - (void)closeInStream {
130 140
     if(self.inStream != nil) {
131 141
         [self.inStream close];
@@ -139,7 +149,15 @@ NSString *const FS_EVENT_ERROR = @"error";
139 149
 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
140 150
 
141 151
     switch(eventCode) {
142
-    
152
+            
153
+        // write stream event
154
+        case NSStreamEventHasSpaceAvailable:
155
+        {
156
+            
157
+        
158
+        }
159
+        
160
+        // read stream incoming chunk
143 161
         case NSStreamEventHasBytesAvailable:
144 162
         {
145 163
             
@@ -196,6 +214,8 @@ NSString *const FS_EVENT_ERROR = @"error";
196 214
             }
197 215
             break;
198 216
         }
217
+            
218
+        // stream error
199 219
         case NSStreamEventErrorOccurred:
200 220
         {
201 221
             [self.bridge.eventDispatcher
@@ -265,7 +285,7 @@ NSString *const FS_EVENT_ERROR = @"error";
265 285
         self.fileStream = [[FetchBlobFS alloc]initWithCallback:self.callback];
266 286
         [self.fileStream openWithPath:path];
267 287
     }
268
-    else if ( [self.options valueForKey:CONFIG_USE_TEMP] == YES ) {
288
+    else if ( [self.options valueForKey:CONFIG_USE_TEMP]!= nil ) {
269 289
         self.fileStream = [[FetchBlobFS alloc]initWithCallback:self.callback];
270 290
         [self.fileStream openWithId:taskId];
271 291
     }
@@ -284,7 +304,7 @@ NSString *const FS_EVENT_ERROR = @"error";
284 304
 
285 305
 
286 306
 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response {
287
-    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
307
+//    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
288 308
     expectedBytes = [response expectedContentLength];
289 309
 }
290 310
 
@@ -294,10 +314,12 @@ NSString *const FS_EVENT_ERROR = @"error";
294 314
     
295 315
     Boolean fileCache = [self.options valueForKey:CONFIG_USE_TEMP];
296 316
     NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
297
-    
317
+    if(path != nil) {
318
+        [self.fileStream write:data toPath:path];
319
+    }
298 320
     // write to tmp file
299
-    if( fileCache == YES || path != nil ) {
300
-        [self.fileStream write:data];
321
+    else if( fileCache != nil) {
322
+        [self.fileStream write:data toPath:[FetchBlobFS getTempPath:self.taskId ]];
301 323
     }
302 324
     // cache data in memory
303 325
     else {
@@ -331,7 +353,7 @@ NSString *const FS_EVENT_ERROR = @"error";
331 353
 
332 354
 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
333 355
     
334
-    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
356
+//    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
335 357
     
336 358
     [self.fileStream closeInStream];
337 359
     [self.fileStream closeOutStream];
@@ -358,7 +380,8 @@ NSString *const FS_EVENT_ERROR = @"error";
358 380
     else
359 381
         data = [[NSData alloc] init];
360 382
     
361
-    NSString * path = [NSString stringWithString:[self.options valueForKey:CONFIG_FILE_PATH]];
383
+    NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
384
+    Boolean useCache = [self.options valueForKey:CONFIG_USE_TEMP];
362 385
     
363 386
     [self.fileStream closeInStream];
364 387
     
@@ -367,7 +390,7 @@ NSString *const FS_EVENT_ERROR = @"error";
367 390
         callback(@[[NSNull null], path]);
368 391
     }
369 392
     // when fileCache option is set but no path specified, save to tmp path
370
-    else if( [self.options valueForKey:CONFIG_USE_TEMP] == YES || path != nil ) {
393
+    else if( [self.options valueForKey:CONFIG_USE_TEMP] != nil) {
371 394
         NSString * tmpPath = [FetchBlobFS getTempPath:taskId];
372 395
         callback(@[[NSNull null], tmpPath]);
373 396
     }
@@ -395,7 +418,13 @@ NSString *const FS_EVENT_ERROR = @"error";
395 418
 RCT_EXPORT_MODULE();
396 419
 
397 420
 // Fetch blob data request
398
-RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options taskId:(NSString *)taskId method:(NSString *)method url:(NSString *)url headers:(NSDictionary *)headers form:(NSArray *)form callback:(RCTResponseSenderBlock)callback)
421
+RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options
422
+                  taskId:(NSString *)taskId
423
+                  method:(NSString *)method
424
+                  url:(NSString *)url
425
+                  headers:(NSDictionary *)headers
426
+                  form:(NSArray *)form
427
+                  callback:(RCTResponseSenderBlock)callback)
399 428
 {
400 429
     
401 430
     // send request
@@ -461,7 +490,12 @@ RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options taskId:(NSString *)taskI
461 490
 }
462 491
 
463 492
 // Fetch blob data request
464
-RCT_EXPORT_METHOD(fetchBlob:(NSDictionary *)options taskId:(NSString *)taskId method:(NSString *)method url:(NSString *)url headers:(NSDictionary *)headers body:(NSString *)body callback:(RCTResponseSenderBlock)callback)
493
+RCT_EXPORT_METHOD(fetchBlob:(NSDictionary *)options
494
+                  taskId:(NSString *)taskId
495
+                  method:(NSString *)method
496
+                  url:(NSString *)url
497
+                  headers:(NSDictionary *)headers
498
+                  body:(NSString *)body callback:(RCTResponseSenderBlock)callback)
465 499
 {
466 500
     // send request
467 501
     NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
@@ -507,4 +541,14 @@ RCT_EXPORT_METHOD(flush:(NSString *)taskId withPath:(NSString *)path) {
507 541
     [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
508 542
 }
509 543
 
544
+RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
545
+    
546
+    callback(@[
547
+               [FetchBlobFS getPictureDir],
548
+               [FetchBlobFS getMovieDir],
549
+               [FetchBlobFS getDocumentDir],
550
+               [FetchBlobFS getCacheDir],
551
+            ]);
552
+}
553
+
510 554
 @end