Browse Source

Merge branch '0.10.0'

Ben Hsieh 7 years ago
parent
commit
5da01132cc
48 changed files with 52288 additions and 190 deletions
  1. 5
    9
      CONTRIBUTORS.md
  2. 8
    7
      README.md
  3. 22
    4
      package.json
  4. 1
    0
      scripts/README.md
  5. 21
    0
      scripts/contributors.sh
  6. 1
    0
      scripts/test.sh
  7. 25
    4
      src/README.md
  8. 52
    3
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java
  9. 9
    0
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java
  10. 30
    6
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java
  11. 33
    17
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
  12. 11
    1
      src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobDefaultResp.java
  13. 2
    2
      src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
  14. 53
    0
      src/android/src/main/java/com/RNFetchBlob/Utils/RNFBCookieJar.java
  15. 15
    2
      src/fs.js
  16. 124
    5
      src/index.js
  17. 44
    0
      src/ios.js
  18. 2
    2
      src/ios/RNFetchBlob.xcodeproj/project.pbxproj
  19. 4
    1
      src/ios/RNFetchBlob/RNFetchBlob.h
  20. 66
    3
      src/ios/RNFetchBlob/RNFetchBlob.m
  21. 1
    0
      src/ios/RNFetchBlobConst.h
  22. 1
    2
      src/ios/RNFetchBlobConst.m
  23. 4
    0
      src/ios/RNFetchBlobFS.h
  24. 37
    5
      src/ios/RNFetchBlobFS.m
  25. 12
    7
      src/ios/RNFetchBlobNetwork.h
  26. 165
    61
      src/ios/RNFetchBlobNetwork.m
  27. 2
    2
      src/ios/RNFetchBlobProgress.m
  28. 39
    0
      src/json-stream.js
  29. 2703
    0
      src/lib/oboe-browser.js
  30. 1
    0
      src/lib/oboe-browser.min.js
  31. 26
    0
      src/net.js
  32. 21
    4
      src/package.json
  33. 1
    1
      src/polyfill/Fetch.js
  34. 17
    3
      src/polyfill/XMLHttpRequest.js
  35. 2
    2
      src/react-native-fetch-blob.podspec
  36. 28
    0
      src/utils/uri.js
  37. 3
    0
      test-server/public/json-dummy-1.json
  38. 48331
    15
      test-server/public/json-dummy.json
  39. 36
    12
      test-server/server.js
  40. 1
    0
      test-server/test.jpeg
  41. 4
    0
      test/nedb.js
  42. 214
    0
      test/test-0.10.0.js
  43. 3
    1
      test/test-0.6.2.js
  44. 8
    2
      test/test-0.9.4.js
  45. 3
    3
      test/test-0.9.5.js
  46. 51
    0
      test/test-background.js
  47. 6
    4
      test/test-init.js
  48. 40
    0
      test/test-readable.js

+ 5
- 9
CONTRIBUTORS.md View File

1
-
1
+Andreas Amsenius <andreas@amsenius.se>
2
+Corentin Smith <corentin.smith@gmail.com>
2
 Dmitry Petukhov <dmitryvpetukhov@gmail.com>
3
 Dmitry Petukhov <dmitryvpetukhov@gmail.com>
3
-
4
 Erik Smartt <code@eriksmartt.com>
4
 Erik Smartt <code@eriksmartt.com>
5
-
6
 Evgeniy Baraniuk <ev.baraniuk@gmail.com>
5
 Evgeniy Baraniuk <ev.baraniuk@gmail.com>
7
-
8
 Juan B. Rodriguez <jbrodriguez@gmail.com>
6
 Juan B. Rodriguez <jbrodriguez@gmail.com>
9
-
10
 Kaishley <kklingachetti@msn.com>
7
 Kaishley <kklingachetti@msn.com>
11
-
8
+Mike Monteith <mike@mikemonteith.com>
12
 Nguyen Cao Nhat Linh <nhatlinh95@gmail.com>
9
 Nguyen Cao Nhat Linh <nhatlinh95@gmail.com>
13
-
10
+Tim Suchanek <tim.suchanek@gmail.com>
14
 follower <github@rancidbacon.com>
11
 follower <github@rancidbacon.com>
15
-
16
 francisco-sanchez-molina <psm1984@gmail.com>
12
 francisco-sanchez-molina <psm1984@gmail.com>
17
-
13
+kejinliang <kejinliang@users.noreply.github.com>
18
 smartt <github@eriksmartt.com>
14
 smartt <github@eriksmartt.com>

+ 8
- 7
README.md View File

14
 - Native-to-native file manipulation API, reduce JS bridging performance loss
14
 - Native-to-native file manipulation API, reduce JS bridging performance loss
15
 - File stream support for dealing with large file
15
 - File stream support for dealing with large file
16
 - Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental)
16
 - Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental)
17
+- JSON stream supported base on [Oboe.js@jimhigson](https://github.com/jimhigson/oboe.js/)
17
 
18
 
18
 ## TOC
19
 ## TOC
19
 * [About](#user-content-about)
20
 * [About](#user-content-about)
81
 RNFB_ANDROID_PERMISSIONS=true react-native link
82
 RNFB_ANDROID_PERMISSIONS=true react-native link
82
 ```
83
 ```
83
 
84
 
84
-pre 0.29 projects 
85
+pre 0.29 projects
85
 
86
 
86
 ```sh
87
 ```sh
87
 RNFB_ANDROID_PERMISSIONS=true rnpm link
88
 RNFB_ANDROID_PERMISSIONS=true rnpm link
88
 ```
89
 ```
89
 
90
 
90
-The link script might not take effect if you have non-default project structure, please visit [the wiki](https://github.com/wkh237/react-native-fetch-blob/wiki/Manually-Link-Package) to manually link the package.
91
+The link script might not take effect if you have non-default project structure, please visit [the wiki](https://github.com/wkh237/react-native-fetch-blob/wiki/Manually-Link-Package) to manually link the pacakge.
91
 
92
 
92
 **Grant Permission to External storage for Android 5.0 or lower**
93
 **Grant Permission to External storage for Android 5.0 or lower**
93
 
94
 
156
 - To send a form data, the `Content-Type` header does not matters. When the body is an `Array` we will set proper content type for you.
157
 - To send a form data, the `Content-Type` header does not matters. When the body is an `Array` we will set proper content type for you.
157
 - To send binary data, you have two choices, use BASE64 encoded string or path points to a file contains the body.
158
 - To send binary data, you have two choices, use BASE64 encoded string or path points to a file contains the body.
158
  - If the `Content-Type` containing substring`;BASE64` or `application/octet` the given body will be considered as a BASE64 encoded data which will be decoded to binary data as the request body.   
159
  - If the `Content-Type` containing substring`;BASE64` or `application/octet` the given body will be considered as a BASE64 encoded data which will be decoded to binary data as the request body.   
159
- - Otherwise, if a string starts with `RNFetchBlob-file://` (which can simply done by `RNFetchBlob.wrap(PATH_TO_THE_FILE)`), it will try to find the data from the URI string after `RNFetchBlob-file://` and use it as request body. 
160
+ - Otherwise, if a string starts with `RNFetchBlob-file://` (which can simply done by `RNFetchBlob.wrap(PATH_TO_THE_FILE)`), it will try to find the data from the URI string after `RNFetchBlob-file://` and use it as request body.
160
 - To send the body as-is, simply use a `Content-Type` header not containing `;BASE64` or `application/octet`.
161
 - To send the body as-is, simply use a `Content-Type` header not containing `;BASE64` or `application/octet`.
161
 
162
 
162
-> It is Worth to mentioning that the HTTP request uses cache by default, if you're going to disable it simply add a Cache Control header `'Cache-Control' : 'no-store'` 
163
+> It is Worth to mentioning that the HTTP request uses cache by default, if you're going to disable it simply add a Cache Control header `'Cache-Control' : 'no-store'`
163
 
164
 
164
 > After 0.9.4, we disabled `Chunked` transfer encoding by default, if you're going to use it, you should explicitly set header `Transfer-Encoding` to `Chunked`.
165
 > After 0.9.4, we disabled `Chunked` transfer encoding by default, if you're going to use it, you should explicitly set header `Transfer-Encoding` to `Chunked`.
165
 
166
 
382
 
383
 
383
 ### Upload/Download progress
384
 ### Upload/Download progress
384
 
385
 
385
-In `version >= 0.4.2` it is possible to know the upload/download progress. After `0.7.0` IOS and Android upload progress are also supported. 
386
+In `version >= 0.4.2` it is possible to know the upload/download progress. After `0.7.0` IOS and Android upload progress are also supported.
386
 
387
 
387
 ```js
388
 ```js
388
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
389
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
739
 
740
 
740
 **Read Stream and Progress Event Overhead**
741
 **Read Stream and Progress Event Overhead**
741
 
742
 
742
-When reading data via `fs.readStream` the process seems blocking JS thread when file is large, it's because the default buffer size is quite small (4kb) which result in large amount of events triggered in JS thread, try to increase the buffer size (for example 100kb = 102400) and set a larger interval (which is introduced in 0.9.4 default value is 10ms) to limit the frequency. 
743
+When reading data via `fs.readStream` the process seems blocking JS thread when file is large, it's because the default buffer size is quite small (4kb) which result in large amount of events triggered in JS thread, try to increase the buffer size (for example 100kb = 102400) and set a larger interval (which is introduced in 0.9.4 default value is 10ms) to limit the frequency.
743
 
744
 
744
 **Reduce RCT Bridge and BASE64 Overhead**
745
 **Reduce RCT Bridge and BASE64 Overhead**
745
 
746
 
761
 
762
 
762
 ## Caveats
763
 ## Caveats
763
 
764
 
764
-* This library does not urlencode unicode characters in URL automatically, see [#146](https://github.com/wkh237/react-native-fetch-blob/issues/146). 
765
+* This library does not urlencode unicode characters in URL automatically, see [#146](https://github.com/wkh237/react-native-fetch-blob/issues/146).
765
 * When a `Blob` is created from existing file, the file **WILL BE REMOVE** if you `close` the blob.
766
 * When a `Blob` is created from existing file, the file **WILL BE REMOVE** if you `close` the blob.
766
 * If you replaced `window.XMLHttpRequest` for some reason (e.g. make Firebase SDK work), it will also effect how official `fetch` works (basically it should work just fine).
767
 * If you replaced `window.XMLHttpRequest` for some reason (e.g. make Firebase SDK work), it will also effect how official `fetch` works (basically it should work just fine).
767
 * When file stream and upload/download progress event slow down your app, consider upgrade to `0.9.6+`, use [additional arguments](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetchprogressconfig-eventlistenerpromisernfetchblobresponse) to limit its frequency.
768
 * When file stream and upload/download progress event slow down your app, consider upgrade to `0.9.6+`, use [additional arguments](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetchprogressconfig-eventlistenerpromisernfetchblobresponse) to limit its frequency.

+ 22
- 4
package.json View File

1
 {
1
 {
2
   "name": "react-native-fetch-blob-dev-env",
2
   "name": "react-native-fetch-blob-dev-env",
3
-  "description" : "RNFB development environment, not dist package",
4
-  "version": "0.9.6",
3
+  "description": "RNFB development environment, not dist package",
4
+  "version": "0.10.0-dev",
5
   "private": true,
5
   "private": true,
6
   "scripts": {
6
   "scripts": {
7
     "start": "node node_modules/react-native/local-cli/cli.js start",
7
     "start": "node node_modules/react-native/local-cli/cli.js start",
8
+    "update-info": "sh scripts/contributors.sh",
8
     "test": "sh test.sh"
9
     "test": "sh test.sh"
9
   },
10
   },
10
   "devDependencies": {
11
   "devDependencies": {
13
     "chokidar": "^1.5.1",
14
     "chokidar": "^1.5.1",
14
     "express": "^4.13.4",
15
     "express": "^4.13.4",
15
     "multer": "^1.1.0"
16
     "multer": "^1.1.0"
16
-  }
17
-}
17
+  },
18
+  "contributors": [
19
+    "Andreas Amsenius <andreas@amsenius.se>",
20
+    "Corentin Smith <corentin.smith@gmail.com>",
21
+    "Dmitry Petukhov <dmitryvpetukhov@gmail.com>",
22
+    "Erik Smartt <code@eriksmartt.com>",
23
+    "Evgeniy Baraniuk <ev.baraniuk@gmail.com>",
24
+    "Juan B. Rodriguez <jbrodriguez@gmail.com>",
25
+    "Kaishley <kklingachetti@msn.com>",
26
+    "Mike Monteith <mike@mikemonteith.com>",
27
+    "Nguyen Cao Nhat Linh <nhatlinh95@gmail.com>",
28
+    "Tim Suchanek <tim.suchanek@gmail.com>",
29
+    "follower <github@rancidbacon.com>",
30
+    "francisco-sanchez-molina <psm1984@gmail.com>",
31
+    "kejinliang <kejinliang@users.noreply.github.com>",
32
+    "smartt <github@eriksmartt.com>",
33
+    ""
34
+  ]
35
+}

+ 1
- 0
scripts/README.md View File

1
+Always execute these scripts from root folder, not here.

+ 21
- 0
scripts/contributors.sh View File

1
+
2
+git log --pretty="%an <%ae>" | sort |uniq > CONTRIBUTORS.md
3
+sed -i.bak '/xeiyan@gmail.com/d' ./CONTRIBUTORS.md
4
+rm CONTRIBUTORS.md.bak
5
+
6
+echo "list contributors .."
7
+
8
+cat CONTRIBUTORS.md
9
+
10
+echo "update package.json .."
11
+
12
+node -e "var fs = require('fs');\
13
+        var json = JSON.parse(fs.readFileSync('./package.json'));\
14
+        var contributors = String(fs.readFileSync('./CONTRIBUTORS.md')).split(/[\r\n]/);\
15
+        json.contributors = contributors;\
16
+        var distJSON = JSON.parse(fs.readFileSync('./src/package.json'));\
17
+        distJSON.contributors = contributors;\
18
+        fs.writeFileSync('./src/package.json', JSON.stringify(distJSON, null, 2));\
19
+        fs.writeFileSync('./package.json', JSON.stringify(json, null, 2));"
20
+
21
+echo "done"

test.sh → scripts/test.sh View File

39
 cd "${TEST_APP_PATH}"
39
 cd "${TEST_APP_PATH}"
40
 # npm install --save "${CWD}/src"
40
 # npm install --save "${CWD}/src"
41
 npm install --save react-native-fetch-blob
41
 npm install --save react-native-fetch-blob
42
+# libs that requires web API polyfills
42
 npm install --save firebase
43
 npm install --save firebase
43
 react-native link
44
 react-native link
44
 
45
 

+ 25
- 4
src/README.md View File

1
 # react-native-fetch-blob
1
 # react-native-fetch-blob
2
 [![release](https://img.shields.io/github/release/wkh237/react-native-fetch-blob.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/releases) [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![](https://img.shields.io/badge/Wiki-Public-brightgreen.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/wiki) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]()
2
 [![release](https://img.shields.io/github/release/wkh237/react-native-fetch-blob.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/releases) [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![](https://img.shields.io/badge/Wiki-Public-brightgreen.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/wiki) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]()
3
 
3
 
4
-
5
 A project committed to make file acess and data transfer easier, efficient for React Native developers.
4
 A project committed to make file acess and data transfer easier, efficient for React Native developers.
6
 
5
 
7
-# [Please visit our Github Page for latest document](https://github.com/wkh237/react-native-fetch-blob)
8
-
9
 ## Features
6
 ## Features
10
 - Transfer data directly from/to storage without BASE64 bridging
7
 - Transfer data directly from/to storage without BASE64 bridging
11
 - File API supports normal files, Asset files, and CameraRoll files
8
 - File API supports normal files, Asset files, and CameraRoll files
85
 RNFB_ANDROID_PERMISSIONS=true rnpm link
82
 RNFB_ANDROID_PERMISSIONS=true rnpm link
86
 ```
83
 ```
87
 
84
 
88
-The link script might not take effect if you have non-default project structure, please visit [the wiki](https://github.com/wkh237/react-native-fetch-blob/wiki/Manually-Link-Package/_edit) to manually link the pacakge.
85
+The link script might not take effect if you have non-default project structure, please visit [the wiki](https://github.com/wkh237/react-native-fetch-blob/wiki/Manually-Link-Package) to manually link the pacakge.
89
 
86
 
90
 **Grant Permission to External storage for Android 5.0 or lower**
87
 **Grant Permission to External storage for Android 5.0 or lower**
91
 
88
 
403
     })
400
     })
404
 ```
401
 ```
405
 
402
 
403
+In `0.9.6`, you can specify an optional first argument which contains `count` and `interval` to limit progress event frequency (this will be done in native context in order to reduce RCT bridge overhead). Notice that `count` argument will not work if the server does not provide response content length.
404
+
405
+
406
+```js
407
+  RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
408
+      ... some headers,
409
+      'Content-Type' : 'octet-stream'
410
+    }, base64DataString)
411
+    // listen to upload progress event, emit every 250ms
412
+    .uploadProgress({ interval : 250 },(written, total) => {
413
+        console.log('uploaded', written / total)
414
+    })
415
+    // listen to download progress event, every 10%
416
+    .progress({ count : 10 }, (received, total) => {
417
+        console.log('progress', received / total)
418
+    })
419
+    .then((resp) => {
420
+      // ...
421
+    })
422
+    .catch((err) => {
423
+      // ...
424
+    })
425
+```
426
+
406
 ### Cancel Request
427
 ### Cancel Request
407
 
428
 
408
 After `0.7.0` it is possible to cancel a HTTP request. When the request cancel, it will definately throws an promise rejection, be sure to catch it.
429
 After `0.7.0` it is possible to cancel a HTTP request. When the request cancel, it will definately throws an promise rejection, be sure to catch it.

+ 52
- 3
src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java View File

3
 import android.content.Intent;
3
 import android.content.Intent;
4
 import android.net.Uri;
4
 import android.net.Uri;
5
 
5
 
6
+import com.RNFetchBlob.Utils.RNFBCookieJar;
6
 import com.facebook.react.bridge.Callback;
7
 import com.facebook.react.bridge.Callback;
8
+import com.facebook.react.bridge.LifecycleEventListener;
7
 import com.facebook.react.bridge.Promise;
9
 import com.facebook.react.bridge.Promise;
8
 import com.facebook.react.bridge.ReactApplicationContext;
10
 import com.facebook.react.bridge.ReactApplicationContext;
9
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
11
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
 import com.facebook.react.bridge.ReactMethod;
12
 import com.facebook.react.bridge.ReactMethod;
11
 import com.facebook.react.bridge.ReadableArray;
13
 import com.facebook.react.bridge.ReadableArray;
12
 import com.facebook.react.bridge.ReadableMap;
14
 import com.facebook.react.bridge.ReadableMap;
15
+import com.facebook.react.bridge.WritableArray;
13
 
16
 
14
 import java.util.Map;
17
 import java.util.Map;
15
 import java.util.concurrent.LinkedBlockingQueue;
18
 import java.util.concurrent.LinkedBlockingQueue;
23
     static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
26
     static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
24
     static LinkedBlockingQueue<Runnable> fsTaskQueue = new LinkedBlockingQueue<>();
27
     static LinkedBlockingQueue<Runnable> fsTaskQueue = new LinkedBlockingQueue<>();
25
     static ThreadPoolExecutor fsThreadPool = new ThreadPoolExecutor(2, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
28
     static ThreadPoolExecutor fsThreadPool = new ThreadPoolExecutor(2, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
29
+    static public boolean ActionViewVisible = false;
26
 
30
 
27
     public RNFetchBlob(ReactApplicationContext reactContext) {
31
     public RNFetchBlob(ReactApplicationContext reactContext) {
28
 
32
 
29
         super(reactContext);
33
         super(reactContext);
34
+
30
         RCTContext = reactContext;
35
         RCTContext = reactContext;
31
     }
36
     }
32
 
37
 
52
     }
57
     }
53
 
58
 
54
     @ReactMethod
59
     @ReactMethod
55
-    public void actionViewIntent(String path, String mime, Promise promise) {
60
+    public void actionViewIntent(String path, String mime, final Promise promise) {
56
         try {
61
         try {
57
             Intent intent= new Intent(Intent.ACTION_VIEW)
62
             Intent intent= new Intent(Intent.ACTION_VIEW)
58
                     .setDataAndType(Uri.parse("file://" + path), mime);
63
                     .setDataAndType(Uri.parse("file://" + path), mime);
59
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
64
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
60
-
61
             this.getReactApplicationContext().startActivity(intent);
65
             this.getReactApplicationContext().startActivity(intent);
62
-            promise.resolve(null);
66
+            ActionViewVisible = true;
67
+
68
+            final LifecycleEventListener listener = new LifecycleEventListener() {
69
+                @Override
70
+                public void onHostResume() {
71
+                    if(ActionViewVisible)
72
+                        promise.resolve(null);
73
+                    RCTContext.removeLifecycleEventListener(this);
74
+                }
75
+
76
+                @Override
77
+                public void onHostPause() {
78
+
79
+                }
80
+
81
+                @Override
82
+                public void onHostDestroy() {
83
+
84
+                }
85
+            };
86
+            RCTContext.addLifecycleEventListener(listener);
63
         } catch(Exception ex) {
87
         } catch(Exception ex) {
64
             promise.reject(ex.getLocalizedMessage());
88
             promise.reject(ex.getLocalizedMessage());
65
         }
89
         }
203
 
227
 
204
     }
228
     }
205
 
229
 
230
+    @ReactMethod
231
+    /**
232
+     * Get cookies belongs specific host.
233
+     * @param host String host name.
234
+     */
235
+    public void getCookies(String host, Promise promise) {
236
+        try {
237
+            WritableArray cookies = RNFBCookieJar.getCookies(host);
238
+            promise.resolve(cookies);
239
+        } catch(Exception err) {
240
+            promise.reject("RNFetchBlob.getCookies", err.getMessage());
241
+        }
242
+    }
243
+
206
     @ReactMethod
244
     @ReactMethod
207
     /**
245
     /**
208
      * @param path Stream file path
246
      * @param path Stream file path
241
         RNFetchBlobReq.progressReport.put(taskId, config);
279
         RNFetchBlobReq.progressReport.put(taskId, config);
242
     }
280
     }
243
 
281
 
282
+    @ReactMethod
283
+    public void df(final Callback callback) {
284
+        fsThreadPool.execute(new Runnable() {
285
+            @Override
286
+            public void run() {
287
+                RNFetchBlobFS.df(callback);
288
+            }
289
+        });
290
+    }
291
+
292
+
244
     @ReactMethod
293
     @ReactMethod
245
     public void enableUploadProgressReport(String taskId, int interval, int count) {
294
     public void enableUploadProgressReport(String taskId, int interval, int count) {
246
         RNFetchBlobProgressConfig config = new RNFetchBlobProgressConfig(true, interval, count, RNFetchBlobProgressConfig.ReportType.Upload);
295
         RNFetchBlobProgressConfig config = new RNFetchBlobProgressConfig(true, interval, count, RNFetchBlobProgressConfig.ReportType.Upload);

+ 9
- 0
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java View File

16
     public String key;
16
     public String key;
17
     public String mime;
17
     public String mime;
18
     public Boolean auto;
18
     public Boolean auto;
19
+    public Boolean overwrite = true;
19
     public long timeout = 60000;
20
     public long timeout = 60000;
21
+    public Boolean increment = false;
20
     public ReadableArray binaryContentTypes = null;
22
     public ReadableArray binaryContentTypes = null;
21
 
23
 
22
     RNFetchBlobConfig(ReadableMap options) {
24
     RNFetchBlobConfig(ReadableMap options) {
31
         }
33
         }
32
         if(options.hasKey("binaryContentTypes"))
34
         if(options.hasKey("binaryContentTypes"))
33
             this.binaryContentTypes = options.getArray("binaryContentTypes");
35
             this.binaryContentTypes = options.getArray("binaryContentTypes");
36
+        if(this.path != null && path.toLowerCase().contains("?append=true")) {
37
+            this.overwrite = false;
38
+        }
39
+
40
+        if(options.hasKey("overwrite"))
41
+            this.overwrite = options.getBoolean("overwrite");
34
         this.key = options.hasKey("key") ? options.getString("key") : null;
42
         this.key = options.hasKey("key") ? options.getString("key") : null;
35
         this.mime = options.hasKey("contentType") ? options.getString("contentType") : null;
43
         this.mime = options.hasKey("contentType") ? options.getString("contentType") : null;
44
+        this.increment = options.hasKey("increment") ? options.getBoolean("increment") : false;
36
         this.auto = options.hasKey("auto") ? options.getBoolean("auto") : false;
45
         this.auto = options.hasKey("auto") ? options.getBoolean("auto") : false;
37
         if(options.hasKey("timeout")) {
46
         if(options.hasKey("timeout")) {
38
             this.timeout = options.getInt("timeout");
47
             this.timeout = options.getInt("timeout");

+ 30
- 6
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java View File

1
 package com.RNFetchBlob;
1
 package com.RNFetchBlob;
2
 
2
 
3
+import android.content.pm.PackageInfo;
4
+import android.content.pm.PackageManager;
3
 import android.content.res.AssetFileDescriptor;
5
 import android.content.res.AssetFileDescriptor;
4
 import android.media.MediaScannerConnection;
6
 import android.media.MediaScannerConnection;
5
 import android.net.Uri;
7
 import android.net.Uri;
6
 import android.os.AsyncTask;
8
 import android.os.AsyncTask;
9
+import android.os.Build;
7
 import android.os.Environment;
10
 import android.os.Environment;
11
+import android.os.StatFs;
8
 import android.os.SystemClock;
12
 import android.os.SystemClock;
9
 import android.util.Base64;
13
 import android.util.Base64;
10
 
14
 
184
      */
188
      */
185
     static public Map<String, Object> getSystemfolders(ReactApplicationContext ctx) {
189
     static public Map<String, Object> getSystemfolders(ReactApplicationContext ctx) {
186
         Map<String, Object> res = new HashMap<>();
190
         Map<String, Object> res = new HashMap<>();
191
+
187
         res.put("DocumentDir", ctx.getFilesDir().getAbsolutePath());
192
         res.put("DocumentDir", ctx.getFilesDir().getAbsolutePath());
188
         res.put("CacheDir", ctx.getCacheDir().getAbsolutePath());
193
         res.put("CacheDir", ctx.getCacheDir().getAbsolutePath());
189
         res.put("DCIMDir", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath());
194
         res.put("DCIMDir", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath());
192
         res.put("DownloadDir", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
197
         res.put("DownloadDir", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
193
         res.put("MovieDir", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath());
198
         res.put("MovieDir", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath());
194
         res.put("RingtoneDir", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES).getAbsolutePath());
199
         res.put("RingtoneDir", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES).getAbsolutePath());
195
-        res.put("SDCard", Environment.getExternalStorageDirectory().getAbsolutePath());
200
+        String state;
201
+        state = Environment.getExternalStorageState();
202
+        if (state.equals(Environment.MEDIA_MOUNTED)) {
203
+            res.put("SDCard", Environment.getExternalStorageDirectory().getAbsolutePath());
204
+        }
205
+        res.put("MainBundleDir", ctx.getApplicationInfo().dataDir);
196
         return res;
206
         return res;
197
     }
207
     }
198
 
208
 
687
             }
697
             }
688
             else {
698
             else {
689
                 if (!created) {
699
                 if (!created) {
690
-                    callback.invoke("create file error: failed to create file at path `" + path + "` for its parent path may not exists");
700
+                    callback.invoke("create file error: failed to create file at path `" + path + "` for its parent path may not exists, or the file already exists. If you intended to overwrite the existing file use fs.writeFile instead.");
691
                     return;
701
                     return;
692
                 }
702
                 }
693
                 OutputStream ostream = new FileOutputStream(dest);
703
                 OutputStream ostream = new FileOutputStream(dest);
730
         }
740
         }
731
     }
741
     }
732
 
742
 
743
+    static void df(Callback callback) {
744
+        StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
745
+        WritableMap args = Arguments.createMap();
746
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
747
+            args.putString("internal_free", String.valueOf(stat.getFreeBytes()));
748
+            args.putString("internal_total", String.valueOf(stat.getTotalBytes()));
749
+            StatFs statEx = new StatFs(Environment.getExternalStorageDirectory().getPath());
750
+            args.putString("external_free", String.valueOf(statEx.getFreeBytes()));
751
+            args.putString("external_total", String.valueOf(statEx.getTotalBytes()));
752
+
753
+        }
754
+        callback.invoke(null ,args);
755
+    }
756
+
733
     /**
757
     /**
734
      * Remove files in session.
758
      * Remove files in session.
735
      * @param paths An array of file paths.
759
      * @param paths An array of file paths.
782
      * @param event Event name, `data`, `end`, `error`, etc.
806
      * @param event Event name, `data`, `end`, `error`, etc.
783
      * @param data  Event data
807
      * @param data  Event data
784
      */
808
      */
785
-    void emitStreamEvent(String streamName, String event, String data) {
809
+    private void emitStreamEvent(String streamName, String event, String data) {
786
         WritableMap eventData = Arguments.createMap();
810
         WritableMap eventData = Arguments.createMap();
787
         eventData.putString("event", event);
811
         eventData.putString("event", event);
788
         eventData.putString("detail", data);
812
         eventData.putString("detail", data);
789
         this.emitter.emit(streamName, eventData);
813
         this.emitter.emit(streamName, eventData);
790
     }
814
     }
791
 
815
 
792
-    void emitStreamEvent(String streamName, String event, WritableArray  data) {
816
+    private void emitStreamEvent(String streamName, String event, WritableArray data) {
793
         WritableMap eventData = Arguments.createMap();
817
         WritableMap eventData = Arguments.createMap();
794
         eventData.putString("event", event);
818
         eventData.putString("event", event);
795
         eventData.putArray("detail", data);
819
         eventData.putArray("detail", data);
838
 
862
 
839
     }
863
     }
840
 
864
 
841
-    public static boolean isAsset(String path) {
865
+    static boolean isAsset(String path) {
842
         if(path != null)
866
         if(path != null)
843
             return path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET);
867
             return path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET);
844
         return false;
868
         return false;
845
     }
869
     }
846
 
870
 
847
-    public static String normalizePath(String path) {
871
+    static String normalizePath(String path) {
848
         if(path == null)
872
         if(path == null)
849
             return null;
873
             return null;
850
         Uri uri = Uri.parse(path);
874
         Uri uri = Uri.parse(path);

+ 33
- 17
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java View File

11
 
11
 
12
 import com.RNFetchBlob.Response.RNFetchBlobDefaultResp;
12
 import com.RNFetchBlob.Response.RNFetchBlobDefaultResp;
13
 import com.RNFetchBlob.Response.RNFetchBlobFileResp;
13
 import com.RNFetchBlob.Response.RNFetchBlobFileResp;
14
+import com.RNFetchBlob.Utils.RNFBCookieJar;
14
 import com.facebook.react.bridge.Arguments;
15
 import com.facebook.react.bridge.Arguments;
15
 import com.facebook.react.bridge.Callback;
16
 import com.facebook.react.bridge.Callback;
16
 import com.facebook.react.bridge.ReactApplicationContext;
17
 import com.facebook.react.bridge.ReactApplicationContext;
25
 import java.io.FileOutputStream;
26
 import java.io.FileOutputStream;
26
 import java.io.IOException;
27
 import java.io.IOException;
27
 import java.io.InputStream;
28
 import java.io.InputStream;
29
+import java.net.CookieHandler;
30
+import java.net.CookieManager;
31
+import java.net.CookiePolicy;
28
 import java.net.MalformedURLException;
32
 import java.net.MalformedURLException;
29
 import java.net.SocketException;
33
 import java.net.SocketException;
30
 import java.net.SocketTimeoutException;
34
 import java.net.SocketTimeoutException;
39
 
43
 
40
 import okhttp3.Call;
44
 import okhttp3.Call;
41
 import okhttp3.ConnectionPool;
45
 import okhttp3.ConnectionPool;
46
+import okhttp3.CookieJar;
42
 import okhttp3.Headers;
47
 import okhttp3.Headers;
43
 import okhttp3.Interceptor;
48
 import okhttp3.Interceptor;
44
 import okhttp3.MediaType;
49
 import okhttp3.MediaType;
162
 
167
 
163
         // find cached result if `key` property exists
168
         // find cached result if `key` property exists
164
         String cacheKey = this.taskId;
169
         String cacheKey = this.taskId;
165
-        String ext = this.options.appendExt.isEmpty() ? "." + this.options.appendExt : "";
170
+        String ext = this.options.appendExt.isEmpty() ? "" : "." + this.options.appendExt;
166
 
171
 
167
         if (this.options.key != null) {
172
         if (this.options.key != null) {
168
             cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
173
             cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
183
         else if(this.options.fileCache)
188
         else if(this.options.fileCache)
184
             this.destPath = RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext;
189
             this.destPath = RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext;
185
 
190
 
191
+
186
         OkHttpClient.Builder clientBuilder;
192
         OkHttpClient.Builder clientBuilder;
187
 
193
 
188
         try {
194
         try {
220
                 }
226
                 }
221
             }
227
             }
222
 
228
 
223
-            if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put")) {
229
+            if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
224
                 String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase();
230
                 String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase();
225
 
231
 
226
                 if(rawRequestBodyArray != null) {
232
                 if(rawRequestBodyArray != null) {
281
                     break;
287
                     break;
282
 
288
 
283
                 case WithoutBody:
289
                 case WithoutBody:
284
-                    if(method.equalsIgnoreCase("POST") || method.equalsIgnoreCase("PUT"))
290
+                    if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch"))
285
                     {
291
                     {
286
                         builder.method(method, RequestBody.create(null, new byte[0]));
292
                         builder.method(method, RequestBody.create(null, new byte[0]));
287
                     }
293
                     }
290
                     break;
296
                     break;
291
             }
297
             }
292
 
298
 
299
+            // #156 fix cookie issue
300
+
301
+
293
             final Request req = builder.build();
302
             final Request req = builder.build();
303
+            clientBuilder.cookieJar(new RNFBCookieJar());
294
             clientBuilder.addNetworkInterceptor(new Interceptor() {
304
             clientBuilder.addNetworkInterceptor(new Interceptor() {
295
                 @Override
305
                 @Override
296
                 public Response intercept(Chain chain) throws IOException {
306
                 public Response intercept(Chain chain) throws IOException {
310
                                 extended = new RNFetchBlobDefaultResp(
320
                                 extended = new RNFetchBlobDefaultResp(
311
                                         RNFetchBlob.RCTContext,
321
                                         RNFetchBlob.RCTContext,
312
                                         taskId,
322
                                         taskId,
313
-                                        originalResponse.body());
323
+                                        originalResponse.body(),
324
+                                        options.increment);
314
                                 break;
325
                                 break;
315
                             case FileStorage:
326
                             case FileStorage:
316
                                 extended = new RNFetchBlobFileResp(
327
                                 extended = new RNFetchBlobFileResp(
317
                                         RNFetchBlob.RCTContext,
328
                                         RNFetchBlob.RCTContext,
318
                                         taskId,
329
                                         taskId,
319
                                         originalResponse.body(),
330
                                         originalResponse.body(),
320
-                                        destPath);
331
+                                        destPath,
332
+                                        options.overwrite);
321
                                 break;
333
                                 break;
322
                             default:
334
                             default:
323
                                 extended = new RNFetchBlobDefaultResp(
335
                                 extended = new RNFetchBlobDefaultResp(
324
                                         RNFetchBlob.RCTContext,
336
                                         RNFetchBlob.RCTContext,
325
                                         taskId,
337
                                         taskId,
326
-                                        originalResponse.body());
338
+                                        originalResponse.body(),
339
+                                        options.increment);
327
                                 break;
340
                                 break;
328
                         }
341
                         }
329
                         return originalResponse.newBuilder().body(extended).build();
342
                         return originalResponse.newBuilder().body(extended).build();
333
                     }
346
                     }
334
                     catch (SocketTimeoutException e ){
347
                     catch (SocketTimeoutException e ){
335
                         timeout = true;
348
                         timeout = true;
349
+                        RNFetchBlobUtils.emitWarningEvent("RNFetchBlob error when sending request : " + e.getLocalizedMessage());
336
                     } catch(Exception ex) {
350
                     } catch(Exception ex) {
337
-                        RNFetchBlobUtils.emitWarningEvent("RNFetchBlob error when sending request : " + ex.getLocalizedMessage());
351
+
338
                     }
352
                     }
339
                     return chain.proceed(chain.request());
353
                     return chain.proceed(chain.request());
340
                 }
354
                 }
486
                     // and write response data to destination path.
500
                     // and write response data to destination path.
487
                     resp.body().bytes();
501
                     resp.body().bytes();
488
                 } catch (Exception ignored) {
502
                 } catch (Exception ignored) {
489
-                    ignored.printStackTrace();
503
+//                    ignored.printStackTrace();
490
                 }
504
                 }
491
                 this.destPath = this.destPath.replace("?append=true", "");
505
                 this.destPath = this.destPath.replace("?append=true", "");
492
                 callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
506
                 callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
613
                 DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
627
                 DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
614
                 dm.query(query);
628
                 dm.query(query);
615
                 Cursor c = dm.query(query);
629
                 Cursor c = dm.query(query);
630
+                String error = null;
616
                 String filePath = null;
631
                 String filePath = null;
617
                 // the file exists in media content database
632
                 // the file exists in media content database
618
                 if (c.moveToFirst()) {
633
                 if (c.moveToFirst()) {
623
                     if (cursor != null) {
638
                     if (cursor != null) {
624
                         cursor.moveToFirst();
639
                         cursor.moveToFirst();
625
                         filePath = cursor.getString(0);
640
                         filePath = cursor.getString(0);
626
-                        cursor.close();
627
-                        if(filePath != null) {
628
-                            this.callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, filePath);
629
-                            return;
630
-                        }
631
                     }
641
                     }
632
                 }
642
                 }
633
                 // When the file is not found in media content database, check if custom path exists
643
                 // When the file is not found in media content database, check if custom path exists
637
                         boolean exists = new File(customDest).exists();
647
                         boolean exists = new File(customDest).exists();
638
                         if(!exists)
648
                         if(!exists)
639
                             throw new Exception("Download manager download failed, the file does not downloaded to destination.");
649
                             throw new Exception("Download manager download failed, the file does not downloaded to destination.");
640
-                        callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, customDest);
650
+                        else
651
+                            this.callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, customDest);
641
 
652
 
642
                     } catch(Exception ex) {
653
                     } catch(Exception ex) {
643
-                        this.callback.invoke(ex.getLocalizedMessage(), null, null);
654
+                        error = ex.getLocalizedMessage();
644
                     }
655
                     }
645
                 }
656
                 }
646
-                else
647
-                    this.callback.invoke("Download manager could not resolve downloaded file path.", RNFetchBlobConst.RNFB_RESPONSE_PATH, null);
657
+                else {
658
+                    if(filePath == null)
659
+                        this.callback.invoke("Download manager could not resolve downloaded file path.", RNFetchBlobConst.RNFB_RESPONSE_PATH, null);
660
+                    else
661
+                        this.callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, filePath);
662
+                }
663
+
648
             }
664
             }
649
         }
665
         }
650
     }
666
     }

+ 11
- 1
src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobDefaultResp.java View File

9
 import com.facebook.react.modules.core.DeviceEventManagerModule;
9
 import com.facebook.react.modules.core.DeviceEventManagerModule;
10
 
10
 
11
 import java.io.IOException;
11
 import java.io.IOException;
12
+import java.nio.charset.Charset;
12
 
13
 
13
 import okhttp3.MediaType;
14
 import okhttp3.MediaType;
14
 import okhttp3.ResponseBody;
15
 import okhttp3.ResponseBody;
26
     String mTaskId;
27
     String mTaskId;
27
     ReactApplicationContext rctContext;
28
     ReactApplicationContext rctContext;
28
     ResponseBody originalBody;
29
     ResponseBody originalBody;
30
+    boolean isIncrement = false;
29
 
31
 
30
-    public RNFetchBlobDefaultResp(ReactApplicationContext ctx, String taskId, ResponseBody body) {
32
+    public RNFetchBlobDefaultResp(ReactApplicationContext ctx, String taskId, ResponseBody body, boolean isIncrement) {
31
         this.rctContext = ctx;
33
         this.rctContext = ctx;
32
         this.mTaskId = taskId;
34
         this.mTaskId = taskId;
33
         this.originalBody = body;
35
         this.originalBody = body;
36
+        this.isIncrement = isIncrement;
34
     }
37
     }
35
 
38
 
36
     @Override
39
     @Override
69
                 args.putString("taskId", mTaskId);
72
                 args.putString("taskId", mTaskId);
70
                 args.putString("written", String.valueOf(bytesRead));
73
                 args.putString("written", String.valueOf(bytesRead));
71
                 args.putString("total", String.valueOf(contentLength()));
74
                 args.putString("total", String.valueOf(contentLength()));
75
+                if(isIncrement) {
76
+                    args.putString("chunk", sink.readString(Charset.defaultCharset()));
77
+                }
78
+                else {
79
+                    args.putString("chunk", "");
80
+                }
81
+
72
                 rctContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
82
                 rctContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
73
                         .emit(RNFetchBlobConst.EVENT_PROGRESS, args);
83
                         .emit(RNFetchBlobConst.EVENT_PROGRESS, args);
74
             }
84
             }

+ 2
- 2
src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java View File

34
     ReactApplicationContext rctContext;
34
     ReactApplicationContext rctContext;
35
     FileOutputStream ofStream;
35
     FileOutputStream ofStream;
36
 
36
 
37
-    public RNFetchBlobFileResp(ReactApplicationContext ctx, String taskId, ResponseBody body, String path) throws IOException {
37
+    public RNFetchBlobFileResp(ReactApplicationContext ctx, String taskId, ResponseBody body, String path, boolean overwrite) throws IOException {
38
         super();
38
         super();
39
         this.rctContext = ctx;
39
         this.rctContext = ctx;
40
         this.mTaskId = taskId;
40
         this.mTaskId = taskId;
42
         assert path != null;
42
         assert path != null;
43
         this.mPath = path;
43
         this.mPath = path;
44
         if (path != null) {
44
         if (path != null) {
45
-            boolean appendToExistingFile = path.contains("?append=true");
45
+            boolean appendToExistingFile = !overwrite;
46
             path = path.replace("?append=true", "");
46
             path = path.replace("?append=true", "");
47
             mPath = path;
47
             mPath = path;
48
             File f = new File(path);
48
             File f = new File(path);

+ 53
- 0
src/android/src/main/java/com/RNFetchBlob/Utils/RNFBCookieJar.java View File

1
+package com.RNFetchBlob.Utils;
2
+
3
+import com.facebook.react.bridge.Arguments;
4
+import com.facebook.react.bridge.ReadableMap;
5
+import com.facebook.react.bridge.WritableArray;
6
+import com.facebook.react.bridge.WritableMap;
7
+
8
+import java.util.ArrayList;
9
+import java.util.HashMap;
10
+import java.util.List;
11
+
12
+import okhttp3.Cookie;
13
+import okhttp3.CookieJar;
14
+import okhttp3.HttpUrl;
15
+
16
+/**
17
+ * Created by wkh237on 2016/10/14.
18
+ */
19
+
20
+
21
+
22
+public class RNFBCookieJar implements CookieJar {
23
+
24
+    static final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
25
+    private List<Cookie> cookies;
26
+
27
+    @Override
28
+    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
29
+        cookieStore.put(url.host(), cookies);
30
+    }
31
+
32
+    @Override
33
+    public List<Cookie> loadForRequest(HttpUrl url) {
34
+        List<Cookie> cookies = cookieStore.get(url.host());
35
+        return cookies != null ? cookies : new ArrayList<Cookie>();
36
+    }
37
+
38
+    public static WritableArray getCookies(String host) {
39
+        HttpUrl url = HttpUrl.parse(host);
40
+        List<Cookie> cookies = null;
41
+        if(url != null) {
42
+            cookies = cookieStore.get(url.host());
43
+        }
44
+        WritableArray cookieList = Arguments.createArray();
45
+        if(cookies != null) {
46
+            for(Cookie c : cookies){
47
+                cookieList.pushString(c.toString());
48
+            }
49
+            return cookieList;
50
+        }
51
+        return null;
52
+    }
53
+}

+ 15
- 2
src/fs.js View File

29
     MovieDir : RNFetchBlob.MovieDir,
29
     MovieDir : RNFetchBlob.MovieDir,
30
     DownloadDir : RNFetchBlob.DownloadDir,
30
     DownloadDir : RNFetchBlob.DownloadDir,
31
     DCIMDir : RNFetchBlob.DCIMDir,
31
     DCIMDir : RNFetchBlob.DCIMDir,
32
-    SDCardDir : RNFetchBlob.SDCardDir
32
+    SDCardDir : RNFetchBlob.SDCardDir,
33
+    MainBundleDir : RNFetchBlob.MainBundleDir
33
 }
34
 }
34
 
35
 
35
 /**
36
 /**
338
 
339
 
339
 }
340
 }
340
 
341
 
342
+function df():Promise<{ free : number, total : number }> {
343
+  return new Promise((resolve, reject) => {
344
+    RNFetchBlob.df((err, stat) => {
345
+      if(err)
346
+        reject(err)
347
+      else
348
+        resolve(stat)
349
+    })
350
+  })
351
+}
352
+
341
 export default {
353
 export default {
342
   RNFetchBlobSession,
354
   RNFetchBlobSession,
343
   unlink,
355
   unlink,
359
   scanFile,
371
   scanFile,
360
   dirs,
372
   dirs,
361
   slice,
373
   slice,
362
-  asset
374
+  asset,
375
+  df
363
 }
376
 }

+ 124
- 5
src/index.js View File

9
   NativeAppEventEmitter,
9
   NativeAppEventEmitter,
10
   Platform,
10
   Platform,
11
   AsyncStorage,
11
   AsyncStorage,
12
+  AppState,
12
 } from 'react-native'
13
 } from 'react-native'
13
 import type {
14
 import type {
14
   RNFetchBlobNative,
15
   RNFetchBlobNative,
16
   RNFetchBlobStream,
17
   RNFetchBlobStream,
17
   RNFetchBlobResponseInfo
18
   RNFetchBlobResponseInfo
18
 } from './types'
19
 } from './types'
20
+import URIUtil from './utils/uri'
19
 import StatefulPromise from './class/StatefulPromise.js'
21
 import StatefulPromise from './class/StatefulPromise.js'
20
 import fs from './fs'
22
 import fs from './fs'
21
 import getUUID from './utils/uuid'
23
 import getUUID from './utils/uuid'
22
 import base64 from 'base-64'
24
 import base64 from 'base-64'
23
 import polyfill from './polyfill'
25
 import polyfill from './polyfill'
24
 import android from './android'
26
 import android from './android'
27
+import ios from './ios'
28
+import net from './net'
29
+import JSONStream from './json-stream'
25
 const {
30
 const {
26
   RNFetchBlobSession,
31
   RNFetchBlobSession,
27
   readStream,
32
   readStream,
38
   cp
43
   cp
39
 } = fs
44
 } = fs
40
 
45
 
41
-
42
 const Blob = polyfill.Blob
46
 const Blob = polyfill.Blob
43
 const emitter = DeviceEventEmitter
47
 const emitter = DeviceEventEmitter
44
-const RNFetchBlob:RNFetchBlobNative = NativeModules.RNFetchBlob
48
+const RNFetchBlob = NativeModules.RNFetchBlob
49
+
50
+// when app resumes, check if there's any expired network task and trigger
51
+// their .expire event
52
+if(Platform.OS === 'ios') {
53
+  AppState.addEventListener('change', (e) => {
54
+    console.log('app state changed', e)
55
+    if(e === 'active')
56
+      RNFetchBlob.emitExpiredEvent(()=>{})
57
+  })
58
+}
45
 
59
 
46
 // register message channel event handler.
60
 // register message channel event handler.
47
 emitter.addListener("RNFetchBlobMessage", (e) => {
61
 emitter.addListener("RNFetchBlobMessage", (e) => {
62
+
48
   if(e.event === 'warn') {
63
   if(e.event === 'warn') {
49
     console.warn(e.detail)
64
     console.warn(e.detail)
50
   }
65
   }
99
   return { fetch : fetch.bind(options) }
114
   return { fetch : fetch.bind(options) }
100
 }
115
 }
101
 
116
 
117
+/**
118
+ * Fetch from file system, use the same interface as RNFB.fetch
119
+ * @param  {RNFetchBlobConfig} [options={}] Fetch configurations
120
+ * @param  {string} method     Should be one of `get`, `post`, `put`
121
+ * @param  {string} url        A file URI string
122
+ * @param  {string} headers    Arguments of file system API
123
+ * @param  {any} body       Data to put or post to file systen.
124
+ * @return {Promise}
125
+ */
126
+function fetchFile(options = {}, method, url, headers = {}, body):Promise {
127
+
128
+  if(!URIUtil.isFileURI(url)) {
129
+    throw `could not fetch file from an invalid URI : ${url}`
130
+  }
131
+
132
+  url = URIUtil.unwrapFileURI(url)
133
+
134
+  let promise = null
135
+  let cursor = 0
136
+  let total = -1
137
+  let cacheData = ''
138
+  let info = null
139
+  let _progress, _uploadProgress, _stateChange
140
+
141
+  switch(method.toLowerCase()) {
142
+
143
+    case 'post':
144
+    break
145
+
146
+    case 'put':
147
+    break
148
+
149
+    // read data from file system
150
+    default:
151
+      promise = fs.stat(url)
152
+      .then((stat) => {
153
+        total = stat.size
154
+        return fs.readStream(url,
155
+          headers.encoding || 'utf8',
156
+          Math.floor(headers.bufferSize) || 409600,
157
+          Math.floor(headers.interval) || 100
158
+        )
159
+      })
160
+      .then((stream) => new Promise((resolve, reject) => {
161
+        stream.open()
162
+        info = {
163
+          state : "2",
164
+          headers : { 'source' : 'system-fs' },
165
+          status : 200,
166
+          respType : 'text',
167
+          rnfbEncode : headers.encoding || 'utf8'
168
+        }
169
+        _stateChange(info)
170
+        stream.onData((chunk) => {
171
+          _progress && _progress(cursor, total, chunk)
172
+          if(headers.noCache)
173
+            return
174
+          cacheData += chunk
175
+        })
176
+        stream.onError((err) => { reject(err) })
177
+        stream.onEnd(() => {
178
+          resolve(new FetchBlobResponse(null, info, cacheData))
179
+        })
180
+      }))
181
+    break
182
+  }
183
+
184
+  promise.progress = (fn) => {
185
+    _progress = fn
186
+    return promise
187
+  }
188
+  promise.stateChange = (fn) => {
189
+    _stateChange = fn
190
+    return promise
191
+  }
192
+  promise.uploadProgress = (fn) => {
193
+    _uploadProgress = fn
194
+    return promise
195
+  }
196
+
197
+  return promise
198
+}
199
+
102
 /**
200
 /**
103
  * Create a HTTP request by settings, the `this` context is a `RNFetchBlobConfig` object.
201
  * Create a HTTP request by settings, the `this` context is a `RNFetchBlobConfig` object.
104
  * @param  {string} method HTTP method, should be `GET`, `POST`, `PUT`, `DELETE`
202
  * @param  {string} method HTTP method, should be `GET`, `POST`, `PUT`, `DELETE`
118
   let options = this || {}
216
   let options = this || {}
119
   let subscription, subscriptionUpload, stateEvent, partEvent
217
   let subscription, subscriptionUpload, stateEvent, partEvent
120
   let respInfo = {}
218
   let respInfo = {}
219
+  let [method, url, headers, body] = [...args]
220
+
221
+  // fetch from file system
222
+  if(URIUtil.isFileURI(url)) {
223
+    return fetchFile(options, method, url, headers, body)
224
+  }
121
 
225
 
226
+  // from remote HTTP(S)
122
   let promise = new Promise((resolve, reject) => {
227
   let promise = new Promise((resolve, reject) => {
123
-    let [method, url, headers, body] = [...args]
124
     let nativeMethodName = Array.isArray(body) ? 'fetchBlobForm' : 'fetchBlob'
228
     let nativeMethodName = Array.isArray(body) ? 'fetchBlobForm' : 'fetchBlob'
125
 
229
 
126
     // on progress event listener
230
     // on progress event listener
127
     subscription = emitter.addListener('RNFetchBlobProgress', (e) => {
231
     subscription = emitter.addListener('RNFetchBlobProgress', (e) => {
128
       if(e.taskId === taskId && promise.onProgress) {
232
       if(e.taskId === taskId && promise.onProgress) {
129
-        promise.onProgress(e.written, e.total)
233
+        promise.onProgress(e.written, e.total, e.chunk)
130
       }
234
       }
131
     })
235
     })
132
 
236
 
143
       }
247
       }
144
     })
248
     })
145
 
249
 
250
+    subscription = emitter.addListener('RNFetchBlobExpire', (e) => {
251
+      console.log(e , 'EXPIRED!!')
252
+      if(e.taskId === taskId && promise.onExpire) {
253
+        promise.onExpire(e)
254
+      }
255
+    })
256
+
146
     partEvent = emitter.addListener('RNFetchBlobServerPush', (e) => {
257
     partEvent = emitter.addListener('RNFetchBlobServerPush', (e) => {
147
       if(e.taskId === taskId && promise.onPartData) {
258
       if(e.taskId === taskId && promise.onPartData) {
148
         promise.onPartData(e.chunk)
259
         promise.onPartData(e.chunk)
180
       delete promise['stateChange']
291
       delete promise['stateChange']
181
       delete promise['part']
292
       delete promise['part']
182
       delete promise['cancel']
293
       delete promise['cancel']
294
+      // delete promise['expire']
183
       promise.cancel = () => {}
295
       promise.cancel = () => {}
184
 
296
 
185
       if(err)
297
       if(err)
245
     promise.onStateChange = fn
357
     promise.onStateChange = fn
246
     return promise
358
     return promise
247
   }
359
   }
360
+  promise.expire = (fn) => {
361
+    promise.onExpire = fn
362
+    return promise
363
+  }
248
   promise.cancel = (fn) => {
364
   promise.cancel = (fn) => {
249
     fn = fn || function(){}
365
     fn = fn || function(){}
250
     subscription.remove()
366
     subscription.remove()
438
   fetch,
554
   fetch,
439
   base64,
555
   base64,
440
   android,
556
   android,
557
+  ios,
441
   config,
558
   config,
442
   session,
559
   session,
443
   fs,
560
   fs,
444
   wrap,
561
   wrap,
445
-  polyfill
562
+  net,
563
+  polyfill,
564
+  JSONStream
446
 }
565
 }

+ 44
- 0
src/ios.js View File

1
+// Copyright 2016 wkh237@github. All rights reserved.
2
+// Use of this source code is governed by a MIT-style license that can be
3
+// found in the LICENSE file.
4
+// @flow
5
+
6
+import {
7
+  NativeModules,
8
+  DeviceEventEmitter,
9
+  Platform,
10
+  NativeAppEventEmitter,
11
+} from 'react-native'
12
+
13
+const RNFetchBlob:RNFetchBlobNative = NativeModules.RNFetchBlob
14
+
15
+/**
16
+ * Open a file using UIDocumentInteractionController
17
+ * @param  {string]} path Path of the file to be open.
18
+ * @param  {string} scheme URI scheme that needs to support, optional
19
+ * @return {Promise}
20
+ */
21
+function previewDocument(path:string, scheme:string) {
22
+  if(Platform.OS === 'ios')
23
+    return RNFetchBlob.previewDocument('file://' + path, scheme)
24
+  else
25
+    return Promise.reject('RNFetchBlob.openDocument only supports IOS.')
26
+}
27
+
28
+/**
29
+ * Preview a file using UIDocumentInteractionController
30
+ * @param  {string]} path Path of the file to be open.
31
+ * @param  {string} scheme URI scheme that needs to support, optional
32
+ * @return {Promise}
33
+ */
34
+function openDocument(path:string, scheme:string) {
35
+  if(Platform.OS === 'ios')
36
+    return RNFetchBlob.openDocument('file://' + path, scheme)
37
+  else
38
+    return Promise.reject('RNFetchBlob.previewDocument only supports IOS.')
39
+}
40
+
41
+export default {
42
+  openDocument,
43
+  previewDocument
44
+}

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

198
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
198
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
199
 				GCC_WARN_UNUSED_FUNCTION = YES;
199
 				GCC_WARN_UNUSED_FUNCTION = YES;
200
 				GCC_WARN_UNUSED_VARIABLE = YES;
200
 				GCC_WARN_UNUSED_VARIABLE = YES;
201
-				IPHONEOS_DEPLOYMENT_TARGET = 7.0;
201
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
202
 				MTL_ENABLE_DEBUG_INFO = YES;
202
 				MTL_ENABLE_DEBUG_INFO = YES;
203
 				ONLY_ACTIVE_ARCH = YES;
203
 				ONLY_ACTIVE_ARCH = YES;
204
 				SDKROOT = iphoneos;
204
 				SDKROOT = iphoneos;
236
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
236
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
237
 				GCC_WARN_UNUSED_FUNCTION = YES;
237
 				GCC_WARN_UNUSED_FUNCTION = YES;
238
 				GCC_WARN_UNUSED_VARIABLE = YES;
238
 				GCC_WARN_UNUSED_VARIABLE = YES;
239
-				IPHONEOS_DEPLOYMENT_TARGET = 7.0;
239
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
240
 				MTL_ENABLE_DEBUG_INFO = NO;
240
 				MTL_ENABLE_DEBUG_INFO = NO;
241
 				SDKROOT = iphoneos;
241
 				SDKROOT = iphoneos;
242
 				VALIDATE_PRODUCT = YES;
242
 				VALIDATE_PRODUCT = YES;

+ 4
- 1
src/ios/RNFetchBlob/RNFetchBlob.h View File

7
 #ifndef RNFetchBlob_h
7
 #ifndef RNFetchBlob_h
8
 #define RNFetchBlob_h
8
 #define RNFetchBlob_h
9
 #import "RCTBridgeModule.h"
9
 #import "RCTBridgeModule.h"
10
+#import <UIKit/UIKit.h>
10
 
11
 
11
 
12
 
12
-@interface RNFetchBlob : NSObject <RCTBridgeModule> {
13
+@interface RNFetchBlob : NSObject <RCTBridgeModule, UIDocumentInteractionControllerDelegate> {
13
 
14
 
14
     NSString * filePathPrefix;
15
     NSString * filePathPrefix;
15
 
16
 
16
 }
17
 }
17
 
18
 
18
 @property (nonatomic) NSString * filePathPrefix;
19
 @property (nonatomic) NSString * filePathPrefix;
20
+@property (retain) UIDocumentInteractionController * documentController;
19
 
21
 
20
 + (RCTBridge *)getRCTBridge;
22
 + (RCTBridge *)getRCTBridge;
23
++ (void) checkExpiredSessions;
21
 
24
 
22
 @end
25
 @end
23
 
26
 

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

6
 
6
 
7
 #import "RNFetchBlob.h"
7
 #import "RNFetchBlob.h"
8
 #import "RCTLog.h"
8
 #import "RCTLog.h"
9
+#import "RCTRootView.h"
9
 #import "RCTBridge.h"
10
 #import "RCTBridge.h"
10
 #import "RCTEventDispatcher.h"
11
 #import "RCTEventDispatcher.h"
11
 #import "RNFetchBlobFS.h"
12
 #import "RNFetchBlobFS.h"
15
 #import "RNFetchBlobProgress.h"
16
 #import "RNFetchBlobProgress.h"
16
 
17
 
17
 
18
 
18
-RCTBridge * bridgeRef;
19
+__strong RCTBridge * bridgeRef;
19
 dispatch_queue_t commonTaskQueue;
20
 dispatch_queue_t commonTaskQueue;
20
 dispatch_queue_t fsQueue;
21
 dispatch_queue_t fsQueue;
21
 
22
 
30
 @implementation RNFetchBlob
31
 @implementation RNFetchBlob
31
 
32
 
32
 @synthesize filePathPrefix;
33
 @synthesize filePathPrefix;
34
+@synthesize documentController;
33
 @synthesize bridge = _bridge;
35
 @synthesize bridge = _bridge;
34
 
36
 
35
 - (dispatch_queue_t) methodQueue {
37
 - (dispatch_queue_t) methodQueue {
40
 
42
 
41
 + (RCTBridge *)getRCTBridge
43
 + (RCTBridge *)getRCTBridge
42
 {
44
 {
43
-    return bridgeRef;
45
+    RCTRootView * rootView = [[UIApplication sharedApplication] keyWindow].rootViewController.view;
46
+    return rootView.bridge;
44
 }
47
 }
45
 
48
 
46
 RCT_EXPORT_MODULE();
49
 RCT_EXPORT_MODULE();
58
         [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
61
         [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
59
     }
62
     }
60
     bridgeRef = _bridge;
63
     bridgeRef = _bridge;
64
+    [RNFetchBlobNetwork emitExpiredTasks];
61
     return self;
65
     return self;
62
 }
66
 }
63
 
67
 
64
 - (NSDictionary *)constantsToExport
68
 - (NSDictionary *)constantsToExport
65
 {
69
 {
66
     return @{
70
     return @{
71
+             @"MainBundleDir" : [RNFetchBlobFS getMainBundleDir],
67
              @"DocumentDir": [RNFetchBlobFS getDocumentDir],
72
              @"DocumentDir": [RNFetchBlobFS getDocumentDir],
68
              @"CacheDir" : [RNFetchBlobFS getCacheDir]
73
              @"CacheDir" : [RNFetchBlobFS getCacheDir]
69
              };
74
              };
87
 
92
 
88
 }
93
 }
89
 
94
 
95
+
90
 // Fetch blob data request
96
 // Fetch blob data request
91
 RCT_EXPORT_METHOD(fetchBlob:(NSDictionary *)options
97
 RCT_EXPORT_METHOD(fetchBlob:(NSDictionary *)options
92
                   taskId:(NSString *)taskId
98
                   taskId:(NSString *)taskId
432
     [RNFetchBlobFS slice:src dest:dest start:start end:end encode:@"" resolver:resolve rejecter:reject];
438
     [RNFetchBlobFS slice:src dest:dest start:start end:end encode:@"" resolver:resolve rejecter:reject];
433
 })
439
 })
434
 
440
 
435
-#pragma mark RNFetchBlob private methods
441
+RCT_EXPORT_METHOD(previewDocument:(NSString*)uri scheme:(NSString *)scheme resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
442
+{
443
+    
444
+    NSURL * url = [[NSURL alloc] initWithString:uri];
445
+    documentController = [UIDocumentInteractionController interactionControllerWithURL:url];
446
+    UIViewController *rootCtrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
447
+    documentController.delegate = self;
448
+    if(scheme == nil || [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:scheme]]) {
449
+        dispatch_sync(dispatch_get_main_queue(), ^{
450
+            [documentController  presentOptionsMenuFromRect:rootCtrl.view.bounds inView:rootCtrl.view animated:YES];
451
+        });
452
+        resolve(@[[NSNull null]]);
453
+    } else {
454
+        reject(@"RNFetchBlob could not open document", @"scheme is not supported", nil);
455
+    }
456
+})
457
+
458
+# pragma mark - open file with UIDocumentInteractionController and delegate
459
+
460
+RCT_EXPORT_METHOD(openDocument:(NSString*)uri scheme:(NSString *)scheme resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
461
+{
462
+    
463
+    NSURL * url = [[NSURL alloc] initWithString:uri];
464
+    documentController = [UIDocumentInteractionController interactionControllerWithURL:url];
465
+    documentController.delegate = self;
466
+    
467
+    if(scheme == nil || [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:scheme]]) {
468
+        dispatch_sync(dispatch_get_main_queue(), ^{
469
+            [documentController presentPreviewAnimated:YES];
470
+        });
471
+        resolve(@[[NSNull null]]);
472
+    } else {
473
+        reject(@"RNFetchBlob could not open document", @"scheme is not supported", nil);
474
+    }
475
+})
476
+
477
+RCT_EXPORT_METHOD(df:(RCTResponseSenderBlock)callback
478
+{
479
+    [RNFetchBlobFS df:callback];
480
+})
481
+
482
+- (UIViewController *) documentInteractionControllerViewControllerForPreview: (UIDocumentInteractionController *) controller {
483
+    UIWindow *window = [UIApplication sharedApplication].keyWindow;
484
+    return window.rootViewController;
485
+}
486
+
487
+# pragma mark - getCookies
488
+RCT_EXPORT_METHOD(getCookies:(NSString *)url resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
489
+{
490
+    resolve([RNFetchBlobNetwork getCookies:url]);
491
+})
492
+
493
+# pragma mark - check expired network events
494
+
495
+RCT_EXPORT_METHOD(emitExpiredEvent:(RCTResponseSenderBlock)callback
496
+{
497
+    [RNFetchBlobNetwork emitExpiredTasks];
498
+})
436
 
499
 
437
 
500
 
438
 @end
501
 @end

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

17
 extern NSString *const MSG_EVENT_WARN;
17
 extern NSString *const MSG_EVENT_WARN;
18
 extern NSString *const MSG_EVENT_ERROR;
18
 extern NSString *const MSG_EVENT_ERROR;
19
 
19
 
20
+extern NSString *const EVENT_EXPIRE;
20
 extern NSString *const EVENT_PROGRESS;
21
 extern NSString *const EVENT_PROGRESS;
21
 extern NSString *const EVENT_SERVER_PUSH;
22
 extern NSString *const EVENT_SERVER_PUSH;
22
 extern NSString *const EVENT_PROGRESS_UPLOAD;
23
 extern NSString *const EVENT_PROGRESS_UPLOAD;

+ 1
- 2
src/ios/RNFetchBlobConst.m View File

11
 extern NSString *const ASSET_PREFIX = @"bundle-assets://";
11
 extern NSString *const ASSET_PREFIX = @"bundle-assets://";
12
 extern NSString *const AL_PREFIX = @"assets-library://";
12
 extern NSString *const AL_PREFIX = @"assets-library://";
13
 
13
 
14
-
15
-
16
 // fetch configs
14
 // fetch configs
17
 extern NSString *const CONFIG_USE_TEMP = @"fileCache";
15
 extern NSString *const CONFIG_USE_TEMP = @"fileCache";
18
 extern NSString *const CONFIG_FILE_PATH = @"path";
16
 extern NSString *const CONFIG_FILE_PATH = @"path";
26
 extern NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush";
24
 extern NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush";
27
 extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
25
 extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
28
 extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
26
 extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
27
+extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";
29
 
28
 
30
 extern NSString *const MSG_EVENT = @"RNFetchBlobMessage";
29
 extern NSString *const MSG_EVENT = @"RNFetchBlobMessage";
31
 extern NSString *const MSG_EVENT_LOG = @"log";
30
 extern NSString *const MSG_EVENT_LOG = @"log";

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

39
 @property (nonatomic) BOOL appendData;
39
 @property (nonatomic) BOOL appendData;
40
 
40
 
41
 // get dirs
41
 // get dirs
42
++ (NSString *) getMainBundleDir;
42
 + (NSString *) getTempPath;
43
 + (NSString *) getTempPath;
43
 + (NSString *) getCacheDir;
44
 + (NSString *) getCacheDir;
44
 + (NSString *) getDocumentDir;
45
 + (NSString *) getDocumentDir;
65
 //+ (void) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append;
66
 //+ (void) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append;
66
 + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest;
67
 + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest;
67
 + (void) readStream:(NSString *)uri encoding:(NSString * )encoding bufferSize:(int)bufferSize tick:(int)tick streamId:(NSString *)streamId bridgeRef:(RCTBridge *)bridgeRef;
68
 + (void) readStream:(NSString *)uri encoding:(NSString * )encoding bufferSize:(int)bufferSize tick:(int)tick streamId:(NSString *)streamId bridgeRef:(RCTBridge *)bridgeRef;
69
++ (void) df:(RCTResponseSenderBlock)callback;
68
 
70
 
69
 // constructor
71
 // constructor
70
 - (id) init;
72
 - (id) init;
82
 - (void) closeInStream;
84
 - (void) closeInStream;
83
 - (void) closeOutStream;
85
 - (void) closeOutStream;
84
 
86
 
87
+- (void) openFile:( NSString * _Nonnull ) uri;
88
+
85
 @end
89
 @end
86
 
90
 
87
 #endif /* RNFetchBlobFS_h */
91
 #endif /* RNFetchBlobFS_h */

+ 37
- 5
src/ios/RNFetchBlobFS.m View File

24
 //  File system access methods
24
 //  File system access methods
25
 //
25
 //
26
 ////////////////////////////////////////
26
 ////////////////////////////////////////
27
-
27
+@interface RNFetchBlobFS() {
28
+    UIDocumentInteractionController * docCtrl;
29
+}
30
+@end
28
 @implementation RNFetchBlobFS
31
 @implementation RNFetchBlobFS
29
 
32
 
30
 
33
 
82
 
85
 
83
 #pragma mark - system directories
86
 #pragma mark - system directories
84
 
87
 
88
++ (NSString *) getMainBundleDir {
89
+    return [[NSBundle mainBundle] bundlePath];
90
+}
91
+
85
 + (NSString *) getCacheDir {
92
 + (NSString *) getCacheDir {
86
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
93
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
87
 }
94
 }
104
 
111
 
105
 + (NSString *) getTempPath {
112
 + (NSString *) getTempPath {
106
     
113
     
107
-    return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingString:@"/RNFetchBlob_tmp"];
114
+    return NSTemporaryDirectory();
108
 }
115
 }
109
 
116
 
110
 + (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext {
117
 + (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext {
409
             }
416
             }
410
             else
417
             else
411
             {
418
             {
412
-                BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path];
413
-                if(!exists) {
414
-                    reject(@"RNFetchBlobFS readFile error", @"file not exists", [[NSError alloc]init]);
419
+                if(![[NSFileManager defaultManager] fileExistsAtPath:path]) {
420
+                    
421
+                    reject(@"RNFetchBlobFS readFile error", @"file not exists", nil);
415
                     return;
422
                     return;
416
                 }
423
                 }
417
                 fileContent = [NSData dataWithContentsOfFile:path];
424
                 fileContent = [NSData dataWithContentsOfFile:path];
715
     }
722
     }
716
 }
723
 }
717
 
724
 
725
+#pragma mark - get disk space
726
+
727
++(void) df:(RCTResponseSenderBlock)callback
728
+{
729
+    uint64_t totalSpace = 0;
730
+    uint64_t totalFreeSpace = 0;
731
+    NSError *error = nil;
732
+    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
733
+    NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];
734
+    
735
+    if (dictionary) {
736
+        NSNumber *fileSystemSizeInBytes = [dictionary objectForKey: NSFileSystemSize];
737
+        NSNumber *freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize];
738
+        totalSpace = [fileSystemSizeInBytes unsignedLongLongValue];
739
+        totalFreeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue];
740
+        callback(@[[NSNull null], @{
741
+                  @"free" : [NSString stringWithFormat:@"%d", totalFreeSpace],
742
+                  @"total" : [NSString stringWithFormat:@"%d", totalSpace]
743
+                }]);
744
+    } else {
745
+        callback(@[@"failed to get storage usage."]);
746
+    }
747
+    
748
+}
749
+
718
 + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest
750
 + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest
719
 {
751
 {
720
     int read = 0;
752
     int read = 0;

+ 12
- 7
src/ios/RNFetchBlobNetwork.h View File

6
 //  Copyright © 2016 wkh237. All rights reserved.
6
 //  Copyright © 2016 wkh237. All rights reserved.
7
 //
7
 //
8
 
8
 
9
-#ifndef RNFetchBlobResp_h
10
-#define RNFetchBlobResp_h
9
+#ifndef RNFetchBlobNetwork_h
10
+#define RNFetchBlobNetwork_h
11
 
11
 
12
 #import <Foundation/Foundation.h>
12
 #import <Foundation/Foundation.h>
13
 #import "RCTBridgeModule.h"
13
 #import "RCTBridgeModule.h"
33
 @property (nullable, nonatomic) NSError * error;
33
 @property (nullable, nonatomic) NSError * error;
34
 
34
 
35
 
35
 
36
-- (nullable id) init;
37
-- (void) sendRequest;
38
-
39
 + (NSMutableDictionary  * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
36
 + (NSMutableDictionary  * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
40
 + (void) cancelRequest:(NSString *)taskId;
37
 + (void) cancelRequest:(NSString *)taskId;
38
++ (void) enableProgressReport:(NSString *) taskId;
39
++ (void) enableUploadProgress:(NSString *) taskId;
40
++ (void) emitExpiredTasks;
41
+
42
+- (nullable id) init;
43
+- (void) sendRequest;
44
+- (void) sendRequest:(NSDictionary  * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
41
 + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config;
45
 + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config;
42
 + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config;
46
 + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config;
43
-- (void) sendRequest:(NSDictionary  * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
47
++ (NSArray *) getCookies:(NSString *) url;
48
+
44
 
49
 
45
 
50
 
46
 @end
51
 @end
47
 
52
 
48
 
53
 
49
-#endif /* RNFetchBlobResp_h */
54
+#endif /* RNFetchBlobNetwork_h */

+ 165
- 61
src/ios/RNFetchBlobNetwork.m View File

9
 #import "RCTLog.h"
9
 #import "RCTLog.h"
10
 #import <Foundation/Foundation.h>
10
 #import <Foundation/Foundation.h>
11
 #import "RCTBridge.h"
11
 #import "RCTBridge.h"
12
+#import "RNFetchBlob.h"
12
 #import "RCTEventDispatcher.h"
13
 #import "RCTEventDispatcher.h"
13
 #import "RNFetchBlobFS.h"
14
 #import "RNFetchBlobFS.h"
15
+#import "RCTRootView.h"
14
 #import "RNFetchBlobNetwork.h"
16
 #import "RNFetchBlobNetwork.h"
15
 #import "RNFetchBlobConst.h"
17
 #import "RNFetchBlobConst.h"
16
 #import "RNFetchBlobReqBuilder.h"
18
 #import "RNFetchBlobReqBuilder.h"
25
 ////////////////////////////////////////
27
 ////////////////////////////////////////
26
 
28
 
27
 NSMapTable * taskTable;
29
 NSMapTable * taskTable;
30
+NSMapTable * expirationTable;
31
+NSMapTable * cookiesTable;
28
 NSMutableDictionary * progressTable;
32
 NSMutableDictionary * progressTable;
29
 NSMutableDictionary * uploadProgressTable;
33
 NSMutableDictionary * uploadProgressTable;
30
 
34
 
39
 {
43
 {
40
     BOOL * respFile;
44
     BOOL * respFile;
41
     BOOL isNewPart;
45
     BOOL isNewPart;
46
+    BOOL * isIncrement;
42
     NSMutableData * partBuffer;
47
     NSMutableData * partBuffer;
43
     NSString * destPath;
48
     NSString * destPath;
44
     NSOutputStream * writeStream;
49
     NSOutputStream * writeStream;
73
         taskQueue = [[NSOperationQueue alloc] init];
78
         taskQueue = [[NSOperationQueue alloc] init];
74
         taskQueue.maxConcurrentOperationCount = 10;
79
         taskQueue.maxConcurrentOperationCount = 10;
75
     }
80
     }
76
-    if(taskTable == nil) {
81
+    if(expirationTable == nil)
82
+    {
83
+        expirationTable = [[NSMapTable alloc] init];
84
+    }
85
+    if(taskTable == nil)
86
+    {
77
         taskTable = [[NSMapTable alloc] init];
87
         taskTable = [[NSMapTable alloc] init];
78
     }
88
     }
79
     if(progressTable == nil)
89
     if(progressTable == nil)
84
     {
94
     {
85
         uploadProgressTable = [[NSMutableDictionary alloc] init];
95
         uploadProgressTable = [[NSMutableDictionary alloc] init];
86
     }
96
     }
97
+    if(cookiesTable == nil)
98
+    {
99
+        cookiesTable = [[NSMapTable alloc] init];
100
+    }
87
     return self;
101
     return self;
88
 }
102
 }
89
 
103
 
104
++ (NSArray *) getCookies:(NSString *) url
105
+{
106
+    NSString * hostname = [[NSURL URLWithString:url] host];
107
+    NSMutableArray * cookies = [NSMutableArray new];
108
+    NSArray * list = [cookiesTable objectForKey:hostname];
109
+    for(NSHTTPCookie * cookie in list)
110
+    {
111
+        NSMutableString * cookieStr = [[NSMutableString alloc] init];
112
+        [cookieStr appendString:cookie.name];
113
+        [cookieStr appendString:@"="];
114
+        [cookieStr appendString:cookie.value];
115
+        
116
+        if(cookie.expiresDate == nil) {
117
+            [cookieStr appendString:@"; max-age=0"];
118
+        }
119
+        else {
120
+            [cookieStr appendString:@"; expires="];
121
+            NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
122
+            [dateFormatter setDateFormat:@"EEE, dd MM yyyy HH:mm:ss ZZZ"];
123
+            NSString *strDate = [dateFormatter stringFromDate:cookie.expiresDate];
124
+            [cookieStr appendString:strDate];
125
+        }
126
+        
127
+        
128
+        [cookieStr appendString:@"; domain="];
129
+        [cookieStr appendString:hostname];
130
+        [cookieStr appendString:@"; path="];
131
+        [cookieStr appendString:cookie.path];
132
+        
133
+        
134
+        if (cookie.isSecure) {
135
+            [cookieStr appendString:@"; secure"];
136
+        }
137
+        
138
+        if (cookie.isHTTPOnly) {
139
+            [cookieStr appendString:@"; httponly"];
140
+        }
141
+        [cookies addObject:cookieStr];
142
+    }
143
+    return cookies;
144
+}
145
+
90
 + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config
146
 + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config
91
 {
147
 {
92
     [progressTable setValue:config forKey:taskId];
148
     [progressTable setValue:config forKey:taskId];
136
     self.expectedBytes = 0;
192
     self.expectedBytes = 0;
137
     self.receivedBytes = 0;
193
     self.receivedBytes = 0;
138
     self.options = options;
194
     self.options = options;
195
+    isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
139
     redirects = [[NSMutableArray alloc] init];
196
     redirects = [[NSMutableArray alloc] init];
140
-    [redirects addObject:req.URL.absoluteString];
141
-    
197
+    if(req.URL != nil)
198
+        [redirects addObject:req.URL.absoluteString];
199
+
142
     // set response format
200
     // set response format
143
     NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
201
     NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
144
     if([[rnfbResp lowercaseString] isEqualToString:@"base64"])
202
     if([[rnfbResp lowercaseString] isEqualToString:@"base64"])
156
     bodyLength = contentLength;
214
     bodyLength = contentLength;
157
 
215
 
158
     // the session trust any SSL certification
216
     // the session trust any SSL certification
159
-    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
217
+//    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
218
+    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
219
+
160
     // set request timeout
220
     // set request timeout
161
     float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
221
     float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
162
     if(timeout > 0)
222
     if(timeout > 0)
193
         respData = [[NSMutableData alloc] init];
253
         respData = [[NSMutableData alloc] init];
194
         respFile = NO;
254
         respFile = NO;
195
     }
255
     }
196
-    NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
256
+
257
+    __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
197
     [taskTable setObject:task forKey:taskId];
258
     [taskTable setObject:task forKey:taskId];
198
     [task resume];
259
     [task resume];
199
 
260
 
200
     // network status indicator
261
     // network status indicator
201
     if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
262
     if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
202
         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
263
         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
264
+    __block UIApplication * app = [UIApplication sharedApplication];
265
+
266
+    // #115 handling task expired when application entering backgound for a long time
267
+    UIBackgroundTaskIdentifier tid = [app beginBackgroundTaskWithName:taskId expirationHandler:^{
268
+        NSLog([NSString stringWithFormat:@"session %@ expired", taskId ]);
269
+        [expirationTable setObject:task forKey:taskId];
270
+        [app endBackgroundTask:tid];
271
+    }];
272
+
273
+
274
+}
275
+
276
+// #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
277
++ (void) emitExpiredTasks
278
+{
279
+    NSEnumerator * emu =  [expirationTable keyEnumerator];
280
+    NSString * key;
281
+
282
+    while((key = [emu nextObject]))
283
+    {
284
+        RCTBridge * bridge = [RNFetchBlob getRCTBridge];
285
+        NSData * args = @{ @"taskId": key };
286
+        [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args];
287
+
288
+    }
289
+    
290
+    // clear expired task entries
291
+    [expirationTable removeAllObjects];
292
+    expirationTable = [[NSMapTable alloc] init];
293
+
203
 }
294
 }
204
 
295
 
205
 ////////////////////////////////////////
296
 ////////////////////////////////////////
211
 
302
 
212
 #pragma mark NSURLSession delegate methods
303
 #pragma mark NSURLSession delegate methods
213
 
304
 
305
+
306
+#pragma mark - Received Response
214
 // set expected content length on response received
307
 // set expected content length on response received
215
 - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
308
 - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
216
 {
309
 {
287
                      @"redirects": redirects,
380
                      @"redirects": redirects,
288
                      @"respType" : respType,
381
                      @"respType" : respType,
289
                      @"timeout" : @NO,
382
                      @"timeout" : @NO,
290
-                     @"status": [NSString stringWithFormat:@"%d", statusCode ]
383
+                     @"status": [NSNumber numberWithInteger:statusCode]
291
                     };
384
                     };
292
 
385
 
386
+#pragma mark - handling cookies
387
+        // # 153 get cookies
388
+        if(response.URL != nil)
389
+        {
390
+            NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
391
+            if(cookies != nil && [cookies count] > 0) {
392
+                [cookiesTable setObject:cookies forKey:response.URL.host];
393
+            }
394
+        }
395
+        
293
         [self.bridge.eventDispatcher
396
         [self.bridge.eventDispatcher
294
          sendDeviceEventWithName: EVENT_STATE_CHANGE
397
          sendDeviceEventWithName: EVENT_STATE_CHANGE
295
          body:respInfo
398
          body:respInfo
296
         ];
399
         ];
297
         headers = nil;
400
         headers = nil;
298
         respInfo = nil;
401
         respInfo = nil;
402
+
299
     }
403
     }
300
     else
404
     else
301
         NSLog(@"oops");
405
         NSLog(@"oops");
309
             {
413
             {
310
                 [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
414
                 [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
311
             }
415
             }
416
+            BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
312
             BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"];
417
             BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"];
418
+            
419
+            appendToExistingFile = !overwrite;
420
+
313
             // For solving #141 append response data if the file already exists
421
             // For solving #141 append response data if the file already exists
314
             // base on PR#139 @kejinliang
422
             // base on PR#139 @kejinliang
315
             if(appendToExistingFile)
423
             if(appendToExistingFile)
320
             {
428
             {
321
                 [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
429
                 [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
322
             }
430
             }
323
-            writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:YES];
431
+            writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
324
             [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
432
             [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
325
             [writeStream open];
433
             [writeStream open];
326
         }
434
         }
343
         [partBuffer appendData:data];
451
         [partBuffer appendData:data];
344
         return ;
452
         return ;
345
     }
453
     }
346
-    
454
+
347
     NSNumber * received = [NSNumber numberWithLong:[data length]];
455
     NSNumber * received = [NSNumber numberWithLong:[data length]];
348
     receivedBytes += [received longValue];
456
     receivedBytes += [received longValue];
457
+    NSString * chunkString = @"";
458
+
459
+    if(isIncrement == YES)
460
+    {
461
+        chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
462
+    }
463
+
349
     if(respFile == NO)
464
     if(respFile == NO)
350
     {
465
     {
351
         [respData appendData:data];
466
         [respData appendData:data];
365
          body:@{
480
          body:@{
366
                 @"taskId": taskId,
481
                 @"taskId": taskId,
367
                 @"written": [NSString stringWithFormat:@"%d", receivedBytes],
482
                 @"written": [NSString stringWithFormat:@"%d", receivedBytes],
368
-                @"total": [NSString stringWithFormat:@"%d", expectedBytes]
483
+                @"total": [NSString stringWithFormat:@"%d", expectedBytes],
484
+                @"chunk": chunkString
369
             }
485
             }
370
          ];
486
          ];
371
     }
487
     }
399
     {
515
     {
400
         errMsg = [error localizedDescription];
516
         errMsg = [error localizedDescription];
401
     }
517
     }
402
-    else
518
+
519
+    if(respFile == YES)
403
     {
520
     {
404
-        if(respFile == YES)
521
+        [writeStream close];
522
+        rnfbRespType = RESP_TYPE_PATH;
523
+        respStr = destPath;
524
+    }
525
+    // base64 response
526
+    else {
527
+        // #73 fix unicode data encoding issue :
528
+        // when response type is BASE64, we should first try to encode the response data to UTF8 format
529
+        // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
530
+        // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
531
+        NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
532
+        
533
+        if(responseFormat == BASE64)
405
         {
534
         {
406
-            [writeStream close];
407
-            rnfbRespType = RESP_TYPE_PATH;
408
-            respStr = destPath;
535
+            rnfbRespType = RESP_TYPE_BASE64;
536
+            respStr = [respData base64EncodedStringWithOptions:0];
409
         }
537
         }
410
-        // base64 response
411
-        else {
412
-            // #73 fix unicode data encoding issue :
413
-            // when response type is BASE64, we should first try to encode the response data to UTF8 format
414
-            // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
415
-            // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
416
-            NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
417
-            
418
-            if(responseFormat == BASE64)
419
-            {
420
-                rnfbRespType = RESP_TYPE_BASE64;
421
-                respStr = [respData base64EncodedStringWithOptions:0];
422
-            }
423
-            else if (responseFormat == UTF8)
538
+        else if (responseFormat == UTF8)
539
+        {
540
+            rnfbRespType = RESP_TYPE_UTF8;
541
+            respStr = utf8;
542
+        }
543
+        else
544
+        {
545
+            if(utf8 != nil)
424
             {
546
             {
425
                 rnfbRespType = RESP_TYPE_UTF8;
547
                 rnfbRespType = RESP_TYPE_UTF8;
426
                 respStr = utf8;
548
                 respStr = utf8;
427
             }
549
             }
428
             else
550
             else
429
             {
551
             {
430
-                if(utf8 != nil)
431
-                {
432
-                    rnfbRespType = RESP_TYPE_UTF8;
433
-                    respStr = utf8;
434
-                }
435
-                else
436
-                {
437
-                    rnfbRespType = RESP_TYPE_BASE64;
438
-                    respStr = [respData base64EncodedStringWithOptions:0];
439
-                }
552
+                rnfbRespType = RESP_TYPE_BASE64;
553
+                respStr = [respData base64EncodedStringWithOptions:0];
440
             }
554
             }
441
         }
555
         }
442
-    }
556
+        }
557
+
443
 
558
 
444
     callback(@[ errMsg, rnfbRespType, respStr]);
559
     callback(@[ errMsg, rnfbRespType, respStr]);
445
 
560
 
488
 
603
 
489
 - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
604
 - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
490
 {
605
 {
491
-    if([options valueForKey:CONFIG_TRUSTY] != nil)
606
+    BOOL trusty = [options valueForKey:CONFIG_TRUSTY];
607
+    if(!trusty)
492
     {
608
     {
493
         completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
609
         completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
494
     }
610
     }
495
     else
611
     else
496
     {
612
     {
497
-        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
498
-        __block NSURLCredential *credential = nil;
499
-        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
500
-        {
501
-            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
502
-            if (credential) {
503
-                disposition = NSURLSessionAuthChallengeUseCredential;
504
-            } else {
505
-                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
506
-            }
507
-        }
508
-        else
509
-        {
510
-            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
511
-            RCTLogWarn(@"counld not create connection with an unstrusted SSL certification, if you're going to create connection anyway, add `trusty:true` to RNFetchBlob.config");
512
-            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
513
-        }
514
-        if (completionHandler) {
515
-            completionHandler(disposition, credential);
516
-        }
613
+        completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
517
     }
614
     }
518
 }
615
 }
519
 
616
 
617
+
618
+- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
619
+{
620
+    NSLog(@"sess done in background");
621
+}
622
+
520
 - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
623
 - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
521
 {
624
 {
522
-    [redirects addObject:[request.URL absoluteString]];
625
+    if(request.URL != nil)
626
+        [redirects addObject:[request.URL absoluteString]];
523
     completionHandler(request);
627
     completionHandler(request);
524
 }
628
 }
525
 
629
 

+ 2
- 2
src/ios/RNFetchBlobProgress.m View File

32
 
32
 
33
 -(BOOL)shouldReport:(NSNumber *)nextProgress
33
 -(BOOL)shouldReport:(NSNumber *)nextProgress
34
 {
34
 {
35
-    BOOL result = YES;
35
+    BOOL * result = YES;
36
     float countF = [self.count floatValue];
36
     float countF = [self.count floatValue];
37
     if(countF > 0 && [nextProgress floatValue] > 0)
37
     if(countF > 0 && [nextProgress floatValue] > 0)
38
     {
38
     {
43
     // NSTimeInterval is defined as double
43
     // NSTimeInterval is defined as double
44
     NSNumber *timeStampObj = [NSNumber numberWithDouble: timeStamp];
44
     NSNumber *timeStampObj = [NSNumber numberWithDouble: timeStamp];
45
     float delta = [timeStampObj doubleValue] - lastTick;
45
     float delta = [timeStampObj doubleValue] - lastTick;
46
-    BOOL shouldReport = delta > [self.interval doubleValue] && self.enable && result;
46
+    BOOL * shouldReport = delta > [self.interval doubleValue] && self.enable && result;
47
     if(shouldReport)
47
     if(shouldReport)
48
     {
48
     {
49
         tick++;
49
         tick++;

+ 39
- 0
src/json-stream.js View File

1
+import Oboe from './lib/oboe-browser.min.js'
2
+import XMLHttpRequest from './polyfill/XMLHttpRequest'
3
+import URIUtil from './utils/uri'
4
+
5
+const OboeExtended = (arg: string | object) => {
6
+
7
+
8
+  window.location = ''
9
+
10
+  if(!window.XMLHttpRequest.isRNFBPolyfill ) {
11
+    window.XMLHttpRequest = XMLHttpRequest
12
+    console.warn('Use JSONStream will automatically replace window.XMLHttpRequest with RNFetchBlob.polyfill.XMLHttpRequest. You are seeing this warning because you did not replace it maually.')
13
+  }
14
+
15
+  if(typeof arg === 'string') {
16
+    if(URIUtil.isFileURI(arg)) {
17
+      arg = {
18
+        url : 'JSONStream://' + arg,
19
+        headers : { noCache : true }
20
+      }
21
+    }
22
+    else
23
+      arg = 'JSONStream://' + arg
24
+
25
+  }
26
+  else if(typeof arg === 'object') {
27
+    let headers = arg.headers || {}
28
+    if(URIUtil.isFileURI(arg.url)) {
29
+      headers.noCache = true
30
+    }
31
+    arg = Object.assign(arg, {
32
+      url : 'JSONStream://' + arg.url,
33
+      headers
34
+    })
35
+  }
36
+  return Oboe(arg)
37
+}
38
+
39
+export default OboeExtended

+ 2703
- 0
src/lib/oboe-browser.js
File diff suppressed because it is too large
View File


+ 1
- 0
src/lib/oboe-browser.min.js
File diff suppressed because it is too large
View File


+ 26
- 0
src/net.js View File

1
+// Copyright 2016 wkh237@github. All rights reserved.
2
+// Use of this source code is governed by a MIT-style license that can be
3
+// found in the LICENSE file.
4
+// @flow
5
+
6
+import {
7
+  NativeModules,
8
+  DeviceEventEmitter,
9
+  Platform,
10
+  NativeAppEventEmitter,
11
+} from 'react-native'
12
+
13
+const RNFetchBlob = NativeModules.RNFetchBlob
14
+
15
+/**
16
+ * Get cookie according to the given url.
17
+ * @param  {string} url HTTP URL string.
18
+ * @return {Promise<Array<String>>}     Cookies of a specific domain.
19
+ */
20
+function getCookies(url:string):Promise<Array<String>> {
21
+  return RNFetchBlob.getCookies(url)
22
+}
23
+
24
+export default {
25
+  getCookies
26
+}

+ 21
- 4
src/package.json View File

1
 {
1
 {
2
   "name": "react-native-fetch-blob",
2
   "name": "react-native-fetch-blob",
3
-  "version": "0.9.6",
3
+  "version": "0.10.0",
4
   "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.",
4
   "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.",
5
   "main": "index.js",
5
   "main": "index.js",
6
   "scripts": {
6
   "scripts": {
29
   "repository": {
29
   "repository": {
30
     "url": "https://github.com/wkh237/react-native-fetch-blob.git"
30
     "url": "https://github.com/wkh237/react-native-fetch-blob.git"
31
   },
31
   },
32
-  "author": "wkh237",
33
-  "license": "MIT"
34
-}
32
+  "author": "wkh237 <xeiyan@gmail.com>",
33
+  "license": "MIT",
34
+  "contributors": [
35
+    "Andreas Amsenius <andreas@amsenius.se>",
36
+    "Corentin Smith <corentin.smith@gmail.com>",
37
+    "Dmitry Petukhov <dmitryvpetukhov@gmail.com>",
38
+    "Erik Smartt <code@eriksmartt.com>",
39
+    "Evgeniy Baraniuk <ev.baraniuk@gmail.com>",
40
+    "Juan B. Rodriguez <jbrodriguez@gmail.com>",
41
+    "Kaishley <kklingachetti@msn.com>",
42
+    "Mike Monteith <mike@mikemonteith.com>",
43
+    "Nguyen Cao Nhat Linh <nhatlinh95@gmail.com>",
44
+    "Tim Suchanek <tim.suchanek@gmail.com>",
45
+    "follower <github@rancidbacon.com>",
46
+    "francisco-sanchez-molina <psm1984@gmail.com>",
47
+    "kejinliang <kejinliang@users.noreply.github.com>",
48
+    "smartt <github@eriksmartt.com>",
49
+    ""
50
+  ]
51
+}

+ 1
- 1
src/polyfill/Fetch.js View File

47
         // When request body is a Blob, use file URI of the Blob as request body.
47
         // When request body is a Blob, use file URI of the Blob as request body.
48
         else if (body.isRNFetchBlobPolyfill)
48
         else if (body.isRNFetchBlobPolyfill)
49
           promise = Promise.resolve(RNFetchBlob.wrap(body.blobPath))
49
           promise = Promise.resolve(RNFetchBlob.wrap(body.blobPath))
50
-        else if (typeof body !== 'object')
50
+        else if (typeof body !== 'object' && options.headers['Content-Type'] !== 'application/json')
51
           promise = Promise.resolve(JSON.stringify(body))
51
           promise = Promise.resolve(JSON.stringify(body))
52
         else if (typeof body !== 'string')
52
         else if (typeof body !== 'string')
53
           promise = Promise.resolve(body.toString())
53
           promise = Promise.resolve(body.toString())

+ 17
- 3
src/polyfill/XMLHttpRequest.js View File

7
 import Log from '../utils/log.js'
7
 import Log from '../utils/log.js'
8
 import Blob from './Blob.js'
8
 import Blob from './Blob.js'
9
 import ProgressEvent from './ProgressEvent.js'
9
 import ProgressEvent from './ProgressEvent.js'
10
+import URIUtil from '../utils/uri'
10
 
11
 
11
 const log = new Log('XMLHttpRequest')
12
 const log = new Log('XMLHttpRequest')
12
 
13
 
30
 
31
 
31
   // readonly
32
   // readonly
32
   _readyState : number = UNSENT;
33
   _readyState : number = UNSENT;
34
+  _uriType : 'net' | 'file' = 'net';
33
   _response : any = '';
35
   _response : any = '';
34
-  _responseText : any = null;
36
+  _responseText : any = '';
35
   _responseHeaders : any = {};
37
   _responseHeaders : any = {};
36
   _responseType : '' | 'arraybuffer' | 'blob'  | 'json' | 'text' = '';
38
   _responseType : '' | 'arraybuffer' | 'blob'  | 'json' | 'text' = '';
37
   // TODO : not suppoted ATM
39
   // TODO : not suppoted ATM
42
   _timeout : number = 60000;
44
   _timeout : number = 60000;
43
   _sendFlag : boolean = false;
45
   _sendFlag : boolean = false;
44
   _uploadStarted : boolean = false;
46
   _uploadStarted : boolean = false;
47
+  _increment : boolean = false;
45
 
48
 
46
   // RNFetchBlob compatible data structure
49
   // RNFetchBlob compatible data structure
47
   _config : RNFetchBlobConfig = {};
50
   _config : RNFetchBlobConfig = {};
129
     this._method = method
132
     this._method = method
130
     this._url = url
133
     this._url = url
131
     this._headers = {}
134
     this._headers = {}
135
+    this._increment = URIUtil.isJSONStreamURI(this._url)
136
+    this._url = this._url.replace(/^JSONStream\:\/\//, '')
132
     this._dispatchReadStateChange(XMLHttpRequest.OPENED)
137
     this._dispatchReadStateChange(XMLHttpRequest.OPENED)
133
   }
138
   }
134
 
139
 
137
    * @param  {any} body Body in RNfetchblob flavor
142
    * @param  {any} body Body in RNfetchblob flavor
138
    */
143
    */
139
   send(body) {
144
   send(body) {
145
+
140
     this._body = body
146
     this._body = body
147
+
141
     if(this._readyState !== XMLHttpRequest.OPENED)
148
     if(this._readyState !== XMLHttpRequest.OPENED)
142
       throw 'InvalidStateError : XMLHttpRequest is not opened yet.'
149
       throw 'InvalidStateError : XMLHttpRequest is not opened yet.'
143
     let promise = Promise.resolve()
150
     let promise = Promise.resolve()
171
       for(let h in _headers) {
178
       for(let h in _headers) {
172
         _headers[h] = _headers[h].toString()
179
         _headers[h] = _headers[h].toString()
173
       }
180
       }
181
+
174
       this._task = RNFetchBlob
182
       this._task = RNFetchBlob
175
                     .config({
183
                     .config({
176
                       auto: true,
184
                       auto: true,
177
                       timeout : this._timeout,
185
                       timeout : this._timeout,
186
+                      increment : this._increment,
178
                       binaryContentTypes : XMLHttpRequest.binaryContentTypes
187
                       binaryContentTypes : XMLHttpRequest.binaryContentTypes
179
                     })
188
                     })
180
                     .fetch(_method, _url, _headers, body)
189
                     .fetch(_method, _url, _headers, body)
184
           .progress(this._progressEvent.bind(this))
193
           .progress(this._progressEvent.bind(this))
185
           .catch(this._onError.bind(this))
194
           .catch(this._onError.bind(this))
186
           .then(this._onDone.bind(this))
195
           .then(this._onDone.bind(this))
196
+
187
     })
197
     })
188
   }
198
   }
189
 
199
 
277
     this.upload.dispatchEvent('progress', new ProgressEvent(true, send, total))
287
     this.upload.dispatchEvent('progress', new ProgressEvent(true, send, total))
278
   }
288
   }
279
 
289
 
280
-  _progressEvent(send:number, total:number) {
290
+  _progressEvent(send:number, total:number, chunk:string) {
281
     log.verbose(this.readyState)
291
     log.verbose(this.readyState)
282
     if(this._readyState === XMLHttpRequest.HEADERS_RECEIVED)
292
     if(this._readyState === XMLHttpRequest.HEADERS_RECEIVED)
283
       this._dispatchReadStateChange(XMLHttpRequest.LOADING)
293
       this._dispatchReadStateChange(XMLHttpRequest.LOADING)
285
     if(total && total >= 0)
295
     if(total && total >= 0)
286
         lengthComputable = true
296
         lengthComputable = true
287
     let e = new ProgressEvent(lengthComputable, send, total)
297
     let e = new ProgressEvent(lengthComputable, send, total)
298
+
299
+    if(this._increment) {
300
+      this._responseText += chunk
301
+    }
288
     this.dispatchEvent('progress', e)
302
     this.dispatchEvent('progress', e)
289
   }
303
   }
290
 
304
 
417
     return this._responseType
431
     return this._responseType
418
   }
432
   }
419
 
433
 
420
-  get isRNFBPolyfill() {
434
+  static get isRNFBPolyfill() {
421
     return true
435
     return true
422
   }
436
   }
423
 
437
 

+ 2
- 2
src/react-native-fetch-blob.podspec View File

1
 Pod::Spec.new do |s|
1
 Pod::Spec.new do |s|
2
   s.name             = "react-native-fetch-blob"
2
   s.name             = "react-native-fetch-blob"
3
-  s.version          = "0.9.6"
3
+  s.version          = "0.10.0"
4
   s.summary          = "A project committed to make file acess and data transfer easier, effiecient for React Native developers."
4
   s.summary          = "A project committed to make file acess and data transfer easier, effiecient for React Native developers."
5
   s.requires_arc = true
5
   s.requires_arc = true
6
   s.license      = 'MIT'
6
   s.license      = 'MIT'
7
   s.homepage     = 'n/a'
7
   s.homepage     = 'n/a'
8
   s.authors      = { "wkh237" => "xeiyan@gmail.com" }
8
   s.authors      = { "wkh237" => "xeiyan@gmail.com" }
9
-  s.source       = { :git => "https://github.com/wkh237/react-native-fetch-blob", :tag => 'v0.9.6'}
9
+  s.source       = { :git => "https://github.com/wkh237/react-native-fetch-blob", :tag => 'v0.10.0'}
10
   s.source_files = 'ios/**/*.{h,m}'
10
   s.source_files = 'ios/**/*.{h,m}'
11
   s.platform     = :ios, "7.0"
11
   s.platform     = :ios, "7.0"
12
   s.dependency 'React/Core'
12
   s.dependency 'React/Core'

+ 28
- 0
src/utils/uri.js View File

1
+export default {
2
+
3
+  isFileURI : (uri:string):boolean => {
4
+    if(typeof uri !== 'string')
5
+      return false
6
+    return /^RNFetchBlob-file\:\/\//.test(uri)
7
+  },
8
+
9
+  isJSONStreamURI : (uri:string):boolean => {
10
+    if(typeof uri !== 'string')
11
+      return false
12
+    return /^JSONStream\:\/\//.test(uri)
13
+  },
14
+
15
+  removeURIScheme : (uri:string, iterations:number):string => {
16
+    iterations = iterations || 1
17
+    let result = uri
18
+    for(let i=0;i<iterations;i++) {
19
+      result = String(result).replace(/^[^\:]+\:\/\//, '')
20
+    }
21
+    return String(result)
22
+  },
23
+
24
+  unwrapFileURI : (uri:string):string => {
25
+    return String(uri).replace(/^RNFetchBlob-file\:\/\//, '')
26
+  }
27
+
28
+}

+ 3
- 0
test-server/public/json-dummy-1.json View File

1
+{
2
+  "name" : "fetchblob-dev"
3
+}

+ 48331
- 15
test-server/public/json-dummy.json
File diff suppressed because it is too large
View File


+ 36
- 12
test-server/server.js View File

66
   next();
66
   next();
67
 })
67
 })
68
 
68
 
69
+
70
+app.get('/10s-download', (req,res) => {
71
+  var count = 0
72
+  var data = ''
73
+  for(var i =0;i<1024000;i++)
74
+    data += '1'
75
+  res.set('Contet-Length', 1024000*10)
76
+  var it = setInterval(() => {
77
+    res.write(data)
78
+    count++
79
+    if(count == 10) {
80
+      clearInterval(it)
81
+      res.end()
82
+    }
83
+  }, 1000)
84
+})
85
+
69
 app.get('/video/:count', (req, res) => {
86
 app.get('/video/:count', (req, res) => {
70
   var count = 0
87
   var count = 0
71
   res.set('Content-Type', 'multipart/x-mixed-replace; boundary="---osclivepreview---"')
88
   res.set('Content-Type', 'multipart/x-mixed-replace; boundary="---osclivepreview---"')
191
   }, 5000)
208
   }, 5000)
192
 })
209
 })
193
 
210
 
194
-app.get('/10s-download', (req,res) => {
195
-  var count = 0
196
-  var data = ''
197
-  for(var i =0;i<1024000;i++)
198
-    data += '1'
199
-  res.set('Contet-Length', 1024000*10)
211
+app.all('/long/:ticks', (req, res) => {
212
+  var count = 0;
200
   var it = setInterval(() => {
213
   var it = setInterval(() => {
201
-    res.write(data)
202
-    count++
203
-    if(count == 10) {
214
+    console.log('write data', count)
215
+    res.write('a')
216
+    if(++count > req.params.ticks){
204
       clearInterval(it)
217
       clearInterval(it)
205
       res.end()
218
       res.end()
206
     }
219
     }
207
-  }, 1000)
220
+  }, 1000);
221
+
208
 })
222
 })
209
 
223
 
210
-app.all('/long', (req, res) => {
211
-  var count = 0;
224
+app.all('/long/', (req, res) => {  var count = 0;
212
   var it = setInterval(() => {
225
   var it = setInterval(() => {
213
     console.log('write data', count)
226
     console.log('write data', count)
214
     res.write('a')
227
     res.write('a')
220
 
233
 
221
 })
234
 })
222
 
235
 
236
+app.all('/cookie/:data', (req, res) => {
237
+  res.cookie('cookieName', req.params.data);
238
+  res.end()
239
+})
240
+
241
+app.all('/err-body', (req, res) => {
242
+  res.status(400)
243
+  res.write(JSON.stringify({ data : Date.now() }))
244
+  res.end()
245
+})
246
+
223
 app.all('/timeout', (req, res) => {
247
 app.all('/timeout', (req, res) => {
224
 })
248
 })
225
 
249
 

+ 1
- 0
test-server/test.jpeg View File

1
+undefined

+ 4
- 0
test/nedb.js
File diff suppressed because it is too large
View File


+ 214
- 0
test/test-0.10.0.js View File

1
+import RNTest from './react-native-testkit/'
2
+import React from 'react'
3
+import RNFetchBlob from 'react-native-fetch-blob'
4
+import {
5
+  StyleSheet,
6
+  Text,
7
+  View,
8
+  ScrollView,
9
+  Linking,
10
+  Platform,
11
+  Dimensions,
12
+  BackAndroid,
13
+  AsyncStorage,
14
+  Image,
15
+} from 'react-native';
16
+
17
+window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
18
+window.Blob = RNFetchBlob.polyfill.Blob
19
+
20
+const JSONStream = RNFetchBlob.JSONStream
21
+const fs = RNFetchBlob.fs
22
+const { Assert, Comparer, Info, prop } = RNTest
23
+const describe = RNTest.config({
24
+  group : '0.10.0',
25
+  run : true,
26
+  expand : false,
27
+  timeout : 20000,
28
+})
29
+const { TEST_SERVER_URL, TEST_SERVER_URL_SSL, FILENAME, DROPBOX_TOKEN, styles } = prop()
30
+const dirs = RNFetchBlob.fs.dirs
31
+let prefix = ((Platform.OS === 'android') ? 'file://' : '')
32
+let begin = Date.now()
33
+
34
+describe('json stream via HTTP', (report, done) => {
35
+
36
+  let count = 0
37
+  JSONStream(`${TEST_SERVER_URL}/public/json-dummy.json`).node('name', (name) => {
38
+    count++
39
+    if(Date.now() - begin < 100)
40
+    return
41
+    begin = Date.now()
42
+    report(<Info key="report" uid="100">
43
+      <Text>{count} records</Text>
44
+    </Info>)
45
+    done()
46
+  })
47
+
48
+})
49
+
50
+describe('json stream via fs', (report, done) => {
51
+
52
+  let fetch2 = new RNFetchBlob.polyfill.Fetch({
53
+    auto : true
54
+  })
55
+  let res = null
56
+  let count = 0
57
+
58
+  RNFetchBlob.config({
59
+    fileCache : true
60
+  })
61
+  .fetch('GET',`${TEST_SERVER_URL}/public/json-dummy.json`)
62
+  .then((resp) => {
63
+    res = resp
64
+    JSONStream({
65
+      url : RNFetchBlob.wrap(res.path()),
66
+      headers : { bufferSize : 10240 }
67
+    }).node('name', (name) => {
68
+      count++
69
+      if(Date.now() - begin < 100)
70
+      return
71
+      begin = Date.now()
72
+      report(<Info key="report" uid="100">
73
+        <Text>{count} records</Text>
74
+      </Info>)
75
+      done()
76
+    })
77
+  })
78
+})
79
+
80
+
81
+describe('cookie test', (report, done) => {
82
+  let time = Date.now()
83
+  RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/cookie/${time}`)
84
+  .then((res) => RNFetchBlob.net.getCookies(`${TEST_SERVER_URL}`))
85
+  .then((cookies) => {
86
+    let result = /cookieName\=[^;]+/.exec(cookies[0])
87
+    console.log(result, 'cookieName=' + time)
88
+    report(<Assert key="cookie should not be empty"
89
+      expect={'cookieName=' + time}
90
+      actual={result[0]}/>)
91
+    done()
92
+  })
93
+
94
+})
95
+
96
+describe('SSL test #159', (report, done) => {
97
+  RNFetchBlob.config({
98
+    trusty : true
99
+    })
100
+    .fetch('GET', `${TEST_SERVER_URL_SSL}/public/github.png`, {
101
+      'Cache-Control' : 'no-store'
102
+    })
103
+    .then(res => {
104
+      report(<Assert
105
+        key="trusty request should pass"
106
+        expect={true}
107
+        actual={true}/>)
108
+      return RNFetchBlob.fetch('GET',`${TEST_SERVER_URL_SSL}/public/github.png`)
109
+    })
110
+    .catch(e => {
111
+      report(<Assert
112
+        key="non-trusty request should not pass"
113
+        expect={true}
114
+        actual={true}/>)
115
+      done()
116
+    })
117
+})
118
+
119
+describe('#171 appendExt verify', (report, done) => {
120
+
121
+  RNFetchBlob.config({
122
+    fileCache : true,
123
+    appendExt : 'png'
124
+  })
125
+  .fetch('GET', `${TEST_SERVER_URL}/public/github.png`, {
126
+    'Cache-Control' : 'no-store'
127
+  })
128
+  .then(res => {
129
+    console.log(res.path())
130
+    report(<Assert
131
+      key="extension appended to tmp path"
132
+      actual={/.png$/.test(res.path())}
133
+      expect={true}/>)
134
+    return fs.stat(res.path())
135
+  })
136
+  .then(stat => {
137
+    report(<Assert
138
+      key="verify the file existence"
139
+      expect="23975"
140
+      actual={stat.size} />)
141
+    done()
142
+  })
143
+
144
+})
145
+
146
+describe('#173 issue with append option', (report, done) => {
147
+  let dest = dirs.DocumentDir + '/tmp' + Date.now()
148
+  RNFetchBlob.config({
149
+    path : dest,
150
+    overwrite : true
151
+  })
152
+  .fetch('GET', `${TEST_SERVER_URL}/public/github.png`)
153
+  .then((res) => fs.stat(res.path()))
154
+  .then((stat) => {
155
+    report(<Assert
156
+      key="file size check #1"
157
+      expect="23975"
158
+      actual={stat.size}/>)
159
+    return RNFetchBlob.config({
160
+      path : dest,
161
+      overwrite : false
162
+    })
163
+    .fetch('GET', `${TEST_SERVER_URL}/public/github.png`)
164
+  })
165
+  .then((res) => fs.stat(res.path()))
166
+  .then((stat) => {
167
+    report(<Assert
168
+      key="file size check #2"
169
+      expect="47950"
170
+      actual={stat.size}/>)
171
+    return RNFetchBlob.config({
172
+      path : dest,
173
+      overwrite : true
174
+    })
175
+    .fetch('GET', `${TEST_SERVER_URL}/public/github.png`)
176
+  })
177
+  .then((res) => fs.stat(res.path()))
178
+  .then((stat) => {
179
+    report(<Assert
180
+      key="file size check #3"
181
+      expect="23975"
182
+      actual={stat.size}/>)
183
+    return RNFetchBlob.config({
184
+      path : dest,
185
+    })
186
+    .fetch('GET', `${TEST_SERVER_URL}/public/github.png`)
187
+  })
188
+  .then((res) => fs.stat(res.path()))
189
+  .then((stat) => {
190
+    report(<Assert
191
+      key="it should successfully overwrite existing file without config"
192
+      expect="23975"
193
+      actual={stat.size}/>)
194
+    done()
195
+  })
196
+
197
+})
198
+
199
+describe('#171 verification ', (report, done) => {
200
+
201
+  RNFetchBlob
202
+    .config({
203
+      session: 'SESSION_NAME',
204
+      fileCache: true,
205
+      appendExt: 'mp4'
206
+    })
207
+    .fetch('GET', `${TEST_SERVER_URL}/public/cat-fu.mp4`)
208
+    .then(res => {
209
+      console.log(res.path())
210
+    })
211
+
212
+
213
+
214
+})

+ 3
- 1
test/test-0.6.2.js View File

62
 
62
 
63
 describe('Upload multipart data with file from CameraRoll', (report, done) => {
63
 describe('Upload multipart data with file from CameraRoll', (report, done) => {
64
     let filename = 'test-from-storage-img-'+Date.now()+'.png'
64
     let filename = 'test-from-storage-img-'+Date.now()+'.png'
65
+    console.log(photo)
65
     RNFetchBlob.fetch('POST', `${TEST_SERVER_URL}/upload-form`, {
66
     RNFetchBlob.fetch('POST', `${TEST_SERVER_URL}/upload-form`, {
66
         'Content-Type' : 'multipart/form-data',
67
         'Content-Type' : 'multipart/form-data',
67
       }, [
68
       }, [
165
   let assetName = fs.asset('test-asset1.json')
166
   let assetName = fs.asset('test-asset1.json')
166
   RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
167
   RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
167
     Authorization : `Bearer ${DROPBOX_TOKEN}`,
168
     Authorization : `Bearer ${DROPBOX_TOKEN}`,
168
-    'Dropbox-API-Arg': `{\"path\": \"/rn-upload/file-from-asset-${Platform.OS}.json\",\"mode\": \"add\",\"autorename\": false,\"mute\": false}`,
169
+    'Dropbox-API-Arg': `{\"path\": \"/rn-upload/file-from-asset-${Platform.OS}.json\",\"mode\": \"overwrite\",\"autorename\": false,\"mute\": false}`,
169
     'Content-Type' : 'application/octet-stream',
170
     'Content-Type' : 'application/octet-stream',
170
   }, RNFetchBlob.wrap(assetName))
171
   }, RNFetchBlob.wrap(assetName))
171
   .then((resp) => {
172
   .then((resp) => {
173
+    console.log(resp)
172
     resp = resp.json()
174
     resp = resp.json()
173
     report(
175
     report(
174
       <Assert key="file name check"
176
       <Assert key="file name check"

+ 8
- 2
test/test-0.9.4.js View File

14
 
14
 
15
 window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
15
 window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
16
 window.Blob = RNFetchBlob.polyfill.Blob
16
 window.Blob = RNFetchBlob.polyfill.Blob
17
+window.fetch = new RNFetchBlob.polyfill.Fetch({
18
+  auto : true,
19
+  binaryContentTypes : ['image/', 'video/', 'audio/']
20
+}).build()
17
 
21
 
18
 const fs = RNFetchBlob.fs
22
 const fs = RNFetchBlob.fs
19
 const { Assert, Comparer, Info, prop } = RNTest
23
 const { Assert, Comparer, Info, prop } = RNTest
56
       return res.json()
60
       return res.json()
57
     })
61
     })
58
     .then((data) => {
62
     .then((data) => {
59
-      // console.log(data)
63
+      console.log(data)
60
       report(<Assert key="fetch request success" expect={20000} actual={data.total}/>)
64
       report(<Assert key="fetch request success" expect={20000} actual={data.total}/>)
61
       done()
65
       done()
62
     })
66
     })
64
 })
68
 })
65
 
69
 
66
 describe('issue #111 get redirect destination', (report, done) => {
70
 describe('issue #111 get redirect destination', (report, done) => {
67
-  RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/redirect`)
71
+  RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/redirect`, {
72
+    'Cache-Control' : 'no-store'
73
+  })
68
   .then((res) => {
74
   .then((res) => {
69
     console.log(res.info())
75
     console.log(res.info())
70
     report(
76
     report(

+ 3
- 3
test/test-0.9.5.js View File

30
 
30
 
31
 describe('issue #122 force response data format', (report, done) => {
31
 describe('issue #122 force response data format', (report, done) => {
32
 
32
 
33
-  RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/public/json-dummy.json`, {
33
+  RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/public/json-dummy-1.json`, {
34
     'RNFB-Response' : 'base64'
34
     'RNFB-Response' : 'base64'
35
   })
35
   })
36
   .then((res) => {
36
   .then((res) => {
38
     report(
38
     report(
39
       <Assert key="test data verify" expect="fetchblob-dev" actual={JSON.parse(r).name}/>,
39
       <Assert key="test data verify" expect="fetchblob-dev" actual={JSON.parse(r).name}/>,
40
       <Assert key="should successfully decode the data" expect={true} actual={true}/>)
40
       <Assert key="should successfully decode the data" expect={true} actual={true}/>)
41
-    return RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/public/json-dummy.json`)
41
+    return RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/public/json-dummy-1.json`)
42
   })
42
   })
43
   .then((res) => {
43
   .then((res) => {
44
     report(
44
     report(
79
 
79
 
80
 })
80
 })
81
 
81
 
82
-false && describe('#131 status code != 200 should not throw an error', (report, done) => {
82
+describe('#131 status code != 200 should not throw an error', (report, done) => {
83
 
83
 
84
   let count = 0
84
   let count = 0
85
   let codes = [404, 500, 501, 403]
85
   let codes = [404, 500, 501, 403]

+ 51
- 0
test/test-background.js View File

1
+import RNTest from './react-native-testkit/'
2
+import React from 'react'
3
+import RNFetchBlob from 'react-native-fetch-blob'
4
+import {
5
+  StyleSheet,
6
+  Text,
7
+  View,
8
+  ScrollView,
9
+  Linking,
10
+  Platform,
11
+  Dimensions,
12
+  AsyncStorage,
13
+  Image,
14
+} from 'react-native';
15
+const JSONStream = RNFetchBlob.JSONStream
16
+const fs = RNFetchBlob.fs
17
+const { Assert, Comparer, Info, prop } = RNTest
18
+const describe = RNTest.config({
19
+  group : 'background',
20
+  run : true,
21
+  expand : true,
22
+  timeout : 20000,
23
+})
24
+const { TEST_SERVER_URL, TEST_SERVER_URL_SSL, FILENAME, DROPBOX_TOKEN, styles } = prop()
25
+const dirs = RNFetchBlob.fs.dirs
26
+let prefix = ((Platform.OS === 'android') ? 'file://' : '')
27
+let begin = Date.now()
28
+
29
+describe('background http response', (report, done) => {
30
+  let count = 0
31
+
32
+  let task = RNFetchBlob.config({
33
+    timeout : -1
34
+  }).fetch('GET', `${TEST_SERVER_URL}/long/3600`, {
35
+    'Cache-Control' : 'no-store'
36
+  })
37
+
38
+  task.expire(() => {
39
+    done()
40
+  })
41
+
42
+  task.catch((err) => {
43
+    console.log(err)
44
+  })
45
+
46
+  task.then((res) => {
47
+    console.log('resp response received', res.data.length)
48
+    // done()
49
+  })
50
+
51
+})

+ 6
- 4
test/test-init.js View File

17
 
17
 
18
 // test environment variables
18
 // test environment variables
19
 
19
 
20
-prop('FILENAME', `${Platform.OS}-0.8.0-${Date.now()}.png`)
20
+prop('FILENAME', `${Platform.OS}-0.10.0-${Date.now()}.png`)
21
 prop('TEST_SERVER_URL', 'http://localhost:8123')
21
 prop('TEST_SERVER_URL', 'http://localhost:8123')
22
 prop('TEST_SERVER_URL_SSL', 'https://localhost:8124')
22
 prop('TEST_SERVER_URL_SSL', 'https://localhost:8124')
23
-// prop('TEST_SERVER_URL', 'http://192.168.17.194:8123')
24
-// prop('TEST_SERVER_URL_SSL', 'https://192.168.17.194:8124')
23
+// prop('TEST_SERVER_URL', 'http://192.168.0.12:8123')
24
+// prop('TEST_SERVER_URL_SSL', 'https://192.168.0.12:8124')
25
 prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
25
 prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
26
 prop('styles', {
26
 prop('styles', {
27
   image : {
27
   image : {
73
 // require('./test-0.9.4')
73
 // require('./test-0.9.4')
74
 // require('./test-0.9.5')
74
 // require('./test-0.9.5')
75
 // require('./test-0.9.6')
75
 // require('./test-0.9.6')
76
-require('./test-stream')
76
+require('./test-0.10.0')
77
+// require('./test-background.js')
78
+// require('./test-stream')
77
 // require('./test-fetch')
79
 // require('./test-fetch')
78
 // require('./test-fs')
80
 // require('./test-fs')
79
 // require('./test-xmlhttp')
81
 // require('./test-xmlhttp')

+ 40
- 0
test/test-readable.js View File

1
+import RNTest from './react-native-testkit/'
2
+import React from 'react'
3
+import RNFetchBlob from 'react-native-fetch-blob'
4
+import Timer from 'react-timer-mixin'
5
+
6
+import {
7
+  StyleSheet,
8
+  Text,
9
+  View,
10
+  ScrollView,
11
+  CameraRoll,
12
+  Platform,
13
+  Dimensions,
14
+  Image,
15
+} from 'react-native';
16
+
17
+import EventEmitter from 'EventEmitter'
18
+
19
+const fs = RNFetchBlob.fs
20
+
21
+const Readable = RNFetchBlob.polyfill.Readable
22
+const { Assert, Comparer, Info, prop } = RNTest
23
+const describe = RNTest.config({
24
+  group : 'Blob',
25
+  run : true,
26
+  expand : false,
27
+  timeout : 20000,
28
+})
29
+const { TEST_SERVER_URL, TEST_SERVER_URL_SSL, DROPBOX_TOKEN, styles } = prop()
30
+const  dirs = RNFetchBlob.fs.dirs
31
+
32
+let prefix = ((Platform.OS === 'android') ? 'file://' : '')
33
+let file = RNTest.prop('image')
34
+
35
+describe('first test', (report, done) => {
36
+  let e = new EventEmitter()
37
+  console.log(e)
38
+  e.addListener('aaa', (data) => { console.log(data) })
39
+  e.emit('aaa', 123)
40
+})