Browse Source

Merge branch '0.9.7' into 0.10.0

Ben Hsieh 8 years ago
parent
commit
9b1edd2226
100 changed files with 497 additions and 208 deletions
  1. 2
    0
      .github/ISSUE_TEMPLATE
  2. 1
    1
      .github/PULL_REQUEST_TEMPLATE
  3. 33
    5
      README.md
  4. 3
    2
      package.json
  5. 79
    92
      src/README.md
  6. 6
    4
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java
  7. 12
    8
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java
  8. 39
    0
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobProgressConfig.java
  9. 10
    9
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
  10. 4
    6
      src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobDefaultResp.java
  11. 25
    16
      src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
  12. 21
    20
      src/fs.js
  13. 59
    5
      src/index.js
  14. 6
    0
      src/ios/RNFetchBlob.xcodeproj/project.pbxproj
  15. 9
    5
      src/ios/RNFetchBlob/RNFetchBlob.m
  16. 1
    0
      src/ios/RNFetchBlobConst.h
  17. 2
    1
      src/ios/RNFetchBlobConst.m
  18. 4
    0
      src/ios/RNFetchBlobNetwork.h
  19. 68
    24
      src/ios/RNFetchBlobNetwork.m
  20. 39
    0
      src/ios/RNFetchBlobProgress.h
  21. 57
    0
      src/ios/RNFetchBlobProgress.m
  22. 2
    1
      src/ios/RNFetchBlobReqBuilder.m
  23. 1
    1
      src/package.json
  24. 2
    2
      src/polyfill/Blob.js
  25. 10
    4
      src/polyfill/XMLHttpRequest.js
  26. 2
    2
      src/react-native-fetch-blob.podspec
  27. BIN
      test-server/cat_fu_mp4/img00001.jpeg
  28. BIN
      test-server/cat_fu_mp4/img00002.jpeg
  29. BIN
      test-server/cat_fu_mp4/img00003.jpeg
  30. BIN
      test-server/cat_fu_mp4/img00004.jpeg
  31. BIN
      test-server/cat_fu_mp4/img00005.jpeg
  32. BIN
      test-server/cat_fu_mp4/img00006.jpeg
  33. BIN
      test-server/cat_fu_mp4/img00007.jpeg
  34. BIN
      test-server/cat_fu_mp4/img00008.jpeg
  35. BIN
      test-server/cat_fu_mp4/img00009.jpeg
  36. BIN
      test-server/cat_fu_mp4/img00010.jpeg
  37. BIN
      test-server/cat_fu_mp4/img00011.jpeg
  38. BIN
      test-server/cat_fu_mp4/img00012.jpeg
  39. BIN
      test-server/cat_fu_mp4/img00013.jpeg
  40. BIN
      test-server/cat_fu_mp4/img00014.jpeg
  41. BIN
      test-server/cat_fu_mp4/img00015.jpeg
  42. BIN
      test-server/cat_fu_mp4/img00016.jpeg
  43. BIN
      test-server/cat_fu_mp4/img00017.jpeg
  44. BIN
      test-server/cat_fu_mp4/img00018.jpeg
  45. BIN
      test-server/cat_fu_mp4/img00019.jpeg
  46. BIN
      test-server/cat_fu_mp4/img00020.jpeg
  47. BIN
      test-server/cat_fu_mp4/img00021.jpeg
  48. BIN
      test-server/cat_fu_mp4/img00022.jpeg
  49. BIN
      test-server/cat_fu_mp4/img00023.jpeg
  50. BIN
      test-server/cat_fu_mp4/img00024.jpeg
  51. BIN
      test-server/cat_fu_mp4/img00025.jpeg
  52. BIN
      test-server/cat_fu_mp4/img00026.jpeg
  53. BIN
      test-server/cat_fu_mp4/img00027.jpeg
  54. BIN
      test-server/cat_fu_mp4/img00028.jpeg
  55. BIN
      test-server/cat_fu_mp4/img00029.jpeg
  56. BIN
      test-server/cat_fu_mp4/img00030.jpeg
  57. BIN
      test-server/cat_fu_mp4/img00031.jpeg
  58. BIN
      test-server/cat_fu_mp4/img00032.jpeg
  59. BIN
      test-server/cat_fu_mp4/img00033.jpeg
  60. BIN
      test-server/cat_fu_mp4/img00034.jpeg
  61. BIN
      test-server/cat_fu_mp4/img00035.jpeg
  62. BIN
      test-server/cat_fu_mp4/img00036.jpeg
  63. BIN
      test-server/cat_fu_mp4/img00037.jpeg
  64. BIN
      test-server/cat_fu_mp4/img00038.jpeg
  65. BIN
      test-server/cat_fu_mp4/img00039.jpeg
  66. BIN
      test-server/cat_fu_mp4/img00040.jpeg
  67. BIN
      test-server/cat_fu_mp4/img00041.jpeg
  68. BIN
      test-server/cat_fu_mp4/img00042.jpeg
  69. BIN
      test-server/cat_fu_mp4/img00043.jpeg
  70. BIN
      test-server/cat_fu_mp4/img00044.jpeg
  71. BIN
      test-server/cat_fu_mp4/img00045.jpeg
  72. BIN
      test-server/cat_fu_mp4/img00046.jpeg
  73. BIN
      test-server/cat_fu_mp4/img00047.jpeg
  74. BIN
      test-server/cat_fu_mp4/img00048.jpeg
  75. BIN
      test-server/cat_fu_mp4/img00049.jpeg
  76. BIN
      test-server/cat_fu_mp4/img00050.jpeg
  77. BIN
      test-server/cat_fu_mp4/img00051.jpeg
  78. BIN
      test-server/cat_fu_mp4/img00052.jpeg
  79. BIN
      test-server/cat_fu_mp4/img00053.jpeg
  80. BIN
      test-server/cat_fu_mp4/img00054.jpeg
  81. BIN
      test-server/cat_fu_mp4/img00055.jpeg
  82. BIN
      test-server/cat_fu_mp4/img00056.jpeg
  83. BIN
      test-server/cat_fu_mp4/img00057.jpeg
  84. BIN
      test-server/cat_fu_mp4/img00058.jpeg
  85. BIN
      test-server/cat_fu_mp4/img00059.jpeg
  86. BIN
      test-server/cat_fu_mp4/img00060.jpeg
  87. BIN
      test-server/cat_fu_mp4/img00061.jpeg
  88. BIN
      test-server/cat_fu_mp4/img00062.jpeg
  89. BIN
      test-server/cat_fu_mp4/img00063.jpeg
  90. BIN
      test-server/cat_fu_mp4/img00064.jpeg
  91. BIN
      test-server/cat_fu_mp4/img00065.jpeg
  92. BIN
      test-server/cat_fu_mp4/img00066.jpeg
  93. BIN
      test-server/cat_fu_mp4/img00067.jpeg
  94. BIN
      test-server/cat_fu_mp4/img00068.jpeg
  95. BIN
      test-server/cat_fu_mp4/img00069.jpeg
  96. BIN
      test-server/cat_fu_mp4/img00070.jpeg
  97. BIN
      test-server/cat_fu_mp4/img00071.jpeg
  98. BIN
      test-server/cat_fu_mp4/img00072.jpeg
  99. BIN
      test-server/cat_fu_mp4/img00073.jpeg
  100. 0
    0
      test-server/cat_fu_mp4/img00074.jpeg

+ 2
- 0
.github/ISSUE_TEMPLATE View File

1
 Hi ! Thank you for reporting an issue, but we would like to remind you, we have a trouble shooting page in our wiki. You may want to take a look on that page :p 
1
 Hi ! Thank you for reporting an issue, but we would like to remind you, we have a trouble shooting page in our wiki. You may want to take a look on that page :p 
2
 
2
 
3
+* please provide the version of installed library and RN project.
4
+* a sample code snippet/repository is very helpful to spotting the problem.
3
 * issues which have been tagged as 'needs feedback', will be closed after 2 weeks if receive no feedbacks.
5
 * issues which have been tagged as 'needs feedback', will be closed after 2 weeks if receive no feedbacks.

+ 1
- 1
.github/PULL_REQUEST_TEMPLATE View File

1
 Thank you for making a pull request ! Just a gentle reminder :)
1
 Thank you for making a pull request ! Just a gentle reminder :)
2
 
2
 
3
 1. If the PR is offering a feature please make the request to our "Feature Branch" 0.10.0
3
 1. If the PR is offering a feature please make the request to our "Feature Branch" 0.10.0
4
-2. Bug fix request to "Bug Fix Branch" 0.9.4
4
+2. Bug fix request to "Bug Fix Branch" 0.9.6
5
 3. Correct README.md can directly to master
5
 3. Correct README.md can directly to master

+ 33
- 5
README.md View File

4
 
4
 
5
 A project committed to make file acess and data transfer easier, efficient for React Native developers.
5
 A project committed to make file acess and data transfer easier, efficient for React Native developers.
6
 
6
 
7
+> The npm package is inside `src` folder, if you're going to install using github repository do not point to here directly
8
+
7
 ## Features
9
 ## Features
8
 - Transfer data directly from/to storage without BASE64 bridging
10
 - Transfer data directly from/to storage without BASE64 bridging
9
 - File API supports normal files, Asset files, and CameraRoll files
11
 - File API supports normal files, Asset files, and CameraRoll files
11
 - File stream support for dealing with large file
13
 - File stream support for dealing with large file
12
 - Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental)
14
 - Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental)
13
 
15
 
14
-> The npm package is inside `src` folder, if you're going to install via git repository do not directly poiint to this folder
15
-
16
 ## TOC
16
 ## TOC
17
 * [About](#user-content-about)
17
 * [About](#user-content-about)
18
 * [Installation](#user-content-installation)
18
 * [Installation](#user-content-installation)
157
  - 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. 
157
  - 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. 
158
 - To send the body as-is, simply use a `Content-Type` header not containing `;BASE64` or `application/octet`.
158
 - To send the body as-is, simply use a `Content-Type` header not containing `;BASE64` or `application/octet`.
159
 
159
 
160
+> 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'` 
161
+
160
 > 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`.
162
 > 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`.
161
 
163
 
162
 ### Download example : Fetch files that needs authorization token
164
 ### Download example : Fetch files that needs authorization token
268
     }),
270
     }),
269
     'Content-Type' : 'application/octet-stream',
271
     'Content-Type' : 'application/octet-stream',
270
     // here's the body you're going to send, should be a BASE64 encoded string
272
     // here's the body you're going to send, should be a BASE64 encoded string
271
-    // (you can use "base64" APIs to make one).
273
+    // (you can use "base64"(refer to the library 'mathiasbynens/base64') APIs to make one).
272
     // The data will be converted to "byte array"(say, blob) before request sent.  
274
     // The data will be converted to "byte array"(say, blob) before request sent.  
273
   }, base64ImageString)
275
   }, base64ImageString)
274
   .then((res) => {
276
   .then((res) => {
378
 
380
 
379
 ### Upload/Download progress
381
 ### Upload/Download progress
380
 
382
 
381
-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.
383
+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. 
382
 
384
 
383
 ```js
385
 ```js
384
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
386
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
401
     })
403
     })
402
 ```
404
 ```
403
 
405
 
406
+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.
407
+
408
+
409
+```js
410
+  RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
411
+      ... some headers,
412
+      'Content-Type' : 'octet-stream'
413
+    }, base64DataString)
414
+    // listen to upload progress event, emit every 250ms
415
+    .uploadProgress({ interval : 250 },(written, total) => {
416
+        console.log('uploaded', written / total)
417
+    })
418
+    // listen to download progress event, every 10%
419
+    .progress({ count : 10 }, (received, total) => {
420
+        console.log('progress', received / total)
421
+    })
422
+    .then((resp) => {
423
+      // ...
424
+    })
425
+    .catch((err) => {
426
+      // ...
427
+    })
428
+```
429
+
404
 ### Cancel Request
430
 ### Cancel Request
405
 
431
 
406
 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.
432
 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.
709
 
735
 
710
 ## Performance Tips
736
 ## Performance Tips
711
 
737
 
712
-**Reduce RCT Bridge and BASE64 Overheard**
738
+**Read Stream Event Overhead**
713
 
739
 
714
 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. 
740
 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. 
715
 
741
 
742
+**Reduce RCT Bridge and BASE64 Overhead**
743
+
716
 React Native connects JS and Native context by passing JSON around React Native bridge, and there will be an overhead to convert data before they sent to each side. When data is large, this will be quite a performance impact to your app, it's recommended to use file storage instead of BASE64 if possible.The following chart shows how much faster when loading data from storage than BASE64 encoded string on iphone 6.
744
 React Native connects JS and Native context by passing JSON around React Native bridge, and there will be an overhead to convert data before they sent to each side. When data is large, this will be quite a performance impact to your app, it's recommended to use file storage instead of BASE64 if possible.The following chart shows how much faster when loading data from storage than BASE64 encoded string on iphone 6.
717
 
745
 
718
 <img src="img/performance_1.png" style="width : 100%"/>
746
 <img src="img/performance_1.png" style="width : 100%"/>

+ 3
- 2
package.json View File

1
 {
1
 {
2
-  "name": "fetchblob-dev",
3
-  "version": "0.9.5-beta.2",
2
+  "name": "react-native-fetch-blob-dev-env",
3
+  "description" : "RNFB development environment, not dist package",
4
+  "version": "0.9.6",
4
   "private": true,
5
   "private": true,
5
   "scripts": {
6
   "scripts": {
6
     "start": "node node_modules/react-native/local-cli/cli.js start",
7
     "start": "node node_modules/react-native/local-cli/cli.js start",

+ 79
- 92
src/README.md View File

1
-# react-native-fetch-blob [![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) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]()
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
 
3
 
3
-# [Visit our Github for latest document](https://github.com/wkh237/react-native-fetch-blob)
4
 
4
 
5
-A project committed to make file acess and data transfer easier, effiecient for React Native developers.
5
+A project committed to make file acess and data transfer easier, efficient for React Native developers.
6
+
7
+# [Please visit our Github Page for latest document](https://github.com/wkh237/react-native-fetch-blob)
6
 
8
 
7
 ## Features
9
 ## Features
8
 - Transfer data directly from/to storage without BASE64 bridging
10
 - Transfer data directly from/to storage without BASE64 bridging
11
 - File stream support for dealing with large file
13
 - File stream support for dealing with large file
12
 - Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental)
14
 - Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental)
13
 
15
 
14
-
15
 ## TOC
16
 ## TOC
16
 * [About](#user-content-about)
17
 * [About](#user-content-about)
17
 * [Installation](#user-content-installation)
18
 * [Installation](#user-content-installation)
24
  * [Cancel HTTP request](#user-content-cancel-request)
25
  * [Cancel HTTP request](#user-content-cancel-request)
25
  * [Android Media Scanner, and Download Manager Support](#user-content-android-media-scanner-and-download-manager-support)
26
  * [Android Media Scanner, and Download Manager Support](#user-content-android-media-scanner-and-download-manager-support)
26
  * [Self-Signed SSL Server](#user-content-self-signed-ssl-server)
27
  * [Self-Signed SSL Server](#user-content-self-signed-ssl-server)
28
+ * [Transfer Encoding](#user-content-transfer-encoding)
27
  * [RNFetchBlob as Fetch](#user-content-rnfetchblob-as-fetch)
29
  * [RNFetchBlob as Fetch](#user-content-rnfetchblob-as-fetch)
28
 * [File System](#user-content-file-system)
30
 * [File System](#user-content-file-system)
29
  * [File access](#user-content-file-access)
31
  * [File access](#user-content-file-access)
37
 
39
 
38
 ## About
40
 ## About
39
 
41
 
40
-This project was initially for solving the issue [facebook/react-native#854](https://github.com/facebook/react-native/issues/854), because React Native lack of `Blob` implementation and it will cause some problem when transfering binary data. Now, this project is committed to make file access and transfer more easier, effiecient for React Native developers. We've implemented highly customizable filesystem and network module which plays well together. For example, upload and download data directly from/to storage which is much more efficient in some cases(especially for large ones). The file system supports file stream, so you don't have to worry about OOM problem when accessing large files.
42
+This project was initially for solving the issue [facebook/react-native#854](https://github.com/facebook/react-native/issues/854), because React Native lack of `Blob` implementation and it will cause some problem when transferring binary data. Now, this project is committed to make file access and transfer more easier, efficient for React Native developers. We've implemented highly customizable filesystem and network module which plays well together. For example, upload and download data directly from/to storage which is much more efficient in some cases(especially for large ones). The file system supports file stream, so you don't have to worry about OOM problem when accessing large files.
41
 
43
 
42
-In `0.8.0` we introduced experimential Web API polyfills that make it possible to use browser-based libraries in React Native, for example, [FireBase JS SDK](https://github.com/wkh237/rn-firebase-storage-upload-sample)
44
+In `0.8.0` we introduced experimental Web API polyfills that make it possible to use browser-based libraries in React Native, such as, [FireBase JS SDK](https://github.com/wkh237/rn-firebase-storage-upload-sample)
43
 
45
 
44
 
46
 
45
 ## Installation
47
 ## Installation
50
 npm install --save react-native-fetch-blob
52
 npm install --save react-native-fetch-blob
51
 ```
53
 ```
52
 
54
 
53
-Link package using [rnpm](https://github.com/rnpm/rnpm)
55
+Or if using CocoaPods, add the pod to your `Podfile`, for example:
54
 
56
 
55
-```sh
56
-rnpm link
57
 ```
57
 ```
58
+pod 'react-native-fetch-blob,
59
+    :path => '../node_modules/react-native-fetch-blob
60
+```
61
+
62
+**Automatically Link Native Modules**
58
 
63
 
59
-### Manually link the package (Android)
64
+For 0.29.2+ projects, simply link native packages via following command because rnpm has been merged into react-native, you no longer need it.
60
 
65
 
61
-If rnpm link command failed to link the package automatically, you might try manually link the package.
66
+```
67
+react-native link
68
+```
62
 
69
 
63
-Open `android/settings.gradle`, and add these lines which will app RNFetchBlob Android project dependency to your app.
70
+As for projects < 0.29 you need `rnpm` to link native packages
64
 
71
 
65
-```diff
66
-include ':app'      
67
-+ include ':react-native-fetch-blob'                                                                                                  
68
-+ project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fetch-blob/android')                        
72
+```sh
73
+rnpm link
69
 ```
74
 ```
70
 
75
 
71
-Add this line to `MainApplication.java`, so that RNFetchBlob package becomes part of react native package.
76
+Optionally, use the following command to add Android permissions to `AndroidManifest.xml` automatically
72
 
77
 
73
-```diff
74
-...
75
-+ import com.RNFetchBlob.RNFetchBlobPackage;                                                                                 
76
-...
77
-protected List<ReactPackage> getPackages() {
78
-      return Arrays.<ReactPackage>asList(
79
-          new MainReactPackage(),
80
-+          new RNFetchBlobPackage()                                                                                         
81
-      );
82
-    }
83
-  };
84
-...
78
+```sh
79
+RNFB_ANDROID_PERMISSIONS=true react-native link
85
 ```
80
 ```
86
-> If you still having problem on installing this package, please check the [trouble shooting page](https://github.com/wkh237/react-native-fetch-blob/wiki/Trouble-Shooting) or [file an issue](https://github.com/wkh237/react-native-fetch-blob/issues/new)
81
+
82
+pre 0.29 projects
83
+
84
+```sh
85
+RNFB_ANDROID_PERMISSIONS=true rnpm link
86
+```
87
+
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.
87
 
89
 
88
 **Grant Permission to External storage for Android 5.0 or lower**
90
 **Grant Permission to External storage for Android 5.0 or lower**
89
 
91
 
138
 var RNFetchBlob = require('react-native-fetch-blob').default
140
 var RNFetchBlob = require('react-native-fetch-blob').default
139
 ```
141
 ```
140
 
142
 
141
-### HTTP Data Transfer
143
+## HTTP Data Transfer
142
 
144
 
143
----
144
 
145
 
145
-#### Regular Request
146
+### Regular Request
146
 
147
 
147
 After `0.8.0` react-native-fetch-blob automatically decide how to send the body by checking its type and `Content-Type` in header. The rule is described in the following diagram
148
 After `0.8.0` react-native-fetch-blob automatically decide how to send the body by checking its type and `Content-Type` in header. The rule is described in the following diagram
148
 
149
 
150
 
151
 
151
 To sum up :
152
 To sum up :
152
 
153
 
153
-- To send a form data, the `Content-Type` header won't take effect if the body is an `Array` because we will set proper content type for you.
154
-- To send binary data, you have two choices, use BASE64 encoded string or a file path which points to a file contains the body. The `Content-Type` header does not matters.
155
- - The body is a BASE64 encoded string, the `Content-Type` header filed must containing substring`;BASE64` or `application/octet`  
156
- - The body is a path point to a file, it must be a string starts with `RNFetchBlob-file://`, which can simply done by `RNFetchBlob.wrap(PATH_TO_THE_FILE)`
157
-- To send the body as-is, set a `Content-Type` header not containing `;BASE64` or `application/octet`.
154
+- 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.
155
+- To send binary data, you have two choices, use BASE64 encoded string or path points to a file contains the body.
156
+ - 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.   
157
+ - 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.
158
+- To send the body as-is, simply use a `Content-Type` header not containing `;BASE64` or `application/octet`.
159
+
160
+> 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'`
161
+
162
+> 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`.
158
 
163
 
159
-#### Download example : Fetch files that needs authorization token
164
+### Download example : Fetch files that needs authorization token
160
 
165
 
161
 Most simple way is download to memory and stored as BASE64 encoded string, this is handy when the response data is small.
166
 Most simple way is download to memory and stored as BASE64 encoded string, this is handy when the response data is small.
162
 
167
 
182
   })
187
   })
183
 ```
188
 ```
184
 
189
 
185
-#### Download to storage directly
190
+### Download to storage directly
186
 
191
 
187
-If the response data is large, that would be a bad idea to convert it into BASE64 string. The better solution is store the response data directly into file system. The simplest way is give a `fileCache` option to config, and set it to `true`. This will make incoming response data stored in a temporary path **without** any file extension.
192
+If the response data is large, that would be a bad idea to convert it into BASE64 string. A better solution is streaming the response directly into a file, simply add a `fileCache` option to config, and set it to `true`. This will make incoming response data stored in a temporary path **without** any file extension.
188
 
193
 
189
 **These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)**
194
 **These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)**
190
 
195
 
206
 
211
 
207
 **Set Temp File Extension**
212
 **Set Temp File Extension**
208
 
213
 
209
-Sometimes you might need a file extension for some reason. For instance, when using file path as source of `Image` component, the path should end with something like .png or .jpg, you can do this by add `appendExt` option to `config`.
214
+Sometimes you might need a file extension for some reason. For example, when using file path as source of `Image` component, the path should end with something like .png or .jpg, you can do this by add `appendExt` option to `config`.
210
 
215
 
211
 ```js
216
 ```js
212
 RNFetchBlob
217
 RNFetchBlob
229
 
234
 
230
 **Use Specific File Path**
235
 **Use Specific File Path**
231
 
236
 
232
-If you prefer a specific path rather than randomly generated one, you can use `path` option. We've added a constant [dirs](#user-content-dirs) in v0.5.0 that contains several common used directories.
237
+If you prefer a specific path rather than randomly generated one, you can use `path` option. We've added [several  constants](#user-content-dirs) in v0.5.0 which represents commonly used directories.
233
 
238
 
234
 ```js
239
 ```js
235
 let dirs = RNFetchBlob.fs.dirs
240
 let dirs = RNFetchBlob.fs.dirs
251
 
256
 
252
 ####  Upload example : Dropbox [files-upload](https://www.dropbox.com/developers/documentation/http/documentation#files-upload) API
257
 ####  Upload example : Dropbox [files-upload](https://www.dropbox.com/developers/documentation/http/documentation#files-upload) API
253
 
258
 
254
-`react-native-fetch-blob` will convert the base64 string in `body` to binary format using native API, this process will be  done in a new thread, so it's async.
259
+`react-native-fetch-blob` will convert the base64 string in `body` to binary format using native API, this process will be  done in a separated thread, so it won't block your GUI.
255
 
260
 
256
 ```js
261
 ```js
257
 
262
 
265
     }),
270
     }),
266
     'Content-Type' : 'application/octet-stream',
271
     'Content-Type' : 'application/octet-stream',
267
     // here's the body you're going to send, should be a BASE64 encoded string
272
     // here's the body you're going to send, should be a BASE64 encoded string
268
-    // (you can use "base64" APIs to make one).
273
+    // (you can use "base64"(refer to the library 'mathiasbynens/base64') APIs to make one).
269
     // The data will be converted to "byte array"(say, blob) before request sent.  
274
     // The data will be converted to "byte array"(say, blob) before request sent.  
270
   }, base64ImageString)
275
   }, base64ImageString)
271
   .then((res) => {
276
   .then((res) => {
276
   })
281
   })
277
 ```
282
 ```
278
 
283
 
279
-#### Upload a file from storage
284
+### Upload a file from storage
280
 
285
 
281
-If you're going to use a `file` request body, just wrap the path with `wrap` API.
286
+If you're going to use a `file` as request body, just wrap the path with `wrap` API.
282
 
287
 
283
 ```js
288
 ```js
284
 RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
289
 RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
302
   })
307
   })
303
 ```
308
 ```
304
 
309
 
305
-#### Multipart/form-data example : Post form data with file and data
310
+### Multipart/form-data example : Post form data with file and data
306
 
311
 
307
 In `version >= 0.3.0` you can also post files with form data, just put an array in `body`, with elements have property `name`, `data`, and `filename`(optional).
312
 In `version >= 0.3.0` you can also post files with form data, just put an array in `body`, with elements have property `name`, `data`, and `filename`(optional).
308
 
313
 
334
   })
339
   })
335
 ```
340
 ```
336
 
341
 
337
-What if you want to upload a file using form data ? Just like [upload a file from storage](#user-content-upload-a-file-from-storage) example, wrap `data` by `wrap` API (this feature is only available for `version >= v0.5.0`). On version >= `0.6.2`, it is possible to set custom MIME type when appending file to form data.
342
+What if you want to append a file to form data ? Just like [upload a file from storage](#user-content-upload-a-file-from-storage) example, wrap `data` by `wrap` API (this feature is only available for `version >= v0.5.0`). On version >= `0.6.2`, it is possible to set custom MIME type when appending file to form data. But keep in mind when the file is large it's likely crash your app. Please consider use other strategy (see [#94](https://github.com/wkh237/react-native-fetch-blob/issues/94)).
338
 
343
 
339
 ```js
344
 ```js
340
 
345
 
373
   })
378
   })
374
 ```
379
 ```
375
 
380
 
376
-#### Upload/Download progress
381
+### Upload/Download progress
377
 
382
 
378
-In `version >= 0.4.2` it is possible to know the upload/download progress. After `0.7.0` IOS and Android upload progress are supported.
383
+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.
379
 
384
 
380
 ```js
385
 ```js
381
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
386
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
398
     })
403
     })
399
 ```
404
 ```
400
 
405
 
401
-#### Cancel Request
406
+### Cancel Request
402
 
407
 
403
 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.
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.
404
 
409
 
537
       android.actionViewIntent(PATH_OF_IMG, 'image/png')
542
       android.actionViewIntent(PATH_OF_IMG, 'image/png')
538
 ```
543
 ```
539
 
544
 
540
-### File System
545
+## File System
541
 
546
 
542
-#### File Access
547
+### File Access
543
 
548
 
544
 File access APIs were made when developing `v0.5.0`, which helping us write tests, and was not planned to be a part of this module. However we realized that, it's hard to find a great solution to manage cached files, every one who use this moudle may need these APIs for there cases.
549
 File access APIs were made when developing `v0.5.0`, which helping us write tests, and was not planned to be a part of this module. However we realized that, it's hard to find a great solution to manage cached files, every one who use this moudle may need these APIs for there cases.
545
 
550
 
567
 
572
 
568
 See [File API](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API) for more information
573
 See [File API](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API) for more information
569
 
574
 
570
-#### File Stream
575
+### File Stream
571
 
576
 
572
 In `v0.5.0` we've added  `writeStream` and `readStream`, which allows your app read/write data from file path. This API creates a file stream, rather than convert whole data into BASE64 encoded string, it's handy when processing **large files**.
577
 In `v0.5.0` we've added  `writeStream` and `readStream`, which allows your app read/write data from file path. This API creates a file stream, rather than convert whole data into BASE64 encoded string, it's handy when processing **large files**.
573
 
578
 
574
-When calling `readStream` method, you have to `open` the stream, and start to read data.
579
+When calling `readStream` method, you have to `open` the stream, and start to read data. When the file is large, consider use an appropriate `bufferSize` and `interval` to reduce the native event dispatching overhead (see [Performance Tips](#user-content-performance-tips))
575
 
580
 
576
 ```js
581
 ```js
577
 let data = ''
582
 let data = ''
616
 
621
 
617
 ```
622
 ```
618
 
623
 
619
-#### Cache File Management
624
+### Cache File Management
620
 
625
 
621
 When using `fileCache` or `path` options along with `fetch` API, response data will automatically stored into file system. The files will **NOT** removed unless you `unlink` it. There're several ways to remove the files
626
 When using `fileCache` or `path` options along with `fetch` API, response data will automatically stored into file system. The files will **NOT** removed unless you `unlink` it. There're several ways to remove the files
622
 
627
 
673
 
678
 
674
 ```
679
 ```
675
 
680
 
676
-#### Self-Signed SSL Server
681
+### Transfer Encoding
682
+
683
+After `0.9.4`, the `Chunked` transfer encoding is disabled by default due to some service provoder may not support chunked transfer. To enable it, set `Transfer-Encoding` header to `Chunked`.
684
+
685
+```js
686
+RNFetchBlob.fetch('POST', 'http://example.com/upload', { 'Transfer-Encoding' : 'Chunked' }, bodyData)
687
+```
688
+
689
+### Self-Signed SSL Server
677
 
690
 
678
 By default, react-native-fetch-blob does NOT allow connection to unknown certification provider since it's dangerous. If you're going to connect a server with self-signed certification, add `trusty` to `config`. This function is available for version >= `0.5.3`
691
 By default, react-native-fetch-blob does NOT allow connection to unknown certification provider since it's dangerous. If you're going to connect a server with self-signed certification, add `trusty` to `config`. This function is available for version >= `0.5.3`
679
 
692
 
687
 })
700
 })
688
 ```
701
 ```
689
 
702
 
690
-### Web API Polyfills
703
+## Web API Polyfills
691
 
704
 
692
 After `0.8.0` we've made some [Web API polyfills](https://github.com/wkh237/react-native-fetch-blob/wiki/Web-API-Polyfills-(experimental)) that makes some browser-based library available in RN.
705
 After `0.8.0` we've made some [Web API polyfills](https://github.com/wkh237/react-native-fetch-blob/wiki/Web-API-Polyfills-(experimental)) that makes some browser-based library available in RN.
693
 
706
 
696
 
709
 
697
 Here's a [sample app](https://github.com/wkh237/rn-firebase-storage-upload-sample) that uses polyfills to upload files to FireBase.
710
 Here's a [sample app](https://github.com/wkh237/rn-firebase-storage-upload-sample) that uses polyfills to upload files to FireBase.
698
 
711
 
699
-### Performance Tips
712
+## Performance Tips
713
+
714
+**Read Stream Event Overhead**
700
 
715
 
701
----
716
+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.
702
 
717
 
703
-**Reduce RCT Bridge and BASE64 Overheard**
718
+**Reduce RCT Bridge and BASE64 Overhead**
704
 
719
 
705
-React Native connects JS and Native context by passing JSON through React bridge, therefore there will be an overhead to convert data before they sent. When data is large, this will be quite a performance impact to your app, it's recommended to use file storage instead of BASE64 if possible. The following chart shows how much faster when loading data from storage than BASE64 encoded string on iphone 6.
720
+React Native connects JS and Native context by passing JSON around React Native bridge, and there will be an overhead to convert data before they sent to each side. When data is large, this will be quite a performance impact to your app, it's recommended to use file storage instead of BASE64 if possible.The following chart shows how much faster when loading data from storage than BASE64 encoded string on iphone 6.
706
 
721
 
707
 <img src="img/performance_1.png" style="width : 100%"/>
722
 <img src="img/performance_1.png" style="width : 100%"/>
708
 
723
 
720
 
735
 
721
 ## Changes
736
 ## Changes
722
 
737
 
723
-| Version | |
724
-|---|---|
725
-| 0.9.1 | Fix Android Blob constructor asynchronous issue caused by 0.9.0 fs change |
726
-| 0.9.0 | Fix unicode response data format issue #73. Improve Android performance by using thread pool instead of async task. Add Fetch replacement #70. Add Android only API `actionViewIntent` to open file or install APK in app |
727
-| 0.8.1 | Remove Web API log and fix ios progress report function. |
728
-| 0.8.0 | Added Web API polyfills, support regular request, added timeout option. |
729
-| 0.7.5 | Fix installation script that make it compatible to react-native < 0.28 |
730
-| 0.7.4 | Fix app crash problem in version > 0.27 |
731
-| 0.7.3 | Fix OkHttp dependency issue in version < 0.29 |
732
-| 0.7.2 | Fix cancel request bug |
733
-| 0.7.1 | Fix #57 ios module could not compile on ios version <= 9.3 |
734
-| 0.7.0 | Add support of Android upload progress, and remove AsyncHttpClient dependency from Android native implementation. |
735
-| 0.6.4 | Fix rnpm link script. |
736
-| 0.6.3 | Fix performance issue on IOS, increase max concurrent request limitation from 1. |
737
-| 0.6.2 | Add support of asset file and camera roll files, Support custom MIME type when sending multipart request, thanks @smartt |
738
-| 0.6.1 | Fix #37 progress report API issue on IOS |
739
-| 0.6.0 | Add readFile and writeFile API for easier file access, also added Android download manager support. |
740
-| 0.5.8 | Fix #33 PUT request will always be sent as POST on Android |
741
-| 0.5.7 | Fix #31 #30 Xcode pre 7.3 build error |
742
-| 0.5.6 | Add support for IOS network status indicator. Fix file stream ASCII reader bug. |
743
-| 0.5.5 | Remove work in progress code added in 0.5.2 which may cause memory leaks. |
744
-| 0.5.4 | Fix #30 #31 build build error, and improve memory efficiency. |
745
-| 0.5.3 | Add API for access untrusted SSL server |
746
-| 0.5.2 | Fix improper url params bug [#26](https://github.com/wkh237/react-native-fetch-blob/issues/26) and change IOS HTTP implementation from NSURLConnection to NSURLSession |
747
-| 0.5.0 | Upload/download with direct access to file storage, and also added file access APIs |
748
-| 0.4.2 | Supports upload/download progress |
749
-| 0.4.1 | Fix upload form-data missing file extension problem on Android |
750
-| 0.4.0 | Add base-64 encode/decode library and API |
751
-| ~0.3.0 | Upload/Download octet-stream and form-data |
738
+See [release notes](https://github.com/wkh237/react-native-fetch-blob/releases)
752
 
739
 
753
 ### Development
740
 ### Development
754
 
741
 

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

236
     }
236
     }
237
 
237
 
238
     @ReactMethod
238
     @ReactMethod
239
-    public void enableProgressReport(String taskId) {
240
-        RNFetchBlobReq.progressReport.put(taskId, true);
239
+    public void enableProgressReport(String taskId, int interval, int count) {
240
+        RNFetchBlobProgressConfig config = new RNFetchBlobProgressConfig(true, interval, count, RNFetchBlobProgressConfig.ReportType.Download);
241
+        RNFetchBlobReq.progressReport.put(taskId, config);
241
     }
242
     }
242
 
243
 
243
     @ReactMethod
244
     @ReactMethod
244
-    public void enableUploadProgressReport(String taskId) {
245
-        RNFetchBlobReq.uploadProgressReport.put(taskId, true);
245
+    public void enableUploadProgressReport(String taskId, int interval, int count) {
246
+        RNFetchBlobProgressConfig config = new RNFetchBlobProgressConfig(true, interval, count, RNFetchBlobProgressConfig.ReportType.Upload);
247
+        RNFetchBlobReq.uploadProgressReport.put(taskId, config);
246
     }
248
     }
247
 
249
 
248
     @ReactMethod
250
     @ReactMethod

+ 12
- 8
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java View File

31
     RNFetchBlobReq.RequestType requestType;
31
     RNFetchBlobReq.RequestType requestType;
32
     MediaType mime;
32
     MediaType mime;
33
     File bodyCache;
33
     File bodyCache;
34
+    int reported = 0;
34
     Boolean chunkedEncoding = false;
35
     Boolean chunkedEncoding = false;
35
 
36
 
36
 
37
 
370
      * @param written
371
      * @param written
371
      */
372
      */
372
     private void emitUploadProgress(int written) {
373
     private void emitUploadProgress(int written) {
373
-        WritableMap args = Arguments.createMap();
374
-        args.putString("taskId", mTaskId);
375
-        args.putString("written", String.valueOf(written));
376
-        args.putString("total", String.valueOf(contentLength));
377
-
378
-        // emit event to js context
379
-        RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
380
-                .emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
374
+        RNFetchBlobProgressConfig config = RNFetchBlobReq.getReportUploadProgress(mTaskId);
375
+        if(config != null && contentLength != 0 && config.shouldReport((float)written/contentLength)) {
376
+            WritableMap args = Arguments.createMap();
377
+            args.putString("taskId", mTaskId);
378
+            args.putString("written", String.valueOf(written));
379
+            args.putString("total", String.valueOf(contentLength));
380
+
381
+            // emit event to js context
382
+            RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
383
+                    .emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
384
+        }
381
     }
385
     }
382
 
386
 
383
 }
387
 }

+ 39
- 0
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobProgressConfig.java View File

1
+package com.RNFetchBlob;
2
+
3
+/**
4
+ * Created by wkh237 on 2016/9/24.
5
+ */
6
+public class RNFetchBlobProgressConfig {
7
+
8
+    public enum ReportType {
9
+        Upload,
10
+        Download
11
+    };
12
+
13
+    long lastTick = 0;
14
+    int tick = 0;
15
+    int count = -1;
16
+    public int interval = -1;
17
+    public boolean enable = false;
18
+    public ReportType type = ReportType.Download;
19
+
20
+    RNFetchBlobProgressConfig(boolean report, int interval, int count, ReportType type) {
21
+        this.enable = report;
22
+        this.interval = interval;
23
+        this.type = type;
24
+        this.count = count;
25
+    }
26
+
27
+    public boolean shouldReport(float progress) {
28
+        boolean checkCount = true;
29
+        if(count > 0 && progress > 0)
30
+            checkCount = Math.floor(progress*count)> tick;
31
+        boolean result = (System.currentTimeMillis() - lastTick> interval) && enable && checkCount;
32
+        if(result) {
33
+            tick++;
34
+            lastTick = System.currentTimeMillis();
35
+        }
36
+        return result;
37
+    }
38
+
39
+}

+ 10
- 9
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java View File

70
     }
70
     }
71
 
71
 
72
     public static HashMap<String, Call> taskTable = new HashMap<>();
72
     public static HashMap<String, Call> taskTable = new HashMap<>();
73
-    static HashMap<String, Boolean> progressReport = new HashMap<>();
74
-    static HashMap<String, Boolean> uploadProgressReport = new HashMap<>();
73
+    static HashMap<String, RNFetchBlobProgressConfig> progressReport = new HashMap<>();
74
+    static HashMap<String, RNFetchBlobProgressConfig> uploadProgressReport = new HashMap<>();
75
     static ConnectionPool pool = new ConnectionPool();
75
     static ConnectionPool pool = new ConnectionPool();
76
 
76
 
77
     ReactApplicationContext ctx;
77
     ReactApplicationContext ctx;
92
     ResponseFormat responseFormat = ResponseFormat.Auto;
92
     ResponseFormat responseFormat = ResponseFormat.Auto;
93
     WritableMap respInfo;
93
     WritableMap respInfo;
94
     boolean timeout = false;
94
     boolean timeout = false;
95
-
96
     ArrayList<String> redirects = new ArrayList<>();
95
     ArrayList<String> redirects = new ArrayList<>();
97
 
96
 
98
     public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, final Callback callback) {
97
     public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, final Callback callback) {
221
                 }
220
                 }
222
             }
221
             }
223
 
222
 
224
-            if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put")) {
223
+            if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
225
                 String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase();
224
                 String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase();
226
 
225
 
227
                 if(rawRequestBodyArray != null) {
226
                 if(rawRequestBodyArray != null) {
282
                     break;
281
                     break;
283
 
282
 
284
                 case WithoutBody:
283
                 case WithoutBody:
285
-                    if(method.equalsIgnoreCase("POST") || method.equalsIgnoreCase("PUT"))
284
+                    if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch"))
286
                     {
285
                     {
287
                         builder.method(method, RequestBody.create(null, new byte[0]));
286
                         builder.method(method, RequestBody.create(null, new byte[0]));
288
                     }
287
                     }
396
                         DownloadManager dm = (DownloadManager)RNFetchBlob.RCTContext.getSystemService(RNFetchBlob.RCTContext.DOWNLOAD_SERVICE);
395
                         DownloadManager dm = (DownloadManager)RNFetchBlob.RCTContext.getSystemService(RNFetchBlob.RCTContext.DOWNLOAD_SERVICE);
397
                         dm.addCompletedDownload(title, desc, scannable, mime, destPath, contentLength, notification);
396
                         dm.addCompletedDownload(title, desc, scannable, mime, destPath, contentLength, notification);
398
                     }
397
                     }
398
+
399
                     done(response);
399
                     done(response);
400
                 }
400
                 }
401
             });
401
             });
490
                 } catch (Exception ignored) {
490
                 } catch (Exception ignored) {
491
                     ignored.printStackTrace();
491
                     ignored.printStackTrace();
492
                 }
492
                 }
493
+                this.destPath = this.destPath.replace("?append=true", "");
493
                 callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
494
                 callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
494
                 break;
495
                 break;
495
             default:
496
             default:
510
      * @param taskId Task ID of the HTTP task.
511
      * @param taskId Task ID of the HTTP task.
511
      * @return Task ID of the target task
512
      * @return Task ID of the target task
512
      */
513
      */
513
-    public static boolean isReportProgress(String taskId) {
514
-        if(!progressReport.containsKey(taskId)) return false;
514
+    public static RNFetchBlobProgressConfig getReportProgress(String taskId) {
515
+        if(!progressReport.containsKey(taskId)) return null;
515
         return progressReport.get(taskId);
516
         return progressReport.get(taskId);
516
     }
517
     }
517
 
518
 
520
      * @param taskId Task ID of the HTTP task.
521
      * @param taskId Task ID of the HTTP task.
521
      * @return Task ID of the target task
522
      * @return Task ID of the target task
522
      */
523
      */
523
-    public static boolean isReportUploadProgress(String taskId) {
524
-        if(!uploadProgressReport.containsKey(taskId)) return false;
524
+    public static RNFetchBlobProgressConfig getReportUploadProgress(String taskId) {
525
+        if(!uploadProgressReport.containsKey(taskId)) return null;
525
         return uploadProgressReport.get(taskId);
526
         return uploadProgressReport.get(taskId);
526
     }
527
     }
527
 
528
 

+ 4
- 6
src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobDefaultResp.java View File

1
 package com.RNFetchBlob.Response;
1
 package com.RNFetchBlob.Response;
2
 
2
 
3
-import com.RNFetchBlob.RNFetchBlob;
4
 import com.RNFetchBlob.RNFetchBlobConst;
3
 import com.RNFetchBlob.RNFetchBlobConst;
4
+import com.RNFetchBlob.RNFetchBlobProgressConfig;
5
 import com.RNFetchBlob.RNFetchBlobReq;
5
 import com.RNFetchBlob.RNFetchBlobReq;
6
 import com.facebook.react.bridge.Arguments;
6
 import com.facebook.react.bridge.Arguments;
7
 import com.facebook.react.bridge.ReactApplicationContext;
7
 import com.facebook.react.bridge.ReactApplicationContext;
11
 import java.io.IOException;
11
 import java.io.IOException;
12
 import java.nio.charset.Charset;
12
 import java.nio.charset.Charset;
13
 
13
 
14
-import okhttp3.Call;
15
-import okhttp3.Callback;
16
 import okhttp3.MediaType;
14
 import okhttp3.MediaType;
17
-import okhttp3.Response;
18
 import okhttp3.ResponseBody;
15
 import okhttp3.ResponseBody;
19
 import okio.Buffer;
16
 import okio.Buffer;
20
 import okio.BufferedSource;
17
 import okio.BufferedSource;
21
-import okio.ForwardingSource;
22
 import okio.Okio;
18
 import okio.Okio;
23
 import okio.Source;
19
 import okio.Source;
24
 import okio.Timeout;
20
 import okio.Timeout;
69
 
65
 
70
             long read =  mOriginalSource.read(sink, byteCount);
66
             long read =  mOriginalSource.read(sink, byteCount);
71
             bytesRead += read > 0 ? read : 0;
67
             bytesRead += read > 0 ? read : 0;
72
-            if(RNFetchBlobReq.isReportProgress(mTaskId)) {
68
+            RNFetchBlobProgressConfig reportConfig = RNFetchBlobReq.getReportProgress(mTaskId);
69
+            long cLen = contentLength();
70
+            if(reportConfig != null && cLen != 0 && reportConfig.shouldReport(bytesRead/contentLength())) {
73
                 WritableMap args = Arguments.createMap();
71
                 WritableMap args = Arguments.createMap();
74
                 args.putString("taskId", mTaskId);
72
                 args.putString("taskId", mTaskId);
75
                 args.putString("written", String.valueOf(bytesRead));
73
                 args.putString("written", String.valueOf(bytesRead));

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

3
 import android.util.Log;
3
 import android.util.Log;
4
 
4
 
5
 import com.RNFetchBlob.RNFetchBlobConst;
5
 import com.RNFetchBlob.RNFetchBlobConst;
6
+import com.RNFetchBlob.RNFetchBlobProgressConfig;
6
 import com.RNFetchBlob.RNFetchBlobReq;
7
 import com.RNFetchBlob.RNFetchBlobReq;
7
 import com.facebook.react.bridge.Arguments;
8
 import com.facebook.react.bridge.Arguments;
8
 import com.facebook.react.bridge.ReactApplicationContext;
9
 import com.facebook.react.bridge.ReactApplicationContext;
41
         assert path != null;
42
         assert path != null;
42
         this.mPath = path;
43
         this.mPath = path;
43
         if (path != null) {
44
         if (path != null) {
45
+            boolean appendToExistingFile = path.contains("?append=true");
46
+            path = path.replace("?append=true", "");
47
+            mPath = path;
44
             File f = new File(path);
48
             File f = new File(path);
45
             if(f.exists() == false)
49
             if(f.exists() == false)
46
                 f.createNewFile();
50
                 f.createNewFile();
47
-            ofStream = new FileOutputStream(new File(path));
51
+            ofStream = new FileOutputStream(new File(path), appendToExistingFile);
48
         }
52
         }
49
     }
53
     }
50
 
54
 
67
     private class ProgressReportingSource implements Source {
71
     private class ProgressReportingSource implements Source {
68
         @Override
72
         @Override
69
         public long read(Buffer sink, long byteCount) throws IOException {
73
         public long read(Buffer sink, long byteCount) throws IOException {
70
-            byte [] bytes = new byte[(int) byteCount];
71
-            long read = originalBody.byteStream().read(bytes, 0, (int) byteCount);
72
-            bytesDownloaded += read > 0 ? read : 0;
73
-            Log.i("bytes downloaded", String.valueOf(byteCount) +"/"+ String.valueOf(read) + "=" + String.valueOf(bytesDownloaded));
74
-            if(read > 0 ) {
75
-                ofStream.write(bytes, 0, (int) read);
74
+            try {
75
+                byte[] bytes = new byte[(int) byteCount];
76
+                long read = originalBody.byteStream().read(bytes, 0, (int) byteCount);
77
+                bytesDownloaded += read > 0 ? read : 0;
78
+                Log.i("bytes downloaded", String.valueOf(byteCount) + "/" + String.valueOf(read) + "=" + String.valueOf(bytesDownloaded));
79
+                if (read > 0) {
80
+                    ofStream.write(bytes, 0, (int) read);
81
+                }
82
+                RNFetchBlobProgressConfig reportConfig = RNFetchBlobReq.getReportProgress(mTaskId);
83
+                if (reportConfig != null && contentLength() != 0 &&reportConfig.shouldReport(bytesDownloaded / contentLength())) {
84
+                    WritableMap args = Arguments.createMap();
85
+                    args.putString("taskId", mTaskId);
86
+                    args.putString("written", String.valueOf(bytesDownloaded));
87
+                    args.putString("total", String.valueOf(contentLength()));
88
+                    rctContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
89
+                            .emit(RNFetchBlobConst.EVENT_PROGRESS, args);
90
+                }
91
+                return read;
92
+            } catch(Exception ex) {
93
+                return -1;
76
             }
94
             }
77
-            if(RNFetchBlobReq.isReportProgress(mTaskId)) {
78
-                WritableMap args = Arguments.createMap();
79
-                args.putString("taskId", mTaskId);
80
-                args.putString("written", String.valueOf(bytesDownloaded));
81
-                args.putString("total", String.valueOf(contentLength()));
82
-                rctContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
83
-                        .emit(RNFetchBlobConst.EVENT_PROGRESS, args);
84
-            }
85
-            return read;
86
         }
95
         }
87
 
96
 
88
         @Override
97
         @Override

+ 21
- 20
src/fs.js View File

61
   return new Promise((resolve, reject) => {
61
   return new Promise((resolve, reject) => {
62
     let handler = (err) => {
62
     let handler = (err) => {
63
       if(err)
63
       if(err)
64
-      reject(err)
64
+        reject(new Error(err))
65
       else
65
       else
66
-      resolve()
66
+        resolve()
67
     }
67
     }
68
     if(encoding.toLowerCase() === 'ascii') {
68
     if(encoding.toLowerCase() === 'ascii') {
69
       if(Array.isArray(data))
69
       if(Array.isArray(data))
70
         RNFetchBlob.createFileASCII(path, data, handler)
70
         RNFetchBlob.createFileASCII(path, data, handler)
71
       else
71
       else
72
-        reject('`data` of ASCII file must be an array contains numbers')
72
+        reject(new Error('`data` of ASCII file must be an array contains numbers'))
73
     }
73
     }
74
     else {
74
     else {
75
       RNFetchBlob.createFile(path, data, encoding, handler)
75
       RNFetchBlob.createFile(path, data, encoding, handler)
96
   return new Promise((resolve, reject) => {
96
   return new Promise((resolve, reject) => {
97
     RNFetchBlob.writeStream(path, encoding || 'base64', append || false, (err, streamId:string) => {
97
     RNFetchBlob.writeStream(path, encoding || 'base64', append || false, (err, streamId:string) => {
98
       if(err)
98
       if(err)
99
-        reject(err)
99
+        reject(new Error(err))
100
       else
100
       else
101
         resolve(new RNFetchBlobWriteStream(streamId, encoding))
101
         resolve(new RNFetchBlobWriteStream(streamId, encoding))
102
     })
102
     })
129
   return new Promise((resolve, reject) => {
129
   return new Promise((resolve, reject) => {
130
     RNFetchBlob.mkdir(path, (err, res) => {
130
     RNFetchBlob.mkdir(path, (err, res) => {
131
       if(err)
131
       if(err)
132
-        reject(err)
132
+        reject(new Error(err))
133
       else
133
       else
134
         resolve()
134
         resolve()
135
     })
135
     })
145
  */
145
  */
146
 function readFile(path:string, encoding:string, bufferSize:?number):Promise<any> {
146
 function readFile(path:string, encoding:string, bufferSize:?number):Promise<any> {
147
   if(typeof path !== 'string')
147
   if(typeof path !== 'string')
148
-    return Promise.reject('Invalid argument "path" ')
148
+    return Promise.reject(new Error('Invalid argument "path" '))
149
   return RNFetchBlob.readFile(path, encoding)
149
   return RNFetchBlob.readFile(path, encoding)
150
 }
150
 }
151
 
151
 
162
     return Promise.reject('Invalid argument "path" ')
162
     return Promise.reject('Invalid argument "path" ')
163
   if(encoding.toLocaleLowerCase() === 'ascii') {
163
   if(encoding.toLocaleLowerCase() === 'ascii') {
164
     if(!Array.isArray(data))
164
     if(!Array.isArray(data))
165
-      Promise.reject(`Expected "data" is an Array when encoding is "ascii", however got ${typeof data}`)
165
+      Promise.reject(new Error(`Expected "data" is an Array when encoding is "ascii", however got ${typeof data}`))
166
     else
166
     else
167
       return RNFetchBlob.writeFileArray(path, data, false);
167
       return RNFetchBlob.writeFileArray(path, data, false);
168
   } else {
168
   } else {
169
     if(typeof data !== 'string')
169
     if(typeof data !== 'string')
170
-      Promise.reject(`Expected "data" is a String when encoding is "utf8" or "base64", however got ${typeof data}`)
170
+      Promise.reject(new Error(`Expected "data" is a String when encoding is "utf8" or "base64", however got ${typeof data}`))
171
     else
171
     else
172
       return RNFetchBlob.writeFile(path, encoding, data, false);
172
       return RNFetchBlob.writeFile(path, encoding, data, false);
173
   }
173
   }
179
     return Promise.reject('Invalid argument "path" ')
179
     return Promise.reject('Invalid argument "path" ')
180
   if(encoding.toLocaleLowerCase() === 'ascii') {
180
   if(encoding.toLocaleLowerCase() === 'ascii') {
181
     if(!Array.isArray(data))
181
     if(!Array.isArray(data))
182
-      Promise.reject(`Expected "data" is an Array when encoding is "ascii", however got ${typeof data}`)
182
+      Promise.reject(new Error(`Expected "data" is an Array when encoding is "ascii", however got ${typeof data}`))
183
     else
183
     else
184
       return RNFetchBlob.writeFileArray(path, data, true);
184
       return RNFetchBlob.writeFileArray(path, data, true);
185
   } else {
185
   } else {
186
     if(typeof data !== 'string')
186
     if(typeof data !== 'string')
187
-      Promise.reject(`Expected "data" is a String when encoding is "utf8" or "base64", however got ${typeof data}`)
187
+      Promise.reject(new Error(`Expected "data" is a String when encoding is "utf8" or "base64", however got ${typeof data}`))
188
     else
188
     else
189
       return RNFetchBlob.writeFile(path, encoding, data, true);
189
       return RNFetchBlob.writeFile(path, encoding, data, true);
190
   }
190
   }
199
   return new Promise((resolve, reject) => {
199
   return new Promise((resolve, reject) => {
200
     RNFetchBlob.stat(path, (err, stat) => {
200
     RNFetchBlob.stat(path, (err, stat) => {
201
       if(err)
201
       if(err)
202
-        reject(err)
202
+        reject(new Error(err))
203
       else
203
       else
204
         resolve(stat)
204
         resolve(stat)
205
     })
205
     })
215
   return new Promise((resolve, reject) => {
215
   return new Promise((resolve, reject) => {
216
     RNFetchBlob.scanFile(pairs, (err) => {
216
     RNFetchBlob.scanFile(pairs, (err) => {
217
       if(err)
217
       if(err)
218
-        reject(err)
218
+        reject(new Error(err))
219
       else
219
       else
220
         resolve()
220
         resolve()
221
     })
221
     })
226
   return new Promise((resolve, reject) => {
226
   return new Promise((resolve, reject) => {
227
     RNFetchBlob.cp(path, dest, (err, res) => {
227
     RNFetchBlob.cp(path, dest, (err, res) => {
228
       if(err)
228
       if(err)
229
-        reject(err)
229
+        reject(new Error(err))
230
       else
230
       else
231
         resolve(res)
231
         resolve(res)
232
     })
232
     })
237
   return new Promise((resolve, reject) => {
237
   return new Promise((resolve, reject) => {
238
     RNFetchBlob.mv(path, dest, (err, res) => {
238
     RNFetchBlob.mv(path, dest, (err, res) => {
239
       if(err)
239
       if(err)
240
-        reject(err)
240
+        reject(new Error(err))
241
       else
241
       else
242
         resolve(res)
242
         resolve(res)
243
     })
243
     })
248
   return new Promise((resolve, reject) => {
248
   return new Promise((resolve, reject) => {
249
     RNFetchBlob.lstat(path, (err, stat) => {
249
     RNFetchBlob.lstat(path, (err, stat) => {
250
       if(err)
250
       if(err)
251
-        reject(err)
251
+        reject(new Error(err))
252
       else
252
       else
253
         resolve(stat)
253
         resolve(stat)
254
     })
254
     })
259
   return new Promise((resolve, reject) => {
259
   return new Promise((resolve, reject) => {
260
     RNFetchBlob.ls(path, (err, res) => {
260
     RNFetchBlob.ls(path, (err, res) => {
261
       if(err)
261
       if(err)
262
-        reject(err)
262
+        reject(new Error(err))
263
       else
263
       else
264
         resolve(res)
264
         resolve(res)
265
     })
265
     })
274
 function unlink(path:string):Promise {
274
 function unlink(path:string):Promise {
275
   return new Promise((resolve, reject) => {
275
   return new Promise((resolve, reject) => {
276
     RNFetchBlob.unlink(path, (err) => {
276
     RNFetchBlob.unlink(path, (err) => {
277
-      if(err)
278
-        reject(err)
277
+      if(err) {
278
+        reject(new Error(err))
279
+      }
279
       else
280
       else
280
         resolve()
281
         resolve()
281
     })
282
     })
295
         resolve(exist)
296
         resolve(exist)
296
       })
297
       })
297
     } catch(err) {
298
     } catch(err) {
298
-      reject(err)
299
+      reject(new Error(err))
299
     }
300
     }
300
   })
301
   })
301
 
302
 
331
         resolve(isDir)
332
         resolve(isDir)
332
       })
333
       })
333
     } catch(err) {
334
     } catch(err) {
334
-      reject(err)
335
+      reject(new Error(err))
335
     }
336
     }
336
   })
337
   })
337
 
338
 

+ 59
- 5
src/index.js View File

204
   // create task ID for receiving progress event
204
   // create task ID for receiving progress event
205
   let taskId = getUUID()
205
   let taskId = getUUID()
206
   let options = this || {}
206
   let options = this || {}
207
-  let subscription, subscriptionUpload, stateEvent
207
+  let subscription, subscriptionUpload, stateEvent, partEvent
208
   let respInfo = {}
208
   let respInfo = {}
209
   let [method, url, headers, body] = [...args]
209
   let [method, url, headers, body] = [...args]
210
 
210
 
241
       console.log(e , 'EXPIRED!!')
241
       console.log(e , 'EXPIRED!!')
242
       if(e.taskId === taskId && promise.onExpire) {
242
       if(e.taskId === taskId && promise.onExpire) {
243
         promise.onExpire(e)
243
         promise.onExpire(e)
244
+
245
+    partEvent = emitter.addListener('RNFetchBlobServerPush', (e) => {
246
+      if(e.taskId === taskId && promise.onPartData) {
247
+        promise.onPartData(e.chunk)
244
       }
248
       }
245
     })
249
     })
246
 
250
 
269
       subscription.remove()
273
       subscription.remove()
270
       subscriptionUpload.remove()
274
       subscriptionUpload.remove()
271
       stateEvent.remove()
275
       stateEvent.remove()
276
+      partEvent.remove()
272
       delete promise['progress']
277
       delete promise['progress']
273
       delete promise['uploadProgress']
278
       delete promise['uploadProgress']
274
       delete promise['stateChange']
279
       delete promise['stateChange']
280
+      delete promise['part']
275
       delete promise['cancel']
281
       delete promise['cancel']
276
       // delete promise['expire']
282
       // delete promise['expire']
277
       promise.cancel = () => {}
283
       promise.cancel = () => {}
295
 
301
 
296
   // extend Promise object, add `progress`, `uploadProgress`, and `cancel`
302
   // extend Promise object, add `progress`, `uploadProgress`, and `cancel`
297
   // method for register progress event handler and cancel request.
303
   // method for register progress event handler and cancel request.
298
-  promise.progress = (fn) => {
304
+  // Add second parameter for performance purpose #140
305
+  // When there's only one argument pass to this method, use default `interval`
306
+  // and `count`, otherwise use the given on.
307
+  // TODO : code refactor, move `uploadProgress` and `progress` to StatefulPromise
308
+  promise.progress = (...args) => {
309
+    let interval = 250
310
+    let count = -1
311
+    let fn = () => {}
312
+    if(args.length === 2) {
313
+      interval = args[0].interval || interval
314
+      count = args[0].count || count
315
+      fn = args[1]
316
+    }
317
+    else {
318
+      fn = args[0]
319
+    }
299
     promise.onProgress = fn
320
     promise.onProgress = fn
300
-    RNFetchBlob.enableProgressReport(taskId)
321
+    RNFetchBlob.enableProgressReport(taskId, interval, count)
301
     return promise
322
     return promise
302
   }
323
   }
303
-  promise.uploadProgress = (fn) => {
324
+  promise.uploadProgress = (...args) => {
325
+    let interval = 250
326
+    let count = -1
327
+    let fn = () => {}
328
+    if(args.length === 2) {
329
+      interval = args[0].interval || interval
330
+      count = args[0].count || count
331
+      fn = args[1]
332
+    }
333
+    else {
334
+      fn = args[0]
335
+    }
304
     promise.onUploadProgress = fn
336
     promise.onUploadProgress = fn
305
-    RNFetchBlob.enableUploadProgressReport(taskId)
337
+    RNFetchBlob.enableUploadProgressReport(taskId, interval, count)
338
+    return promise
339
+  }
340
+  promise.part = (fn) => {
341
+    promise.onPartData = fn
306
     return promise
342
     return promise
307
   }
343
   }
308
   promise.stateChange = (fn) => {
344
   promise.stateChange = (fn) => {
356
     this.info = ():RNFetchBlobResponseInfo => {
392
     this.info = ():RNFetchBlobResponseInfo => {
357
       return this.respInfo
393
       return this.respInfo
358
     }
394
     }
395
+
396
+    this.array = ():Promise<Array> => {
397
+      let cType = info.headers['Content-Type'] || info.headers['content-type']
398
+      return new Promise((resolve, reject) => {
399
+        switch(this.type) {
400
+          case 'base64':
401
+            // TODO : base64 to array buffer
402
+          break
403
+          case 'path':
404
+            fs.readFile(this.data, 'ascii').then(resolve)
405
+          break
406
+          default:
407
+            // TODO : text to array buffer
408
+          break
409
+        }
410
+      })
411
+    }
412
+
359
     /**
413
     /**
360
      * Convert result to javascript RNFetchBlob object.
414
      * Convert result to javascript RNFetchBlob object.
361
      * @return {Promise<Blob>} Return a promise resolves Blob object.
415
      * @return {Promise<Blob>} Return a promise resolves Blob object.

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

12
 		A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */; };
12
 		A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */; };
13
 		A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = A15C30131CD25C330074CB35 /* RNFetchBlob.m */; };
13
 		A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = A15C30131CD25C330074CB35 /* RNFetchBlob.m */; };
14
 		A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */ = {isa = PBXBuildFile; fileRef = A15C30111CD25C330074CB35 /* RNFetchBlob.h */; };
14
 		A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */ = {isa = PBXBuildFile; fileRef = A15C30111CD25C330074CB35 /* RNFetchBlob.h */; };
15
+		A19B48251D98102400E6868A /* RNFetchBlobProgress.m in Sources */ = {isa = PBXBuildFile; fileRef = A19B48241D98102400E6868A /* RNFetchBlobProgress.m */; };
15
 		A1AAE2991D300E4D0051D11C /* RNFetchBlobReqBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */; };
16
 		A1AAE2991D300E4D0051D11C /* RNFetchBlobReqBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */; };
16
 /* End PBXBuildFile section */
17
 /* End PBXBuildFile section */
17
 
18
 
37
 		A15C300E1CD25C330074CB35 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFetchBlob.a; sourceTree = BUILT_PRODUCTS_DIR; };
38
 		A15C300E1CD25C330074CB35 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFetchBlob.a; sourceTree = BUILT_PRODUCTS_DIR; };
38
 		A15C30111CD25C330074CB35 /* RNFetchBlob.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFetchBlob.h; path = RNFetchBlob/RNFetchBlob.h; sourceTree = "<group>"; };
39
 		A15C30111CD25C330074CB35 /* RNFetchBlob.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFetchBlob.h; path = RNFetchBlob/RNFetchBlob.h; sourceTree = "<group>"; };
39
 		A15C30131CD25C330074CB35 /* RNFetchBlob.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFetchBlob.m; path = RNFetchBlob/RNFetchBlob.m; sourceTree = "<group>"; };
40
 		A15C30131CD25C330074CB35 /* RNFetchBlob.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFetchBlob.m; path = RNFetchBlob/RNFetchBlob.m; sourceTree = "<group>"; };
41
+		A19B48231D98100800E6868A /* RNFetchBlobProgress.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobProgress.h; sourceTree = "<group>"; };
42
+		A19B48241D98102400E6868A /* RNFetchBlobProgress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobProgress.m; sourceTree = "<group>"; };
40
 		A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobReqBuilder.h; sourceTree = "<group>"; };
43
 		A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobReqBuilder.h; sourceTree = "<group>"; };
41
 		A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobReqBuilder.m; sourceTree = "<group>"; };
44
 		A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobReqBuilder.m; sourceTree = "<group>"; };
42
 		A1F950181D7E9134002A95A6 /* IOS7Polyfill.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IOS7Polyfill.h; sourceTree = "<group>"; };
45
 		A1F950181D7E9134002A95A6 /* IOS7Polyfill.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IOS7Polyfill.h; sourceTree = "<group>"; };
63
 		A15C30051CD25C330074CB35 = {
66
 		A15C30051CD25C330074CB35 = {
64
 			isa = PBXGroup;
67
 			isa = PBXGroup;
65
 			children = (
68
 			children = (
69
+				A19B48241D98102400E6868A /* RNFetchBlobProgress.m */,
70
+				A19B48231D98100800E6868A /* RNFetchBlobProgress.h */,
66
 				A1F950181D7E9134002A95A6 /* IOS7Polyfill.h */,
71
 				A1F950181D7E9134002A95A6 /* IOS7Polyfill.h */,
67
 				A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */,
72
 				A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */,
68
 				A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */,
73
 				A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */,
147
 				A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */,
152
 				A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */,
148
 				A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */,
153
 				A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */,
149
 				A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */,
154
 				A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */,
155
+				A19B48251D98102400E6868A /* RNFetchBlobProgress.m in Sources */,
150
 				A1AAE2991D300E4D0051D11C /* RNFetchBlobReqBuilder.m in Sources */,
156
 				A1AAE2991D300E4D0051D11C /* RNFetchBlobReqBuilder.m in Sources */,
151
 				A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */,
157
 				A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */,
152
 			);
158
 			);

+ 9
- 5
src/ios/RNFetchBlob/RNFetchBlob.m View File

13
 #import "RNFetchBlobNetwork.h"
13
 #import "RNFetchBlobNetwork.h"
14
 #import "RNFetchBlobConst.h"
14
 #import "RNFetchBlobConst.h"
15
 #import "RNFetchBlobReqBuilder.h"
15
 #import "RNFetchBlobReqBuilder.h"
16
+#import "RNFetchBlobProgress.h"
16
 
17
 
17
 
18
 
18
 __strong RCTBridge * bridgeRef;
19
 __strong RCTBridge * bridgeRef;
216
     NSError * error = nil;
217
     NSError * error = nil;
217
     NSString * tmpPath = nil;
218
     NSString * tmpPath = nil;
218
     [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
219
     [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
219
-    if(error == nil)
220
+    if(error == nil || [[NSFileManager defaultManager] fileExistsAtPath:path] == NO)
220
         callback(@[[NSNull null]]);
221
         callback(@[[NSNull null]]);
221
     else
222
     else
222
         callback(@[[NSString stringWithFormat:@"failed to unlink file or path at %@", path]]);
223
         callback(@[[NSString stringWithFormat:@"failed to unlink file or path at %@", path]]);
417
 }
418
 }
418
 
419
 
419
 #pragma mark - net.enableProgressReport
420
 #pragma mark - net.enableProgressReport
420
-RCT_EXPORT_METHOD(enableProgressReport:(NSString *)taskId {
421
-    [RNFetchBlobNetwork enableProgressReport:taskId];
421
+RCT_EXPORT_METHOD(enableProgressReport:(NSString *)taskId interval:(nonnull NSNumber*)interval count:(nonnull NSNumber*)count  {
422
+    
423
+    RNFetchBlobProgress * cfg = [[RNFetchBlobProgress alloc] initWithType:Download interval:interval count:count];
424
+    [RNFetchBlobNetwork enableProgressReport:taskId config:cfg];
422
 })
425
 })
423
 
426
 
424
 #pragma mark - net.enableUploadProgressReport
427
 #pragma mark - net.enableUploadProgressReport
425
-RCT_EXPORT_METHOD(enableUploadProgressReport:(NSString *)taskId {
426
-    [RNFetchBlobNetwork enableUploadProgress:taskId];
428
+RCT_EXPORT_METHOD(enableUploadProgressReport:(NSString *)taskId interval:(nonnull NSNumber*)interval count:(nonnull NSNumber*)count{
429
+    RNFetchBlobProgress * cfg = [[RNFetchBlobProgress alloc] initWithType:Upload interval:interval count:count];
430
+    [RNFetchBlobNetwork enableUploadProgress:taskId config:cfg];
427
 })
431
 })
428
 
432
 
429
 #pragma mark - fs.slice
433
 #pragma mark - fs.slice

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

19
 
19
 
20
 extern NSString *const EVENT_EXPIRE;
20
 extern NSString *const EVENT_EXPIRE;
21
 extern NSString *const EVENT_PROGRESS;
21
 extern NSString *const EVENT_PROGRESS;
22
+extern NSString *const EVENT_SERVER_PUSH;
22
 extern NSString *const EVENT_PROGRESS_UPLOAD;
23
 extern NSString *const EVENT_PROGRESS_UPLOAD;
23
 extern NSString *const EVENT_STATE_CHANGE;
24
 extern NSString *const EVENT_STATE_CHANGE;
24
 
25
 

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

21
 extern NSString *const CONFIG_EXTRA_BLOB_CTYPE = @"binaryContentTypes";
21
 extern NSString *const CONFIG_EXTRA_BLOB_CTYPE = @"binaryContentTypes";
22
 
22
 
23
 extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState";
23
 extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState";
24
+extern NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush";
24
 extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
25
 extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
25
 extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
26
 extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
26
 extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";
27
 extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";
40
 // response type
41
 // response type
41
 extern NSString *const RESP_TYPE_BASE64 = @"base64";
42
 extern NSString *const RESP_TYPE_BASE64 = @"base64";
42
 extern NSString *const RESP_TYPE_UTF8 = @"utf8";
43
 extern NSString *const RESP_TYPE_UTF8 = @"utf8";
43
-extern NSString *const RESP_TYPE_PATH = @"path";
44
+extern NSString *const RESP_TYPE_PATH = @"path";

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

11
 
11
 
12
 #import <Foundation/Foundation.h>
12
 #import <Foundation/Foundation.h>
13
 #import "RCTBridgeModule.h"
13
 #import "RCTBridgeModule.h"
14
+#import "RNFetchBlobProgress.h"
14
 #import "RNFetchBlobFS.h"
15
 #import "RNFetchBlobFS.h"
15
 
16
 
16
 typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error);
17
 typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error);
21
 @property (nullable, nonatomic) NSString * taskId;
22
 @property (nullable, nonatomic) NSString * taskId;
22
 @property (nonatomic) int expectedBytes;
23
 @property (nonatomic) int expectedBytes;
23
 @property (nonatomic) int receivedBytes;
24
 @property (nonatomic) int receivedBytes;
25
+@property (nonatomic) BOOL isServerPush;
24
 @property (nullable, nonatomic) NSMutableData * respData;
26
 @property (nullable, nonatomic) NSMutableData * respData;
25
 @property (strong, nonatomic) RCTResponseSenderBlock callback;
27
 @property (strong, nonatomic) RCTResponseSenderBlock callback;
26
 @property (nullable, nonatomic) RCTBridge * bridge;
28
 @property (nullable, nonatomic) RCTBridge * bridge;
40
 - (nullable id) init;
42
 - (nullable id) init;
41
 - (void) sendRequest;
43
 - (void) sendRequest;
42
 - (void) sendRequest:(NSDictionary  * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
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;
45
++ (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config;
46
++ (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config;
43
 
47
 
44
 
48
 
45
 
49
 

+ 68
- 24
src/ios/RNFetchBlobNetwork.m View File

18
 #import "RNFetchBlobReqBuilder.h"
18
 #import "RNFetchBlobReqBuilder.h"
19
 #import "IOS7Polyfill.h"
19
 #import "IOS7Polyfill.h"
20
 #import <CommonCrypto/CommonDigest.h>
20
 #import <CommonCrypto/CommonDigest.h>
21
+#import "RNFetchBlobProgress.h"
21
 
22
 
22
 ////////////////////////////////////////
23
 ////////////////////////////////////////
23
 //
24
 //
40
 @interface RNFetchBlobNetwork ()
41
 @interface RNFetchBlobNetwork ()
41
 {
42
 {
42
     BOOL * respFile;
43
     BOOL * respFile;
43
-    BOOL * isIncrement;
44
+    BOOL isNewPart;
45
+    NSMutableData * partBuffer;
44
     NSString * destPath;
46
     NSString * destPath;
45
     NSOutputStream * writeStream;
47
     NSOutputStream * writeStream;
46
     long bodyLength;
48
     long bodyLength;
93
     return self;
95
     return self;
94
 }
96
 }
95
 
97
 
96
-+ (void) enableProgressReport:(NSString *) taskId
98
++ (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config
97
 {
99
 {
98
-    [progressTable setValue:@YES forKey:taskId];
100
+    [progressTable setValue:config forKey:taskId];
99
 }
101
 }
100
 
102
 
101
-+ (void) enableUploadProgress:(NSString *) taskId
103
++ (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config
102
 {
104
 {
103
-    [uploadProgressTable setValue:@YES forKey:taskId];
105
+    [uploadProgressTable setValue:config forKey:taskId];
104
 }
106
 }
105
 
107
 
106
 // removing case from headers
108
 // removing case from headers
145
     isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
147
     isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
146
     redirects = [[NSMutableArray alloc] init];
148
     redirects = [[NSMutableArray alloc] init];
147
     [redirects addObject:req.URL.absoluteString];
149
     [redirects addObject:req.URL.absoluteString];
148
-    
150
+
149
     // set response format
151
     // set response format
150
     NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
152
     NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
151
     if([[rnfbResp lowercaseString] isEqualToString:@"base64"])
153
     if([[rnfbResp lowercaseString] isEqualToString:@"base64"])
165
     // the session trust any SSL certification
167
     // the session trust any SSL certification
166
 //    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
168
 //    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
167
     NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
169
     NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
168
-    
170
+
169
     // set request timeout
171
     // set request timeout
170
     float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
172
     float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
171
     if(timeout > 0)
173
     if(timeout > 0)
202
         respData = [[NSMutableData alloc] init];
204
         respData = [[NSMutableData alloc] init];
203
         respFile = NO;
205
         respFile = NO;
204
     }
206
     }
205
-    
207
+
206
     __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
208
     __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
207
     [taskTable setObject:task forKey:taskId];
209
     [taskTable setObject:task forKey:taskId];
208
     [task resume];
210
     [task resume];
211
     if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
213
     if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
212
         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
214
         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
213
     __block UIApplication * app = [UIApplication sharedApplication];
215
     __block UIApplication * app = [UIApplication sharedApplication];
214
-    
216
+
215
     // #115 handling task expired when application entering backgound for a long time
217
     // #115 handling task expired when application entering backgound for a long time
216
     [app beginBackgroundTaskWithName:taskId expirationHandler:^{
218
     [app beginBackgroundTaskWithName:taskId expirationHandler:^{
217
         NSLog([NSString stringWithFormat:@"session %@ expired event emit", taskId ]);
219
         NSLog([NSString stringWithFormat:@"session %@ expired event emit", taskId ]);
218
         [expirationTable setObject:task forKey:taskId];
220
         [expirationTable setObject:task forKey:taskId];
219
         [app endBackgroundTask:task];
221
         [app endBackgroundTask:task];
220
-        
222
+
221
     }];
223
     }];
222
-    
223
-    
224
+
225
+
224
 }
226
 }
225
 
227
 
226
 // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
228
 // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
259
     {
261
     {
260
         NSDictionary *headers = [httpResponse allHeaderFields];
262
         NSDictionary *headers = [httpResponse allHeaderFields];
261
         NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
263
         NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
264
+        if(self.isServerPush == NO)
265
+        {
266
+            self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"];
267
+        }
268
+        if(self.isServerPush)
269
+        {
270
+            if(partBuffer != nil)
271
+            {
272
+                [self.bridge.eventDispatcher
273
+                 sendDeviceEventWithName:EVENT_SERVER_PUSH
274
+                 body:@{
275
+                        @"taskId": taskId,
276
+                        @"chunk": [partBuffer base64EncodedStringWithOptions:0],
277
+                        }
278
+                 ];
279
+            }
280
+            partBuffer = [[NSMutableData alloc] init];
281
+            completionHandler(NSURLSessionResponseAllow);
282
+            return;
283
+        }
262
         if(respCType != nil)
284
         if(respCType != nil)
263
         {
285
         {
264
             NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
286
             NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
320
         @try{
342
         @try{
321
             NSFileManager * fm = [NSFileManager defaultManager];
343
             NSFileManager * fm = [NSFileManager defaultManager];
322
             NSString * folder = [destPath stringByDeletingLastPathComponent];
344
             NSString * folder = [destPath stringByDeletingLastPathComponent];
323
-            if(![fm fileExistsAtPath:folder]) {
345
+            if(![fm fileExistsAtPath:folder])
346
+            {
324
                 [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
347
                 [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
325
             }
348
             }
326
-            [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
349
+            BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"];
350
+            // For solving #141 append response data if the file already exists
351
+            // base on PR#139 @kejinliang
352
+            if(appendToExistingFile)
353
+            {
354
+                destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
355
+            }
356
+            if (![fm fileExistsAtPath:destPath])
357
+            {
358
+                [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
359
+            }
327
             writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:YES];
360
             writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:YES];
328
             [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
361
             [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
329
             [writeStream open];
362
             [writeStream open];
337
     completionHandler(NSURLSessionResponseAllow);
370
     completionHandler(NSURLSessionResponseAllow);
338
 }
371
 }
339
 
372
 
373
+
340
 // download progress handler
374
 // download progress handler
341
 - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
375
 - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
342
 {
376
 {
377
+    // For #143 handling multipart/x-mixed-replace response
378
+    if(self.isServerPush)
379
+    {
380
+        [partBuffer appendData:data];
381
+        return ;
382
+    }
383
+
343
     NSNumber * received = [NSNumber numberWithLong:[data length]];
384
     NSNumber * received = [NSNumber numberWithLong:[data length]];
344
     receivedBytes += [received longValue];
385
     receivedBytes += [received longValue];
345
     NSString * chunkString = @"";
386
     NSString * chunkString = @"";
357
     {
398
     {
358
         [writeStream write:[data bytes] maxLength:[data length]];
399
         [writeStream write:[data bytes] maxLength:[data length]];
359
     }
400
     }
360
-
361
-    if([progressTable valueForKey:taskId] == @YES)
401
+    RNFetchBlobProgress * pconfig = [progressTable valueForKey:taskId];
402
+    if(expectedBytes == 0)
403
+        return;
404
+    NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
405
+    if(pconfig != nil && [pconfig shouldReport:now])
362
     {
406
     {
363
         [self.bridge.eventDispatcher
407
         [self.bridge.eventDispatcher
364
          sendDeviceEventWithName:EVENT_PROGRESS
408
          sendDeviceEventWithName:EVENT_PROGRESS
380
         session = nil;
424
         session = nil;
381
 }
425
 }
382
 
426
 
427
+
383
 - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
428
 - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
384
 {
429
 {
385
 
430
 
399
     {
444
     {
400
         errMsg = [error localizedDescription];
445
         errMsg = [error localizedDescription];
401
     }
446
     }
402
-    // Fix #72 response with status code 200 ~ 299 considered as success
403
-    else if(respStatus> 299 || respStatus < 200)
404
-    {
405
-        errMsg = [NSString stringWithFormat:@"Request failed, status %d", respStatus];
406
-    }
407
     else
447
     else
408
     {
448
     {
409
         if(respFile == YES)
449
         if(respFile == YES)
419
             // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
459
             // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
420
             // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
460
             // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
421
             NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
461
             NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
422
-            
462
+
423
             if(responseFormat == BASE64)
463
             if(responseFormat == BASE64)
424
             {
464
             {
425
                 rnfbRespType = RESP_TYPE_BASE64;
465
                 rnfbRespType = RESP_TYPE_BASE64;
467
 // upload progress handler
507
 // upload progress handler
468
 - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
508
 - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
469
 {
509
 {
470
-    if([uploadProgressTable valueForKey:taskId] == @YES) {
510
+    RNFetchBlobProgress * pconfig = [uploadProgressTable valueForKey:taskId];
511
+    if(totalBytesExpectedToWrite == 0)
512
+        return;
513
+    NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
514
+    if(pconfig != nil && [pconfig shouldReport:now]) {
471
         [self.bridge.eventDispatcher
515
         [self.bridge.eventDispatcher
472
          sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD
516
          sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD
473
          body:@{
517
          body:@{
474
                 @"taskId": taskId,
518
                 @"taskId": taskId,
475
                 @"written": [NSString stringWithFormat:@"%d", totalBytesWritten],
519
                 @"written": [NSString stringWithFormat:@"%d", totalBytesWritten],
476
-                @"total": [NSString stringWithFormat:@"%d", bodyLength]
520
+                @"total": [NSString stringWithFormat:@"%d", totalBytesExpectedToWrite]
477
                 }
521
                 }
478
          ];
522
          ];
479
     }
523
     }

+ 39
- 0
src/ios/RNFetchBlobProgress.h View File

1
+//
2
+//  RNFetchBlobProgress.h
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/9/25.
6
+//  Copyright © 2016年 wkh237.github.io. All rights reserved.
7
+//
8
+
9
+#ifndef RNFetchBlobProgress_h
10
+#define RNFetchBlobProgress_h
11
+
12
+#import <Foundation/Foundation.h>
13
+
14
+typedef NS_ENUM(NSUInteger, ProgressType) {
15
+    Download,
16
+    Upload,
17
+};
18
+
19
+@interface RNFetchBlobProgress : NSObject
20
+{
21
+    NSNumber * count;
22
+    NSNumber * interval;
23
+    ProgressType type;
24
+    BOOL enable;
25
+    
26
+}
27
+
28
+@property (nonatomic) NSNumber * count;
29
+@property (nonatomic) NSNumber * interval;
30
+@property (nonatomic) NSInteger type;
31
+@property (nonatomic) BOOL enable;
32
+
33
+-(id)initWithType:(ProgressType)type interval:(NSNumber*)interval count:(NSNumber *)count;
34
+-(BOOL)shouldReport:(NSNumber *) nextProgress;
35
+
36
+
37
+@end
38
+
39
+#endif /* RNFetchBlobProgress_h */

+ 57
- 0
src/ios/RNFetchBlobProgress.m View File

1
+//
2
+//  RNFetchBlobProgress.m
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/9/25.
6
+//  Copyright © 2016年 wkh237.github.io. All rights reserved.
7
+//
8
+
9
+#import "RNFetchBlobProgress.h"
10
+
11
+@interface RNFetchBlobProgress ()
12
+{
13
+    float progress;
14
+    int tick;
15
+    double lastTick;
16
+}
17
+@end
18
+
19
+@implementation RNFetchBlobProgress
20
+
21
+-(id)initWithType:(ProgressType)type interval:(NSNumber *)interval count:(NSNumber *)count
22
+{
23
+    self = [super init];
24
+    self.count = count;
25
+    self.interval = [NSNumber numberWithFloat:[interval floatValue] /1000];
26
+    self.type = type;
27
+    self.enable = YES;
28
+    lastTick = 0;
29
+    tick = 1;
30
+    return self;
31
+}
32
+
33
+-(BOOL)shouldReport:(NSNumber *)nextProgress
34
+{
35
+    BOOL result = YES;
36
+    float countF = [self.count floatValue];
37
+    if(countF > 0 && [nextProgress floatValue] > 0)
38
+    {
39
+        result = (int)(floorf([nextProgress floatValue]*countF)) >= tick;
40
+    }
41
+    
42
+    NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
43
+    // NSTimeInterval is defined as double
44
+    NSNumber *timeStampObj = [NSNumber numberWithDouble: timeStamp];
45
+    float delta = [timeStampObj doubleValue] - lastTick;
46
+    BOOL shouldReport = delta > [self.interval doubleValue] && self.enable && result;
47
+    if(shouldReport)
48
+    {
49
+        tick++;
50
+        lastTick = [timeStampObj doubleValue];
51
+    }
52
+    return shouldReport;
53
+    
54
+}
55
+
56
+
57
+@end

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

119
                     }
119
                     }
120
                     else
120
                     else
121
                     {
121
                     {
122
-                        [request setHTTPBody:[NSData dataWithContentsOfFile:orgPath ]];
122
+                        __block NSData * bodyBytes = [NSData dataWithContentsOfFile:orgPath ];
123
+                        [request setHTTPBody:bodyBytes];
123
                     }
124
                     }
124
                 }
125
                 }
125
                 // otherwise convert it as BASE64 data string
126
                 // otherwise convert it as BASE64 data string

+ 1
- 1
src/package.json View File

1
 {
1
 {
2
   "name": "react-native-fetch-blob",
2
   "name": "react-native-fetch-blob",
3
-  "version": "0.9.5-beta.2",
3
+  "version": "0.9.6",
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": {

+ 2
- 2
src/polyfill/Blob.js View File

51
   }
51
   }
52
 
52
 
53
   static setLog(level:number) {
53
   static setLog(level:number) {
54
-    if(number === -1)
54
+    if(level === -1)
55
       log.disable()
55
       log.disable()
56
     else
56
     else
57
       log.level(level)
57
       log.level(level)
224
    * @param  {string} contentType Optional, content type of new Blob object
224
    * @param  {string} contentType Optional, content type of new Blob object
225
    * @return {Blob}
225
    * @return {Blob}
226
    */
226
    */
227
-  slice(start:?number, end:?number, contentType='':?string):Blob {
227
+  slice(start:?number, end:?number, contentType:?string=''):Blob {
228
     if(this._closed)
228
     if(this._closed)
229
       throw 'Blob has been released.'
229
       throw 'Blob has been released.'
230
     log.verbose('slice called', start, end, contentType)
230
     log.verbose('slice called', start, end, contentType)

+ 10
- 4
src/polyfill/XMLHttpRequest.js View File

35
   _response : any = '';
35
   _response : any = '';
36
   _responseText : any = '';
36
   _responseText : any = '';
37
   _responseHeaders : any = {};
37
   _responseHeaders : any = {};
38
-  _responseType : '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' = '';
38
+  _responseType : '' | 'arraybuffer' | 'blob'  | 'json' | 'text' = '';
39
   // TODO : not suppoted ATM
39
   // TODO : not suppoted ATM
40
   _responseURL : null = '';
40
   _responseURL : null = '';
41
   _responseXML : null = '';
41
   _responseXML : null = '';
87
   }
87
   }
88
 
88
 
89
   static setLog(level:number) {
89
   static setLog(level:number) {
90
-    if(number === -1)
90
+    if(level === -1)
91
       log.disable()
91
       log.disable()
92
     else
92
     else
93
       log.level(level)
93
       log.level(level)
272
     if(e.state === "2") {
272
     if(e.state === "2") {
273
       this._responseHeaders = e.headers
273
       this._responseHeaders = e.headers
274
       this._statusText = e.status
274
       this._statusText = e.status
275
-      this._responseType = e.respType || ''
276
       this._status = Math.floor(e.status)
275
       this._status = Math.floor(e.status)
277
       this._dispatchReadStateChange(XMLHttpRequest.HEADERS_RECEIVED)
276
       this._dispatchReadStateChange(XMLHttpRequest.HEADERS_RECEIVED)
278
     }
277
     }
337
     if(resp) {
336
     if(resp) {
338
       let info = resp.respInfo || {}
337
       let info = resp.respInfo || {}
339
       log.debug(this._url, info, info.respType)
338
       log.debug(this._url, info, info.respType)
340
-      switch(info.respType) {
339
+      switch(this._responseType) {
341
         case 'blob' :
340
         case 'blob' :
342
           resp.blob().then((b) => {
341
           resp.blob().then((b) => {
343
             this._responseText = resp.text()
342
             this._responseText = resp.text()
345
             responseDataReady()
344
             responseDataReady()
346
           })
345
           })
347
         break;
346
         break;
347
+        case 'arraybuffer':
348
+          // TODO : to array buffer
349
+        break
350
+        case 'json':
351
+          this._response = resp.json()
352
+          this._responseText = resp.text()
353
+        break
348
         default :
354
         default :
349
           this._responseText = resp.text()
355
           this._responseText = resp.text()
350
           this._response = this.responseText
356
           this._response = this.responseText

+ 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.5-beta.2"
3
+  s.version          = "0.9.6"
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.5-beta.2'}
9
+  s.source       = { :git => "https://github.com/wkh237/react-native-fetch-blob", :tag => 'v0.9.6'}
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'

BIN
test-server/cat_fu_mp4/img00001.jpeg View File


BIN
test-server/cat_fu_mp4/img00002.jpeg View File


BIN
test-server/cat_fu_mp4/img00003.jpeg View File


BIN
test-server/cat_fu_mp4/img00004.jpeg View File


BIN
test-server/cat_fu_mp4/img00005.jpeg View File


BIN
test-server/cat_fu_mp4/img00006.jpeg View File


BIN
test-server/cat_fu_mp4/img00007.jpeg View File


BIN
test-server/cat_fu_mp4/img00008.jpeg View File


BIN
test-server/cat_fu_mp4/img00009.jpeg View File


BIN
test-server/cat_fu_mp4/img00010.jpeg View File


BIN
test-server/cat_fu_mp4/img00011.jpeg View File


BIN
test-server/cat_fu_mp4/img00012.jpeg View File


BIN
test-server/cat_fu_mp4/img00013.jpeg View File


BIN
test-server/cat_fu_mp4/img00014.jpeg View File


BIN
test-server/cat_fu_mp4/img00015.jpeg View File


BIN
test-server/cat_fu_mp4/img00016.jpeg View File


BIN
test-server/cat_fu_mp4/img00017.jpeg View File


BIN
test-server/cat_fu_mp4/img00018.jpeg View File


BIN
test-server/cat_fu_mp4/img00019.jpeg View File


BIN
test-server/cat_fu_mp4/img00020.jpeg View File


BIN
test-server/cat_fu_mp4/img00021.jpeg View File


BIN
test-server/cat_fu_mp4/img00022.jpeg View File


BIN
test-server/cat_fu_mp4/img00023.jpeg View File


BIN
test-server/cat_fu_mp4/img00024.jpeg View File


BIN
test-server/cat_fu_mp4/img00025.jpeg View File


BIN
test-server/cat_fu_mp4/img00026.jpeg View File


BIN
test-server/cat_fu_mp4/img00027.jpeg View File


BIN
test-server/cat_fu_mp4/img00028.jpeg View File


BIN
test-server/cat_fu_mp4/img00029.jpeg View File


BIN
test-server/cat_fu_mp4/img00030.jpeg View File


BIN
test-server/cat_fu_mp4/img00031.jpeg View File


BIN
test-server/cat_fu_mp4/img00032.jpeg View File


BIN
test-server/cat_fu_mp4/img00033.jpeg View File


BIN
test-server/cat_fu_mp4/img00034.jpeg View File


BIN
test-server/cat_fu_mp4/img00035.jpeg View File


BIN
test-server/cat_fu_mp4/img00036.jpeg View File


BIN
test-server/cat_fu_mp4/img00037.jpeg View File


BIN
test-server/cat_fu_mp4/img00038.jpeg View File


BIN
test-server/cat_fu_mp4/img00039.jpeg View File


BIN
test-server/cat_fu_mp4/img00040.jpeg View File


BIN
test-server/cat_fu_mp4/img00041.jpeg View File


BIN
test-server/cat_fu_mp4/img00042.jpeg View File


BIN
test-server/cat_fu_mp4/img00043.jpeg View File


BIN
test-server/cat_fu_mp4/img00044.jpeg View File


BIN
test-server/cat_fu_mp4/img00045.jpeg View File


BIN
test-server/cat_fu_mp4/img00046.jpeg View File


BIN
test-server/cat_fu_mp4/img00047.jpeg View File


BIN
test-server/cat_fu_mp4/img00048.jpeg View File


BIN
test-server/cat_fu_mp4/img00049.jpeg View File


BIN
test-server/cat_fu_mp4/img00050.jpeg View File


BIN
test-server/cat_fu_mp4/img00051.jpeg View File


BIN
test-server/cat_fu_mp4/img00052.jpeg View File


BIN
test-server/cat_fu_mp4/img00053.jpeg View File


BIN
test-server/cat_fu_mp4/img00054.jpeg View File


BIN
test-server/cat_fu_mp4/img00055.jpeg View File


BIN
test-server/cat_fu_mp4/img00056.jpeg View File


BIN
test-server/cat_fu_mp4/img00057.jpeg View File


BIN
test-server/cat_fu_mp4/img00058.jpeg View File


BIN
test-server/cat_fu_mp4/img00059.jpeg View File


BIN
test-server/cat_fu_mp4/img00060.jpeg View File


BIN
test-server/cat_fu_mp4/img00061.jpeg View File


BIN
test-server/cat_fu_mp4/img00062.jpeg View File


BIN
test-server/cat_fu_mp4/img00063.jpeg View File


BIN
test-server/cat_fu_mp4/img00064.jpeg View File


BIN
test-server/cat_fu_mp4/img00065.jpeg View File


BIN
test-server/cat_fu_mp4/img00066.jpeg View File


BIN
test-server/cat_fu_mp4/img00067.jpeg View File


BIN
test-server/cat_fu_mp4/img00068.jpeg View File


BIN
test-server/cat_fu_mp4/img00069.jpeg View File


BIN
test-server/cat_fu_mp4/img00070.jpeg View File


BIN
test-server/cat_fu_mp4/img00071.jpeg View File


BIN
test-server/cat_fu_mp4/img00072.jpeg View File


BIN
test-server/cat_fu_mp4/img00073.jpeg View File


+ 0
- 0
test-server/cat_fu_mp4/img00074.jpeg View File


Some files were not shown because too many files changed in this diff