Bladeren bron

Merge branch 'master' into 0.10.0

Ben Hsieh 8 jaren geleden
bovenliggende
commit
9658ea1a78
43 gewijzigde bestanden met toevoegingen van 1112 en 812 verwijderingen
  1. 1
    1
      .github/PULL_REQUEST_TEMPLATE
  2. 9
    1
      CONTRIBUTORS.md
  3. 69
    88
      README.md
  4. BIN
      img/ios-1.png
  5. BIN
      img/ios-2.png
  6. BIN
      img/ios-3.png
  7. BIN
      img/ios-4.png
  8. BIN
      img/ios-5.png
  9. 1
    4
      scripts/test.sh
  10. 1
    1
      src/android.js
  11. 12
    4
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java
  12. 43
    37
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java
  13. 1
    3
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java
  14. 0
    4
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConst.java
  15. 87
    102
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java
  16. 1
    11
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java
  17. 58
    48
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
  18. 1
    3
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java
  19. 12
    12
      src/class/RNFetchBlobReadStream.js
  20. 3
    2
      src/fs.js
  21. 6
    18
      src/index.js
  22. 27
    0
      src/ios/IOS7Polyfill.h
  23. 3
    1
      src/ios/RNFetchBlob.xcodeproj/project.pbxproj
  24. 2
    1
      src/ios/RNFetchBlob/RNFetchBlob.h
  25. 107
    46
      src/ios/RNFetchBlob/RNFetchBlob.m
  26. 4
    4
      src/ios/RNFetchBlobFS.h
  27. 303
    301
      src/ios/RNFetchBlobFS.m
  28. 15
    8
      src/ios/RNFetchBlobNetwork.m
  29. 28
    9
      src/ios/RNFetchBlobReqBuilder.m
  30. 3
    2
      src/package.json
  31. 22
    6
      src/polyfill/Blob.js
  32. 1
    1
      src/polyfill/File.js
  33. 7
    0
      src/polyfill/XMLHttpRequest.js
  34. 13
    0
      src/react-native-fetch-blob.podspec
  35. 57
    62
      src/scripts/prelink.js
  36. 2
    1
      src/utils/log.js
  37. BIN
      test-server/public/1600k-img-dummy.jpg
  38. 6
    1
      test-server/server.js
  39. 2
    0
      test/test-0.7.0.js
  40. 127
    0
      test/test-0.9.4.js
  41. 60
    11
      test/test-firebase.js
  42. 16
    17
      test/test-init.js
  43. 2
    2
      test/test-xmlhttp.js

+ 1
- 1
.github/PULL_REQUEST_TEMPLATE Bestand weergeven

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.2
4
+2. Bug fix request to "Bug Fix Branch" 0.9.4
5
 3. Correct README.md can directly to master
5
 3. Correct README.md can directly to master

+ 9
- 1
CONTRIBUTORS.md Bestand weergeven

1
+
1
 Dmitry Petukhov <dmitryvpetukhov@gmail.com>
2
 Dmitry Petukhov <dmitryvpetukhov@gmail.com>
3
+
2
 Erik Smartt <code@eriksmartt.com>
4
 Erik Smartt <code@eriksmartt.com>
5
+
3
 Evgeniy Baraniuk <ev.baraniuk@gmail.com>
6
 Evgeniy Baraniuk <ev.baraniuk@gmail.com>
7
+
4
 Juan B. Rodriguez <jbrodriguez@gmail.com>
8
 Juan B. Rodriguez <jbrodriguez@gmail.com>
9
+
5
 Kaishley <kklingachetti@msn.com>
10
 Kaishley <kklingachetti@msn.com>
11
+
6
 Nguyen Cao Nhat Linh <nhatlinh95@gmail.com>
12
 Nguyen Cao Nhat Linh <nhatlinh95@gmail.com>
7
-Tim Suchanek <tim.suchanek@gmail.com>
13
+
8
 follower <github@rancidbacon.com>
14
 follower <github@rancidbacon.com>
15
+
9
 francisco-sanchez-molina <psm1984@gmail.com>
16
 francisco-sanchez-molina <psm1984@gmail.com>
17
+
10
 smartt <github@eriksmartt.com>
18
 smartt <github@eriksmartt.com>

+ 69
- 88
README.md Bestand weergeven

1
-# react-native-fetch-blob 
1
+# react-native-fetch-blob
2
 [![release](https://img.shields.io/github/release/wkh237/react-native-fetch-blob.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/releases) [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![](https://img.shields.io/badge/Wiki-Public-brightgreen.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/wiki) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]()
2
 [![release](https://img.shields.io/github/release/wkh237/react-native-fetch-blob.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/releases) [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![](https://img.shields.io/badge/Wiki-Public-brightgreen.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/wiki) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]()
3
 
3
 
4
 
4
 
11
 - File stream support for dealing with large file
11
 - File stream support for dealing with large file
12
 - Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental)
12
 - Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental)
13
 
13
 
14
-> The npm package is inside `src` folder, this is development folder
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
 
15
 
16
 ## TOC
16
 ## TOC
17
 * [About](#user-content-about)
17
 * [About](#user-content-about)
25
  * [Cancel HTTP request](#user-content-cancel-request)
25
  * [Cancel HTTP request](#user-content-cancel-request)
26
  * [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)
27
  * [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)
28
  * [RNFetchBlob as Fetch](#user-content-rnfetchblob-as-fetch)
29
  * [RNFetchBlob as Fetch](#user-content-rnfetchblob-as-fetch)
29
 * [File System](#user-content-file-system)
30
 * [File System](#user-content-file-system)
30
  * [File access](#user-content-file-access)
31
  * [File access](#user-content-file-access)
51
 npm install --save react-native-fetch-blob
52
 npm install --save react-native-fetch-blob
52
 ```
53
 ```
53
 
54
 
54
-Link package using [rnpm](https://github.com/rnpm/rnpm)
55
+Or if using CocoaPods, add the pod to your `Podfile`, for example:
55
 
56
 
56
-```sh
57
-rnpm link
57
+```
58
+pod 'react-native-fetch-blob,
59
+    :path => '../node_modules/react-native-fetch-blob
58
 ```
60
 ```
59
 
61
 
60
-### Manually link the package (Android)
62
+**Automatically Link Native Modules**
61
 
63
 
62
-If rnpm link command failed to link the package automatically, you might try manually link the package.
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.
63
 
65
 
64
-Open `android/settings.gradle`, and add these lines which will app RNFetchBlob Android project dependency to your app.
66
+```
67
+react-native link
68
+```
65
 
69
 
66
-```diff
67
-include ':app'      
68
-+ include ':react-native-fetch-blob'                                                                                                  
69
-+ project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fetch-blob/android')                        
70
+As for projects < 0.29 you need `rnpm` to link native packages
71
+
72
+```sh
73
+rnpm link
70
 ```
74
 ```
71
 
75
 
72
-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
73
 
77
 
74
-```diff
75
-...
76
-+ import com.RNFetchBlob.RNFetchBlobPackage;                                                                                 
77
-...
78
-protected List<ReactPackage> getPackages() {
79
-      return Arrays.<ReactPackage>asList(
80
-          new MainReactPackage(),
81
-+          new RNFetchBlobPackage()                                                                                         
82
-      );
83
-    }
84
-  };
85
-...
78
+```sh
79
+RNFB_ANDROID_PERMISSIONS=true react-native link
86
 ```
80
 ```
87
-> 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.
88
 
89
 
89
 **Grant Permission to External storage for Android 5.0 or lower**
90
 **Grant Permission to External storage for Android 5.0 or lower**
90
 
91
 
139
 var RNFetchBlob = require('react-native-fetch-blob').default
140
 var RNFetchBlob = require('react-native-fetch-blob').default
140
 ```
141
 ```
141
 
142
 
142
-### HTTP Data Transfer
143
+## HTTP Data Transfer
143
 
144
 
144
----
145
 
145
 
146
-#### Regular Request
146
+### Regular Request
147
 
147
 
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
 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
149
 
149
 
151
 
151
 
152
 To sum up :
152
 To sum up :
153
 
153
 
154
-- 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.
155
-- 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.
156
- - The body is a BASE64 encoded string, the `Content-Type` header filed must containing substring`;BASE64` or `application/octet`  
157
- - 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)`
158
-- 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
+> 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`.
159
 
161
 
160
-#### Download example : Fetch files that needs authorization token
162
+### Download example : Fetch files that needs authorization token
161
 
163
 
162
 Most simple way is download to memory and stored as BASE64 encoded string, this is handy when the response data is small.
164
 Most simple way is download to memory and stored as BASE64 encoded string, this is handy when the response data is small.
163
 
165
 
183
   })
185
   })
184
 ```
186
 ```
185
 
187
 
186
-#### Download to storage directly
188
+### Download to storage directly
187
 
189
 
188
-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.
190
+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.
189
 
191
 
190
 **These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)**
192
 **These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)**
191
 
193
 
207
 
209
 
208
 **Set Temp File Extension**
210
 **Set Temp File Extension**
209
 
211
 
210
-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`.
212
+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`.
211
 
213
 
212
 ```js
214
 ```js
213
 RNFetchBlob
215
 RNFetchBlob
230
 
232
 
231
 **Use Specific File Path**
233
 **Use Specific File Path**
232
 
234
 
233
-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.
235
+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.
234
 
236
 
235
 ```js
237
 ```js
236
 let dirs = RNFetchBlob.fs.dirs
238
 let dirs = RNFetchBlob.fs.dirs
252
 
254
 
253
 ####  Upload example : Dropbox [files-upload](https://www.dropbox.com/developers/documentation/http/documentation#files-upload) API
255
 ####  Upload example : Dropbox [files-upload](https://www.dropbox.com/developers/documentation/http/documentation#files-upload) API
254
 
256
 
255
-`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.
257
+`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.
256
 
258
 
257
 ```js
259
 ```js
258
 
260
 
277
   })
279
   })
278
 ```
280
 ```
279
 
281
 
280
-#### Upload a file from storage
282
+### Upload a file from storage
281
 
283
 
282
-If you're going to use a `file` request body, just wrap the path with `wrap` API.
284
+If you're going to use a `file` as request body, just wrap the path with `wrap` API.
283
 
285
 
284
 ```js
286
 ```js
285
 RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
287
 RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
303
   })
305
   })
304
 ```
306
 ```
305
 
307
 
306
-#### Multipart/form-data example : Post form data with file and data
308
+### Multipart/form-data example : Post form data with file and data
307
 
309
 
308
 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).
310
 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).
309
 
311
 
335
   })
337
   })
336
 ```
338
 ```
337
 
339
 
338
-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.
340
+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)).
339
 
341
 
340
 ```js
342
 ```js
341
 
343
 
374
   })
376
   })
375
 ```
377
 ```
376
 
378
 
377
-#### Upload/Download progress
379
+### Upload/Download progress
378
 
380
 
379
-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.
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.
380
 
382
 
381
 ```js
383
 ```js
382
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
384
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
399
     })
401
     })
400
 ```
402
 ```
401
 
403
 
402
-#### Cancel Request
404
+### Cancel Request
403
 
405
 
404
 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.
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.
405
 
407
 
538
       android.actionViewIntent(PATH_OF_IMG, 'image/png')
540
       android.actionViewIntent(PATH_OF_IMG, 'image/png')
539
 ```
541
 ```
540
 
542
 
541
-### File System
543
+## File System
542
 
544
 
543
-#### File Access
545
+### File Access
544
 
546
 
545
 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.
547
 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.
546
 
548
 
568
 
570
 
569
 See [File API](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API) for more information
571
 See [File API](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API) for more information
570
 
572
 
571
-#### File Stream
573
+### File Stream
572
 
574
 
573
 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**.
575
 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**.
574
 
576
 
575
-When calling `readStream` method, you have to `open` the stream, and start to read data.
577
+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))
576
 
578
 
577
 ```js
579
 ```js
578
 let data = ''
580
 let data = ''
617
 
619
 
618
 ```
620
 ```
619
 
621
 
620
-#### Cache File Management
622
+### Cache File Management
621
 
623
 
622
 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
624
 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
623
 
625
 
674
 
676
 
675
 ```
677
 ```
676
 
678
 
677
-#### Self-Signed SSL Server
679
+### Transfer Encoding
680
+
681
+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`.
682
+
683
+```js
684
+RNFetchBlob.fetch('POST', 'http://example.com/upload', { 'Transfer-Encoding' : 'Chunked' }, bodyData)
685
+```
686
+
687
+### Self-Signed SSL Server
678
 
688
 
679
 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`
689
 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`
680
 
690
 
688
 })
698
 })
689
 ```
699
 ```
690
 
700
 
691
-### Web API Polyfills
701
+## Web API Polyfills
692
 
702
 
693
 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.
703
 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.
694
 
704
 
697
 
707
 
698
 Here's a [sample app](https://github.com/wkh237/rn-firebase-storage-upload-sample) that uses polyfills to upload files to FireBase.
708
 Here's a [sample app](https://github.com/wkh237/rn-firebase-storage-upload-sample) that uses polyfills to upload files to FireBase.
699
 
709
 
700
-### Performance Tips
701
-
702
----
710
+## Performance Tips
703
 
711
 
704
 **Reduce RCT Bridge and BASE64 Overheard**
712
 **Reduce RCT Bridge and BASE64 Overheard**
705
 
713
 
706
-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.
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. 
715
+
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.
707
 
717
 
708
 <img src="img/performance_1.png" style="width : 100%"/>
718
 <img src="img/performance_1.png" style="width : 100%"/>
709
 
719
 
721
 
731
 
722
 ## Changes
732
 ## Changes
723
 
733
 
724
-| Version | |
725
-|---|---|
726
-| 0.9.2 | Add Blob.slice() method implementation #89 |
727
-| 0.9.1 | Fix Android Blob constructor asynchronous issue caused by 0.9.0 fs change |
728
-| 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 |
729
-| 0.8.1 | Remove Web API log and fix ios progress report function. |
730
-| 0.8.0 | Added Web API polyfills, support regular request, added timeout option. |
731
-| 0.7.5 | Fix installation script that make it compatible to react-native < 0.28 |
732
-| 0.7.4 | Fix app crash problem in version > 0.27 |
733
-| 0.7.3 | Fix OkHttp dependency issue in version < 0.29 |
734
-| 0.7.2 | Fix cancel request bug |
735
-| 0.7.1 | Fix #57 ios module could not compile on ios version <= 9.3 |
736
-| 0.7.0 | Add support of Android upload progress, and remove AsyncHttpClient dependency from Android native implementation. |
737
-| 0.6.4 | Fix rnpm link script. |
738
-| 0.6.3 | Fix performance issue on IOS, increase max concurrent request limitation from 1. |
739
-| 0.6.2 | Add support of asset file and camera roll files, Support custom MIME type when sending multipart request, thanks @smartt |
740
-| 0.6.1 | Fix #37 progress report API issue on IOS |
741
-| 0.6.0 | Add readFile and writeFile API for easier file access, also added Android download manager support. |
742
-| 0.5.8 | Fix #33 PUT request will always be sent as POST on Android |
743
-| 0.5.7 | Fix #31 #30 Xcode pre 7.3 build error |
744
-| 0.5.6 | Add support for IOS network status indicator. Fix file stream ASCII reader bug. |
745
-| 0.5.5 | Remove work in progress code added in 0.5.2 which may cause memory leaks. |
746
-| 0.5.4 | Fix #30 #31 build build error, and improve memory efficiency. |
747
-| 0.5.3 | Add API for access untrusted SSL server |
748
-| 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 |
749
-| 0.5.0 | Upload/download with direct access to file storage, and also added file access APIs |
750
-| 0.4.2 | Supports upload/download progress |
751
-| 0.4.1 | Fix upload form-data missing file extension problem on Android |
752
-| 0.4.0 | Add base-64 encode/decode library and API |
753
-| ~0.3.0 | Upload/Download octet-stream and form-data |
734
+See [release notes](https://github.com/wkh237/react-native-fetch-blob/releases)
754
 
735
 
755
 ### Development
736
 ### Development
756
 
737
 

BIN
img/ios-1.png Bestand weergeven


BIN
img/ios-2.png Bestand weergeven


BIN
img/ios-3.png Bestand weergeven


BIN
img/ios-4.png Bestand weergeven


BIN
img/ios-5.png Bestand weergeven


+ 1
- 4
scripts/test.sh Bestand weergeven

41
 npm install --save react-native-fetch-blob
41
 npm install --save react-native-fetch-blob
42
 # libs that requires web API polyfills
42
 # libs that requires web API polyfills
43
 npm install --save firebase
43
 npm install --save firebase
44
-# libs that requires Node polyfills
45
-npm install --save oboe
46
-
47
-rnpm link
44
+react-native link
48
 
45
 
49
 # copy android assets
46
 # copy android assets
50
 cd ${CWD}
47
 cd ${CWD}

+ 1
- 1
src/android.js Bestand weergeven

18
  * @param  {string} mime MIME type string
18
  * @param  {string} mime MIME type string
19
  * @return {Promise}
19
  * @return {Promise}
20
  */
20
  */
21
-function actionViewIntent(path:string, mime = 'text/plain':string) {
21
+function actionViewIntent(path:string, mime:string = 'text/plain') {
22
   if(Platform.OS === 'android')
22
   if(Platform.OS === 'android')
23
     return RNFetchBlob.actionViewIntent(path, mime)
23
     return RNFetchBlob.actionViewIntent(path, mime)
24
   else
24
   else

+ 12
- 4
src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java Bestand weergeven

21
     static ReactApplicationContext RCTContext;
21
     static ReactApplicationContext RCTContext;
22
     static LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
22
     static LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
23
     static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
23
     static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
24
+    static LinkedBlockingQueue<Runnable> fsTaskQueue = new LinkedBlockingQueue<>();
25
+    static ThreadPoolExecutor fsThreadPool = new ThreadPoolExecutor(2, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
24
 
26
 
25
     public RNFetchBlob(ReactApplicationContext reactContext) {
27
     public RNFetchBlob(ReactApplicationContext reactContext) {
26
 
28
 
205
     /**
207
     /**
206
      * @param path Stream file path
208
      * @param path Stream file path
207
      * @param encoding Stream encoding, should be one of `base64`, `ascii`, and `utf8`
209
      * @param encoding Stream encoding, should be one of `base64`, `ascii`, and `utf8`
208
-     * @param bufferSize Stream buffer size, default to 1024 or 1026(base64).
210
+     * @param bufferSize Stream buffer size, default to 4096 or 4095(base64).
209
      */
211
      */
210
-    public void readStream(String path, String encoding, int bufferSize) {
211
-        RNFetchBlobFS fs = new RNFetchBlobFS(this.getReactApplicationContext());
212
-        fs.readStream(path, encoding, bufferSize);
212
+    public void readStream(final String path, final String encoding, final int bufferSize, final int tick, final String streamId) {
213
+        final ReactApplicationContext ctx = this.getReactApplicationContext();
214
+        fsThreadPool.execute(new Runnable() {
215
+            @Override
216
+            public void run() {
217
+                RNFetchBlobFS fs = new RNFetchBlobFS(ctx);
218
+                fs.readStream(path, encoding, bufferSize, tick, streamId);
219
+            }
220
+        });
213
     }
221
     }
214
 
222
 
215
     @ReactMethod
223
     @ReactMethod

+ 43
- 37
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java Bestand weergeven

21
 import okhttp3.RequestBody;
21
 import okhttp3.RequestBody;
22
 import okio.BufferedSink;
22
 import okio.BufferedSink;
23
 
23
 
24
-/**
25
- * Created by wkh237 on 2016/7/11.
26
- */
27
 public class RNFetchBlobBody extends RequestBody{
24
 public class RNFetchBlobBody extends RequestBody{
28
 
25
 
29
     InputStream requestStream;
26
     InputStream requestStream;
34
     RNFetchBlobReq.RequestType requestType;
31
     RNFetchBlobReq.RequestType requestType;
35
     MediaType mime;
32
     MediaType mime;
36
     File bodyCache;
33
     File bodyCache;
34
+    Boolean chunkedEncoding = false;
37
 
35
 
38
 
36
 
39
-    /**
40
-     * Single file or raw content request constructor
41
-     * @param taskId
42
-     * @param type
43
-     * @param form
44
-     * @param contentType
45
-     */
46
-    public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, ReadableArray form, MediaType contentType) {
37
+    public RNFetchBlobBody(String taskId) {
47
         this.mTaskId = taskId;
38
         this.mTaskId = taskId;
48
-        this.form = form;
49
-        requestType = type;
50
-        mime = contentType;
51
-        try {
52
-            bodyCache = createMultipartBodyCache();
53
-            requestStream = new FileInputStream(bodyCache);
54
-            contentLength = bodyCache.length();
55
-        } catch(Exception ex) {
56
-            ex.printStackTrace();
57
-            RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create request multipart body :" + ex.getLocalizedMessage());
58
-        }
39
+    }
40
+
41
+    RNFetchBlobBody chunkedEncoding(boolean val) {
42
+        this.chunkedEncoding = val;
43
+        return this;
44
+    }
45
+
46
+    RNFetchBlobBody setMIME(MediaType mime) {
47
+        this.mime = mime;
48
+        return this;
49
+    }
50
+
51
+    RNFetchBlobBody setRequestType( RNFetchBlobReq.RequestType type) {
52
+        this.requestType = type;
53
+        return this;
59
     }
54
     }
60
 
55
 
61
     /**
56
     /**
62
-     * Multipart request constructor
63
-     * @param taskId
64
-     * @param type
65
-     * @param rawBody
66
-     * @param contentType
57
+     * Set request body
58
+     * @param body A string represents the request body
59
+     * @return object itself
67
      */
60
      */
68
-    public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, String rawBody, MediaType contentType) {
69
-        this.mTaskId = taskId;
70
-        requestType = type;
71
-        this.rawBody = rawBody;
72
-        mime = contentType;
61
+    RNFetchBlobBody setBody(String body) {
62
+        this.rawBody = body;
73
         if(rawBody == null) {
63
         if(rawBody == null) {
74
             this.rawBody = "";
64
             this.rawBody = "";
75
             requestType = RNFetchBlobReq.RequestType.AsIs;
65
             requestType = RNFetchBlobReq.RequestType.AsIs;
82
                     break;
72
                     break;
83
                 case AsIs:
73
                 case AsIs:
84
                     contentLength = this.rawBody.getBytes().length;
74
                     contentLength = this.rawBody.getBytes().length;
75
+                    requestStream = new ByteArrayInputStream(this.rawBody.getBytes());
85
                     break;
76
                     break;
86
                 case Others:
77
                 case Others:
87
                     break;
78
                     break;
90
             ex.printStackTrace();
81
             ex.printStackTrace();
91
             RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create single content request body :" + ex.getLocalizedMessage() + "\r\n");
82
             RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create single content request body :" + ex.getLocalizedMessage() + "\r\n");
92
         }
83
         }
84
+        return this;
85
+    }
93
 
86
 
87
+    /**
88
+     * Set request body (Array)
89
+     * @param body A Readable array contains form data
90
+     * @return object itself
91
+     */
92
+    RNFetchBlobBody setBody(ReadableArray body) {
93
+        this.form = body;
94
+        try {
95
+            bodyCache = createMultipartBodyCache();
96
+            requestStream = new FileInputStream(bodyCache);
97
+            contentLength = bodyCache.length();
98
+        } catch(Exception ex) {
99
+            ex.printStackTrace();
100
+            RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create request multipart body :" + ex.getLocalizedMessage());
101
+        }
102
+        return this;
94
     }
103
     }
95
 
104
 
96
     @Override
105
     @Override
97
     public long contentLength() {
106
     public long contentLength() {
98
-        return contentLength;
107
+        return chunkedEncoding ? -1 : contentLength;
99
     }
108
     }
100
 
109
 
101
     @Override
110
     @Override
106
     @Override
115
     @Override
107
     public void writeTo(BufferedSink sink) {
116
     public void writeTo(BufferedSink sink) {
108
         try {
117
         try {
109
-            if (requestType == RNFetchBlobReq.RequestType.AsIs)
110
-                sink.write(rawBody.getBytes());
111
-            else
112
-                pipeStreamToSink(requestStream, sink);
118
+            pipeStreamToSink(requestStream, sink);
113
         } catch(Exception ex) {
119
         } catch(Exception ex) {
114
             RNFetchBlobUtils.emitWarningEvent(ex.getLocalizedMessage());
120
             RNFetchBlobUtils.emitWarningEvent(ex.getLocalizedMessage());
115
             ex.printStackTrace();
121
             ex.printStackTrace();

+ 1
- 3
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java Bestand weergeven

5
 
5
 
6
 import java.util.HashMap;
6
 import java.util.HashMap;
7
 
7
 
8
-/**
9
- * Created by wkh237 on 2016/5/29.
10
- */
8
+
11
 public class RNFetchBlobConfig {
9
 public class RNFetchBlobConfig {
12
 
10
 
13
     public Boolean fileCache;
11
     public Boolean fileCache;

+ 0
- 4
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConst.java Bestand weergeven

1
 package com.RNFetchBlob;
1
 package com.RNFetchBlob;
2
 
2
 
3
-import okhttp3.MediaType;
4
 
3
 
5
-/**
6
- * Created by wkh237 on 2016/7/11.
7
- */
8
 public class RNFetchBlobConst {
4
 public class RNFetchBlobConst {
9
     public static final String EVENT_UPLOAD_PROGRESS = "RNFetchBlobProgress-upload";
5
     public static final String EVENT_UPLOAD_PROGRESS = "RNFetchBlobProgress-upload";
10
     public static final String EVENT_PROGRESS = "RNFetchBlobProgress";
6
     public static final String EVENT_PROGRESS = "RNFetchBlobProgress";

+ 87
- 102
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java Bestand weergeven

1
 package com.RNFetchBlob;
1
 package com.RNFetchBlob;
2
 
2
 
3
-import android.app.LoaderManager;
4
-import android.content.ContentResolver;
5
-import android.content.CursorLoader;
6
 import android.content.res.AssetFileDescriptor;
3
 import android.content.res.AssetFileDescriptor;
7
-import android.database.Cursor;
8
 import android.media.MediaScannerConnection;
4
 import android.media.MediaScannerConnection;
9
 import android.net.Uri;
5
 import android.net.Uri;
10
 import android.os.AsyncTask;
6
 import android.os.AsyncTask;
11
 import android.os.Environment;
7
 import android.os.Environment;
12
-import android.os.Looper;
13
-import android.provider.MediaStore;
8
+import android.os.SystemClock;
14
 import android.util.Base64;
9
 import android.util.Base64;
15
 
10
 
16
 import com.RNFetchBlob.Utils.PathResolver;
11
 import com.RNFetchBlob.Utils.PathResolver;
21
 import com.facebook.react.bridge.ReadableArray;
16
 import com.facebook.react.bridge.ReadableArray;
22
 import com.facebook.react.bridge.WritableArray;
17
 import com.facebook.react.bridge.WritableArray;
23
 import com.facebook.react.bridge.WritableMap;
18
 import com.facebook.react.bridge.WritableMap;
24
-import com.facebook.react.bridge.WritableNativeArray;
25
 import com.facebook.react.modules.core.DeviceEventManagerModule;
19
 import com.facebook.react.modules.core.DeviceEventManagerModule;
26
 
20
 
27
 import java.io.File;
21
 import java.io.File;
28
 import java.io.FileInputStream;
22
 import java.io.FileInputStream;
29
-import java.io.FileNotFoundException;
30
 import java.io.FileOutputStream;
23
 import java.io.FileOutputStream;
31
 import java.io.IOException;
24
 import java.io.IOException;
32
 import java.io.InputStream;
25
 import java.io.InputStream;
33
 import java.io.OutputStream;
26
 import java.io.OutputStream;
34
-import java.io.UnsupportedEncodingException;
35
-import java.net.URLDecoder;
36
 import java.nio.charset.Charset;
27
 import java.nio.charset.Charset;
37
 import java.util.HashMap;
28
 import java.util.HashMap;
38
 import java.util.Map;
29
 import java.util.Map;
39
 import java.util.UUID;
30
 import java.util.UUID;
40
-import java.util.concurrent.BlockingDeque;
41
-import java.util.concurrent.BlockingQueue;
42
-import java.util.concurrent.LinkedBlockingQueue;
43
-import java.util.concurrent.ThreadPoolExecutor;
44
-import java.util.concurrent.TimeUnit;
45
-
46
-/**
47
- * Created by wkh237 on 2016/5/26.
48
- */
31
+
49
 public class RNFetchBlobFS {
32
 public class RNFetchBlobFS {
50
 
33
 
51
     ReactApplicationContext mCtx;
34
     ReactApplicationContext mCtx;
74
      * @param path Destination file path.
57
      * @param path Destination file path.
75
      * @param encoding Encoding of the string.
58
      * @param encoding Encoding of the string.
76
      * @param data Array passed from JS context.
59
      * @param data Array passed from JS context.
77
-     * @param promise
60
+     * @param promise RCT Promise
78
      */
61
      */
79
     static public void writeFile(String path, String encoding, String data, final boolean append, final Promise promise) {
62
     static public void writeFile(String path, String encoding, String data, final boolean append, final Promise promise) {
80
         try {
63
         try {
86
             FileOutputStream fout = new FileOutputStream(f, append);
69
             FileOutputStream fout = new FileOutputStream(f, append);
87
             // write data from a file
70
             // write data from a file
88
             if(encoding.equalsIgnoreCase(RNFetchBlobConst.DATA_ENCODE_URI)) {
71
             if(encoding.equalsIgnoreCase(RNFetchBlobConst.DATA_ENCODE_URI)) {
72
+                data = normalizePath(data);
89
                 File src = new File(data);
73
                 File src = new File(data);
90
                 if(!src.exists()) {
74
                 if(!src.exists()) {
91
                     promise.reject("RNfetchBlob writeFileError", "source file : " + data + "not exists");
75
                     promise.reject("RNfetchBlob writeFileError", "source file : " + data + "not exists");
118
      * Write array of bytes into file
102
      * Write array of bytes into file
119
      * @param path Destination file path.
103
      * @param path Destination file path.
120
      * @param data Array passed from JS context.
104
      * @param data Array passed from JS context.
121
-     * @param promise
105
+     * @param promise RCT Promise
122
      */
106
      */
123
     static public void writeFile(String path, ReadableArray data, final boolean append, final Promise promise) {
107
     static public void writeFile(String path, ReadableArray data, final boolean append, final Promise promise) {
124
 
108
 
228
      * @param encoding  File stream decoder, should be one of `base64`, `utf8`, `ascii`
212
      * @param encoding  File stream decoder, should be one of `base64`, `utf8`, `ascii`
229
      * @param bufferSize    Buffer size of read stream, default to 4096 (4095 when encode is `base64`)
213
      * @param bufferSize    Buffer size of read stream, default to 4096 (4095 when encode is `base64`)
230
      */
214
      */
231
-    public void readStream( String path, String encoding, int bufferSize) {
215
+    public void readStream(String path, String encoding, int bufferSize, int tick, final String streamId) {
232
         path = normalizePath(path);
216
         path = normalizePath(path);
233
-        AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
234
-            @Override
235
-            protected Integer doInBackground(String ... args) {
236
-                String path = args[0];
237
-                String encoding = args[1];
238
-                int bufferSize = Integer.parseInt(args[2]);
239
-                String eventName = "RNFetchBlobStream+" + path;
240
-                try {
217
+        try {
241
 
218
 
242
-                    int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096;
243
-                    if(bufferSize > 0)
244
-                        chunkSize = bufferSize;
219
+            int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096;
220
+            if(bufferSize > 0)
221
+                chunkSize = bufferSize;
245
 
222
 
246
-                    InputStream fs;
247
-                    if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {
248
-                        fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, ""));
249
-                    }
250
-                    else {
251
-                        fs = new FileInputStream(new File(path));
252
-                    }
223
+            InputStream fs;
224
+            if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {
225
+                fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, ""));
226
+            }
227
+            else {
228
+                fs = new FileInputStream(new File(path));
229
+            }
253
 
230
 
254
-                    byte[] buffer = new byte[chunkSize];
255
-                    int cursor = 0;
256
-                    boolean error = false;
231
+            byte[] buffer = new byte[chunkSize];
232
+            int cursor = 0;
233
+            boolean error = false;
257
 
234
 
258
-                    if (encoding.equalsIgnoreCase("utf8")) {
259
-                        while ((cursor = fs.read(buffer)) != -1) {
260
-                            String chunk = new String(buffer, 0, cursor, "UTF-8");
261
-                            emitStreamEvent(eventName, "data", chunk);
262
-                        }
263
-                    } else if (encoding.equalsIgnoreCase("ascii")) {
264
-                        while ((cursor = fs.read(buffer)) != -1) {
265
-                            WritableArray chunk = Arguments.createArray();
266
-                            for(int i =0;i<cursor;i++)
267
-                            {
268
-                                chunk.pushInt((int)buffer[i]);
269
-                            }
270
-                            emitStreamEvent(eventName, "data", chunk);
271
-                        }
272
-                    } else if (encoding.equalsIgnoreCase("base64")) {
273
-                        while ((cursor = fs.read(buffer)) != -1) {
274
-                            if(cursor < chunkSize) {
275
-                                byte [] copy = new byte[cursor];
276
-                                for(int i =0;i<cursor;i++) {
277
-                                    copy[i] = buffer[i];
278
-                                }
279
-                                emitStreamEvent(eventName, "data", Base64.encodeToString(copy, Base64.NO_WRAP));
280
-                            }
281
-                            else
282
-                                emitStreamEvent(eventName, "data", Base64.encodeToString(buffer, Base64.NO_WRAP));
235
+            if (encoding.equalsIgnoreCase("utf8")) {
236
+                while ((cursor = fs.read(buffer)) != -1) {
237
+                    String chunk = new String(buffer, 0, cursor, "UTF-8");
238
+                    emitStreamEvent(streamId, "data", chunk);
239
+                    if(tick > 0)
240
+                        SystemClock.sleep(tick);
241
+                }
242
+            } else if (encoding.equalsIgnoreCase("ascii")) {
243
+                while ((cursor = fs.read(buffer)) != -1) {
244
+                    WritableArray chunk = Arguments.createArray();
245
+                    for(int i =0;i<cursor;i++)
246
+                    {
247
+                        chunk.pushInt((int)buffer[i]);
248
+                    }
249
+                    emitStreamEvent(streamId, "data", chunk);
250
+                    if(tick > 0)
251
+                        SystemClock.sleep(tick);
252
+                }
253
+            } else if (encoding.equalsIgnoreCase("base64")) {
254
+                while ((cursor = fs.read(buffer)) != -1) {
255
+                    if(cursor < chunkSize) {
256
+                        byte [] copy = new byte[cursor];
257
+                        for(int i =0;i<cursor;i++) {
258
+                            copy[i] = buffer[i];
283
                         }
259
                         }
284
-                    } else {
285
-                        String msg = "unrecognized encoding `" + encoding + "`";
286
-                        emitStreamEvent(eventName, "error", msg);
287
-                        error = true;
260
+                        emitStreamEvent(streamId, "data", Base64.encodeToString(copy, Base64.NO_WRAP));
288
                     }
261
                     }
289
-
290
-                    if(!error)
291
-                        emitStreamEvent(eventName, "end", "");
292
-                    fs.close();
293
-                    buffer = null;
294
-
295
-                } catch (Exception err) {
296
-                    emitStreamEvent(eventName, "error", err.getLocalizedMessage());
262
+                    else
263
+                        emitStreamEvent(streamId, "data", Base64.encodeToString(buffer, Base64.NO_WRAP));
264
+                    if(tick > 0)
265
+                        SystemClock.sleep(tick);
297
                 }
266
                 }
298
-                return null;
267
+            } else {
268
+                String msg = "unrecognized encoding `" + encoding + "`";
269
+                emitStreamEvent(streamId, "error", msg);
270
+                error = true;
299
             }
271
             }
300
-        };
301
-        task.execute(path, encoding, String.valueOf(bufferSize));
272
+
273
+            if(!error)
274
+                emitStreamEvent(streamId, "end", "");
275
+            fs.close();
276
+            buffer = null;
277
+
278
+        } catch (Exception err) {
279
+            emitStreamEvent(streamId, "error", err.getLocalizedMessage());
280
+        }
302
     }
281
     }
303
 
282
 
304
     /**
283
     /**
433
      * @param callback  JS context callback
412
      * @param callback  JS context callback
434
      */
413
      */
435
     static void cp(String path, String dest, Callback callback) {
414
     static void cp(String path, String dest, Callback callback) {
415
+
436
         path = normalizePath(path);
416
         path = normalizePath(path);
437
         InputStream in = null;
417
         InputStream in = null;
438
         OutputStream out = null;
418
         OutputStream out = null;
443
                 callback.invoke("cp error: source file at path`" + path + "` not exists");
423
                 callback.invoke("cp error: source file at path`" + path + "` not exists");
444
                 return;
424
                 return;
445
             }
425
             }
446
-
447
             if(!new File(dest).exists())
426
             if(!new File(dest).exists())
448
                 new File(dest).createNewFile();
427
                 new File(dest).createNewFile();
449
 
428
 
457
             }
436
             }
458
 
437
 
459
         } catch (Exception err) {
438
         } catch (Exception err) {
460
-            if(err != null)
461
-                callback.invoke(err.getLocalizedMessage());
439
+            callback.invoke(err.getLocalizedMessage());
462
         } finally {
440
         } finally {
463
             try {
441
             try {
464
-                in.close();
465
-                out.close();
442
+                if (in != null) {
443
+                    in.close();
444
+                }
445
+                if (out != null) {
446
+                    out.close();
447
+                }
466
                 callback.invoke();
448
                 callback.invoke();
467
-            } catch (IOException e) {
449
+            } catch (Exception e) {
468
                 callback.invoke(e.getLocalizedMessage());
450
                 callback.invoke(e.getLocalizedMessage());
469
             }
451
             }
470
         }
452
         }
492
      * @param callback  JS context callback
474
      * @param callback  JS context callback
493
      */
475
      */
494
     static void exists(String path, Callback callback) {
476
     static void exists(String path, Callback callback) {
495
-        path = normalizePath(path);
477
+
496
         if(isAsset(path)) {
478
         if(isAsset(path)) {
497
             try {
479
             try {
498
                 String filename = path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
480
                 String filename = path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
503
             }
485
             }
504
         }
486
         }
505
         else {
487
         else {
488
+            path = normalizePath(path);
506
             boolean exist = new File(path).exists();
489
             boolean exist = new File(path).exists();
507
             boolean isDir = new File(path).isDirectory();
490
             boolean isDir = new File(path).isDirectory();
508
             callback.invoke(exist, isDir);
491
             callback.invoke(exist, isDir);
509
         }
492
         }
510
-
511
     }
493
     }
512
 
494
 
513
     /**
495
     /**
536
      * @param dest  Destination of created file
518
      * @param dest  Destination of created file
537
      * @param start Start byte offset in source file
519
      * @param start Start byte offset in source file
538
      * @param end   End byte offset
520
      * @param end   End byte offset
539
-     * @param encode
521
+     * @param encode NOT IMPLEMENTED
540
      */
522
      */
541
     public static void slice(String src, String dest, int start, int end, String encode, Promise promise) {
523
     public static void slice(String src, String dest, int start, int end, String encode, Promise promise) {
542
         try {
524
         try {
525
+            src = normalizePath(src);
543
             File source = new File(src);
526
             File source = new File(src);
544
             if(!source.exists()) {
527
             if(!source.exists()) {
545
                 promise.reject("RNFetchBlob.slice error", "source file : " + src + " not exists");
528
                 promise.reject("RNFetchBlob.slice error", "source file : " + src + " not exists");
529
+                return;
546
             }
530
             }
547
             long size = source.length();
531
             long size = source.length();
548
             long max = Math.min(size, end);
532
             long max = Math.min(size, end);
605
      */
589
      */
606
     static void stat(String path, Callback callback) {
590
     static void stat(String path, Callback callback) {
607
         try {
591
         try {
592
+            path = normalizePath(path);
608
             WritableMap result = statFile(path);
593
             WritableMap result = statFile(path);
609
             if(result == null)
594
             if(result == null)
610
                 callback.invoke("stat error: failed to list path `" + path + "` for it is not exist or it is not a folder", null);
595
                 callback.invoke("stat error: failed to list path `" + path + "` for it is not exist or it is not a folder", null);
673
 
658
 
674
     /**
659
     /**
675
      * Create new file at path
660
      * Create new file at path
676
-     * @param path
677
-     * @param data
678
-     * @param encoding
679
-     * @param callback
661
+     * @param path The destination path of the new file.
662
+     * @param data Initial data of the new file.
663
+     * @param encoding Encoding of initial data.
664
+     * @param callback RCT bridge callback.
680
      */
665
      */
681
     static void createFile(String path, String data, String encoding, Callback callback) {
666
     static void createFile(String path, String data, String encoding, Callback callback) {
682
         try {
667
         try {
782
             return data.getBytes(Charset.forName("US-ASCII"));
767
             return data.getBytes(Charset.forName("US-ASCII"));
783
         }
768
         }
784
         else if(encoding.toLowerCase().contains("base64")) {
769
         else if(encoding.toLowerCase().contains("base64")) {
785
-            byte [] b = Base64.decode(data, Base64.NO_WRAP);
786
-            return b;
770
+            return Base64.decode(data, Base64.NO_WRAP);
771
+
787
         }
772
         }
788
         else if(encoding.equalsIgnoreCase("utf8")) {
773
         else if(encoding.equalsIgnoreCase("utf8")) {
789
             return data.getBytes(Charset.forName("UTF-8"));
774
             return data.getBytes(Charset.forName("UTF-8"));
822
     /**
807
     /**
823
      * Get input stream of the given path, when the path is a string starts with bundle-assets://
808
      * Get input stream of the given path, when the path is a string starts with bundle-assets://
824
      * the stream is created by Assets Manager, otherwise use FileInputStream.
809
      * the stream is created by Assets Manager, otherwise use FileInputStream.
825
-     * @param path
826
-     * @return
810
+     * @param path The file to open stream
811
+     * @return InputStream instance
827
      * @throws IOException
812
      * @throws IOException
828
      */
813
      */
829
     static InputStream inputStreamFromPath(String path) throws IOException {
814
     static InputStream inputStreamFromPath(String path) throws IOException {
835
 
820
 
836
     /**
821
     /**
837
      * Check if the asset or the file exists
822
      * Check if the asset or the file exists
838
-     * @param path
839
-     * @return
823
+     * @param path A file path URI string
824
+     * @return A boolean value represents if the path exists.
840
      */
825
      */
841
     static boolean isPathExists(String path) {
826
     static boolean isPathExists(String path) {
842
         if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {
827
         if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {

+ 1
- 11
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java Bestand weergeven

1
 package com.RNFetchBlob;
1
 package com.RNFetchBlob;
2
 
2
 
3
 import com.facebook.react.ReactPackage;
3
 import com.facebook.react.ReactPackage;
4
-import com.facebook.react.bridge.Callback;
5
 import com.facebook.react.bridge.JavaScriptModule;
4
 import com.facebook.react.bridge.JavaScriptModule;
6
 import com.facebook.react.bridge.NativeModule;
5
 import com.facebook.react.bridge.NativeModule;
7
 import com.facebook.react.bridge.ReactApplicationContext;
6
 import com.facebook.react.bridge.ReactApplicationContext;
8
-import com.facebook.react.bridge.ReactContext;
9
-import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
-import com.facebook.react.bridge.ReactMethod;
11
-import com.facebook.react.bridge.ReadableArray;
12
-import com.facebook.react.bridge.ReadableMap;
13
-import com.facebook.react.bridge.ReadableMapKeySetIterator;
14
-import com.facebook.react.bridge.ReadableType;
15
 import com.facebook.react.uimanager.ViewManager;
7
 import com.facebook.react.uimanager.ViewManager;
16
 
8
 
17
 import java.util.ArrayList;
9
 import java.util.ArrayList;
18
 import java.util.Collections;
10
 import java.util.Collections;
19
 import java.util.List;
11
 import java.util.List;
20
 
12
 
21
-/**
22
- * Created by wkh237 on 2016/4/29.
23
- */
13
+
24
 public class RNFetchBlobPackage implements ReactPackage {
14
 public class RNFetchBlobPackage implements ReactPackage {
25
 
15
 
26
     @Override
16
     @Override

+ 58
- 48
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java Bestand weergeven

17
 import com.facebook.react.bridge.ReadableArray;
17
 import com.facebook.react.bridge.ReadableArray;
18
 import com.facebook.react.bridge.ReadableMap;
18
 import com.facebook.react.bridge.ReadableMap;
19
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
19
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
20
+import com.facebook.react.bridge.WritableArray;
20
 import com.facebook.react.bridge.WritableMap;
21
 import com.facebook.react.bridge.WritableMap;
21
 import com.facebook.react.modules.core.DeviceEventManagerModule;
22
 import com.facebook.react.modules.core.DeviceEventManagerModule;
22
 
23
 
32
 import java.nio.charset.CharacterCodingException;
33
 import java.nio.charset.CharacterCodingException;
33
 import java.nio.charset.Charset;
34
 import java.nio.charset.Charset;
34
 import java.nio.charset.CharsetEncoder;
35
 import java.nio.charset.CharsetEncoder;
36
+import java.util.ArrayList;
35
 import java.util.HashMap;
37
 import java.util.HashMap;
36
 import java.util.concurrent.TimeUnit;
38
 import java.util.concurrent.TimeUnit;
37
 
39
 
46
 import okhttp3.Response;
48
 import okhttp3.Response;
47
 import okhttp3.ResponseBody;
49
 import okhttp3.ResponseBody;
48
 
50
 
49
-
50
-/**
51
- * Created by wkh237 on 2016/6/21.
52
- */
53
 public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
51
 public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
54
 
52
 
55
     enum RequestType  {
53
     enum RequestType  {
58
         AsIs,
56
         AsIs,
59
         WithoutBody,
57
         WithoutBody,
60
         Others
58
         Others
61
-    };
59
+    }
62
 
60
 
63
     enum ResponseType {
61
     enum ResponseType {
64
         KeepInMemory,
62
         KeepInMemory,
65
         FileStorage
63
         FileStorage
66
-    };
64
+    }
67
 
65
 
68
     public static HashMap<String, Call> taskTable = new HashMap<>();
66
     public static HashMap<String, Call> taskTable = new HashMap<>();
69
     static HashMap<String, Boolean> progressReport = new HashMap<>();
67
     static HashMap<String, Boolean> progressReport = new HashMap<>();
87
     ResponseType responseType;
85
     ResponseType responseType;
88
     WritableMap respInfo;
86
     WritableMap respInfo;
89
     boolean timeout = false;
87
     boolean timeout = false;
88
+    ArrayList<String> redirects = new ArrayList<>();
90
 
89
 
91
     public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, final Callback callback) {
90
     public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, final Callback callback) {
92
         this.method = method.toUpperCase();
91
         this.method = method.toUpperCase();
104
             responseType = ResponseType.KeepInMemory;
103
             responseType = ResponseType.KeepInMemory;
105
 
104
 
106
 
105
 
107
-		if (body != null)
106
+        if (body != null)
108
             requestType = RequestType.SingleFile;
107
             requestType = RequestType.SingleFile;
109
         else if (arrayBody != null)
108
         else if (arrayBody != null)
110
             requestType = RequestType.Form;
109
             requestType = RequestType.Form;
156
 
155
 
157
         // find cached result if `key` property exists
156
         // find cached result if `key` property exists
158
         String cacheKey = this.taskId;
157
         String cacheKey = this.taskId;
159
-		String ext = this.options.appendExt.isEmpty() ? "." + this.options.appendExt : "";
158
+        String ext = this.options.appendExt.isEmpty() ? "." + this.options.appendExt : "";
160
 
159
 
161
-       	if (this.options.key != null) {
162
-           cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
163
-           if (cacheKey == null) {
164
-               cacheKey = this.taskId;
165
-           }
160
+        if (this.options.key != null) {
161
+            cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
162
+            if (cacheKey == null) {
163
+                cacheKey = this.taskId;
164
+            }
166
 
165
 
167
-           File file = new File(RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext);
166
+            File file = new File(RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext);
168
 
167
 
169
-           if (file.exists()) {
170
-               callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, file.getAbsolutePath());
171
-               return;
172
-           }
173
-       }
168
+            if (file.exists()) {
169
+                callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, file.getAbsolutePath());
170
+                return;
171
+            }
172
+        }
174
 
173
 
175
         if(this.options.path != null)
174
         if(this.options.path != null)
176
             this.destPath = this.options.path;
175
             this.destPath = this.options.path;
236
                 requestType = RequestType.WithoutBody;
235
                 requestType = RequestType.WithoutBody;
237
             }
236
             }
238
 
237
 
238
+            boolean isChunkedRequest = getHeaderIgnoreCases(mheaders, "Transfer-Encoding").equalsIgnoreCase("chunked");
239
 
239
 
240
             // set request body
240
             // set request body
241
             switch (requestType) {
241
             switch (requestType) {
242
                 case SingleFile:
242
                 case SingleFile:
243
-                    requestBody = new RNFetchBlobBody(
244
-                            taskId,
245
-                            requestType,
246
-                            rawRequestBody,
247
-                            MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type"))
248
-                    );
243
+                    requestBody = new RNFetchBlobBody(taskId)
244
+                            .chunkedEncoding(isChunkedRequest)
245
+                            .setRequestType(requestType)
246
+                            .setBody(rawRequestBody)
247
+                            .setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
249
                     builder.method(method, requestBody);
248
                     builder.method(method, requestBody);
250
                     break;
249
                     break;
251
                 case AsIs:
250
                 case AsIs:
252
-                    requestBody = new RNFetchBlobBody(
253
-                            taskId,
254
-                            requestType,
255
-                            rawRequestBody,
256
-                            MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type"))
257
-                    );
251
+                    requestBody = new RNFetchBlobBody(taskId)
252
+                            .chunkedEncoding(isChunkedRequest)
253
+                            .setRequestType(requestType)
254
+                            .setBody(rawRequestBody)
255
+                            .setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
258
                     builder.method(method, requestBody);
256
                     builder.method(method, requestBody);
259
                     break;
257
                     break;
260
                 case Form:
258
                 case Form:
261
                     String boundary = "RNFetchBlob-" + taskId;
259
                     String boundary = "RNFetchBlob-" + taskId;
262
-                    requestBody = new RNFetchBlobBody(
263
-                            taskId,
264
-                            requestType,
265
-                            rawRequestBodyArray,
266
-                            MediaType.parse("multipart/form-data; boundary="+ boundary)
267
-                    );
260
+                    requestBody = new RNFetchBlobBody(taskId)
261
+                            .chunkedEncoding(isChunkedRequest)
262
+                            .setRequestType(requestType)
263
+                            .setBody(rawRequestBodyArray)
264
+                            .setMIME(MediaType.parse("multipart/form-data; boundary="+ boundary));
268
                     builder.method(method, requestBody);
265
                     builder.method(method, requestBody);
269
                     break;
266
                     break;
270
 
267
 
279
             }
276
             }
280
 
277
 
281
             final Request req = builder.build();
278
             final Request req = builder.build();
282
-
279
+            clientBuilder.addNetworkInterceptor(new Interceptor() {
280
+                @Override
281
+                public Response intercept(Chain chain) throws IOException {
282
+                        redirects.add(chain.request().url().toString());
283
+                        return chain.proceed(chain.request());
284
+                    }
285
+            });
283
             // Add request interceptor for upload progress event
286
             // Add request interceptor for upload progress event
284
             clientBuilder.addInterceptor(new Interceptor() {
287
             clientBuilder.addInterceptor(new Interceptor() {
285
                 @Override
288
                 @Override
334
             clientBuilder.retryOnConnectionFailure(false);
337
             clientBuilder.retryOnConnectionFailure(false);
335
             clientBuilder.followRedirects(true);
338
             clientBuilder.followRedirects(true);
336
 
339
 
340
+
337
             OkHttpClient client = clientBuilder.retryOnConnectionFailure(true).build();
341
             OkHttpClient client = clientBuilder.retryOnConnectionFailure(true).build();
338
             Call call =  client.newCall(req);
342
             Call call =  client.newCall(req);
339
             taskTable.put(taskId, call);
343
             taskTable.put(taskId, call);
414
                 try {
418
                 try {
415
                     // For XMLHttpRequest, automatic response data storing strategy, when response
419
                     // For XMLHttpRequest, automatic response data storing strategy, when response
416
                     // data is considered as binary data, write it to file system
420
                     // data is considered as binary data, write it to file system
417
-                    if(isBlobResp && options.auto == true) {
421
+                    if(isBlobResp && options.auto) {
418
                         String dest = RNFetchBlobFS.getTmpPath(ctx, taskId);
422
                         String dest = RNFetchBlobFS.getTmpPath(ctx, taskId);
419
                         InputStream ins = resp.body().byteStream();
423
                         InputStream ins = resp.body().byteStream();
420
                         FileOutputStream os = new FileOutputStream(new File(dest));
424
                         FileOutputStream os = new FileOutputStream(new File(dest));
458
                     // It uses customized response body which is able to report download progress
462
                     // It uses customized response body which is able to report download progress
459
                     // and write response data to destination path.
463
                     // and write response data to destination path.
460
                     resp.body().bytes();
464
                     resp.body().bytes();
461
-                } catch (Exception ignored) {  }
465
+                } catch (Exception ignored) {
466
+                    ignored.printStackTrace();
467
+                }
462
                 callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
468
                 callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
463
                 break;
469
                 break;
464
             default:
470
             default:
469
                 }
475
                 }
470
                 break;
476
                 break;
471
         }
477
         }
472
-//        if(!resp.isSuccessful())
473
-//            resp.body().close();
474
-        resp.body().close();
478
+        if(!resp.isSuccessful())
479
+            resp.body().close();
475
         releaseTaskResource();
480
         releaseTaskResource();
476
     }
481
     }
477
 
482
 
496
     }
501
     }
497
 
502
 
498
     /**
503
     /**
499
-     * Create response information object, conatins status code, headers, etc.
500
-     * @param resp
501
-     * @param isBlobResp
502
-     * @return
504
+     * Create response information object, contains status code, headers, etc.
505
+     * @param resp Response object
506
+     * @param isBlobResp If the response is binary data
507
+     * @return Get RCT bridge object contains response information.
503
      */
508
      */
504
     private WritableMap getResponseInfo(Response resp, boolean isBlobResp) {
509
     private WritableMap getResponseInfo(Response resp, boolean isBlobResp) {
505
         WritableMap info = Arguments.createMap();
510
         WritableMap info = Arguments.createMap();
511
         for(int i =0;i< resp.headers().size();i++) {
516
         for(int i =0;i< resp.headers().size();i++) {
512
             headers.putString(resp.headers().name(i), resp.headers().value(i));
517
             headers.putString(resp.headers().name(i), resp.headers().value(i));
513
         }
518
         }
519
+        WritableArray redirectList = Arguments.createArray();
520
+        for(String r : redirects) {
521
+                redirectList.pushString(r);
522
+        }
523
+        info.putArray("redirects", redirectList);
514
         info.putMap("headers", headers);
524
         info.putMap("headers", headers);
515
         Headers h = resp.headers();
525
         Headers h = resp.headers();
516
         if(isBlobResp) {
526
         if(isBlobResp) {
531
     /**
541
     /**
532
      * Check if response data is binary data.
542
      * Check if response data is binary data.
533
      * @param resp OkHttp response.
543
      * @param resp OkHttp response.
534
-     * @return
544
+     * @return If the response data contains binary bytes
535
      */
545
      */
536
     private boolean isBlobResponse(Response resp) {
546
     private boolean isBlobResponse(Response resp) {
537
         Headers h = resp.headers();
547
         Headers h = resp.headers();

+ 1
- 3
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java Bestand weergeven

16
 
16
 
17
 import okhttp3.OkHttpClient;
17
 import okhttp3.OkHttpClient;
18
 
18
 
19
-/**
20
- * Created by wkh237 on 2016/7/11.
21
- */
19
+
22
 public class RNFetchBlobUtils {
20
 public class RNFetchBlobUtils {
23
 
21
 
24
     public static String getMD5(String input) {
22
     public static String getMD5(String input) {

+ 12
- 12
src/class/RNFetchBlobReadStream.js Bestand weergeven

8
   DeviceEventEmitter,
8
   DeviceEventEmitter,
9
   NativeAppEventEmitter,
9
   NativeAppEventEmitter,
10
 } from 'react-native'
10
 } from 'react-native'
11
+import UUID from '../utils/uuid'
11
 
12
 
12
 const RNFetchBlob = NativeModules.RNFetchBlob
13
 const RNFetchBlob = NativeModules.RNFetchBlob
13
 const emitter = DeviceEventEmitter
14
 const emitter = DeviceEventEmitter
18
   encoding : 'utf8' | 'ascii' | 'base64';
19
   encoding : 'utf8' | 'ascii' | 'base64';
19
   bufferSize : ?number;
20
   bufferSize : ?number;
20
   closed : boolean;
21
   closed : boolean;
22
+  tick : number = 10;
21
 
23
 
22
-  constructor(path:string, encoding:string, bufferSize?:?number) {
24
+  constructor(path:string, encoding:string, bufferSize?:?number, tick:number) {
23
     if(!path)
25
     if(!path)
24
       throw Error('RNFetchBlob could not open file stream with empty `path`')
26
       throw Error('RNFetchBlob could not open file stream with empty `path`')
25
     this.encoding = encoding || 'utf8'
27
     this.encoding = encoding || 'utf8'
26
     this.bufferSize = bufferSize
28
     this.bufferSize = bufferSize
27
     this.path = path
29
     this.path = path
28
     this.closed = false
30
     this.closed = false
31
+    this.tick = tick
29
     this._onData = () => {}
32
     this._onData = () => {}
30
     this._onEnd = () => {}
33
     this._onEnd = () => {}
31
     this._onError = () => {}
34
     this._onError = () => {}
35
+    this.streamId = 'RNFBRS'+ UUID()
32
 
36
 
33
     // register for file stream event
37
     // register for file stream event
34
-    let subscription = emitter.addListener(`RNFetchBlobStream+${this.path}`, (e) => {
35
-
38
+    let subscription = emitter.addListener(this.streamId, (e) => {
36
       let {event, detail} = e
39
       let {event, detail} = e
37
-      if(this._onData && event === 'data')
40
+      if(this._onData && event === 'data') {
38
         this._onData(detail)
41
         this._onData(detail)
42
+        return
43
+      }
39
       else if (this._onEnd && event === 'end') {
44
       else if (this._onEnd && event === 'end') {
40
         this._onEnd(detail)
45
         this._onEnd(detail)
41
       }
46
       }
56
 
61
 
57
   open() {
62
   open() {
58
     if(!this.closed)
63
     if(!this.closed)
59
-      RNFetchBlob.readStream(this.path, this.encoding, this.bufferSize || 0)
64
+      RNFetchBlob.readStream(this.path, this.encoding, this.bufferSize || 10240 , this.tick || -1, this.streamId)
60
     else
65
     else
61
       throw new Error('Stream closed')
66
       throw new Error('Stream closed')
62
   }
67
   }
63
 
68
 
64
-  onData(fn) {
65
-    if(this.encoding.toLowerCase() === 'ascii')
66
-      this._onData = (data) => {
67
-        fn(data)
68
-      }
69
-    else
70
-      this._onData = fn
69
+  onData(fn:() => void) {
70
+    this._onData = fn
71
   }
71
   }
72
 
72
 
73
   onError(fn) {
73
   onError(fn) {

+ 3
- 2
src/fs.js Bestand weergeven

113
 function readStream(
113
 function readStream(
114
   path : string,
114
   path : string,
115
   encoding : 'utf8' | 'ascii' | 'base64',
115
   encoding : 'utf8' | 'ascii' | 'base64',
116
-  bufferSize? : ?number
116
+  bufferSize? : ?number,
117
+  tick : ?number = 10
117
 ):Promise<RNFetchBlobReadStream> {
118
 ):Promise<RNFetchBlobReadStream> {
118
-  return Promise.resolve(new RNFetchBlobReadStream(path, encoding, bufferSize))
119
+  return Promise.resolve(new RNFetchBlobReadStream(path, encoding, bufferSize, tick))
119
 }
120
 }
120
 
121
 
121
 /**
122
 /**

+ 6
- 18
src/index.js Bestand weergeven

261
       delete promise['uploadProgress']
261
       delete promise['uploadProgress']
262
       delete promise['stateChange']
262
       delete promise['stateChange']
263
       delete promise['cancel']
263
       delete promise['cancel']
264
-      promise.cancel = () => {
265
-        console.warn('finished request could not be canceled')
266
-      }
264
+      promise.cancel = () => {}
267
 
265
 
268
       if(err)
266
       if(err)
269
         reject(new Error(err, respInfo))
267
         reject(new Error(err, respInfo))
320
   path : () => string | null;
318
   path : () => string | null;
321
   type : 'base64' | 'path' | 'utf8';
319
   type : 'base64' | 'path' | 'utf8';
322
   data : any;
320
   data : any;
323
-  blob : (contentType:string, sliceSize:number) => null;
324
-  text : () => string;
321
+  blob : (contentType:string, sliceSize:number) => Promise<Blob>;
322
+  text : () => string | Promise<any>;
325
   json : () => any;
323
   json : () => any;
326
   base64 : () => any;
324
   base64 : () => any;
327
   flush : () => void;
325
   flush : () => void;
328
   respInfo : RNFetchBlobResponseInfo;
326
   respInfo : RNFetchBlobResponseInfo;
329
   session : (name:string) => RNFetchBlobSession | null;
327
   session : (name:string) => RNFetchBlobSession | null;
330
-  readFile : (encode: 'base64' | 'utf8' | 'ascii') => ?Promise;
328
+  readFile : (encode: 'base64' | 'utf8' | 'ascii') => ?Promise<any>;
331
   readStream : (
329
   readStream : (
332
     encode: 'utf8' | 'ascii' | 'base64',
330
     encode: 'utf8' | 'ascii' | 'base64',
333
   ) => RNFetchBlobStream | null;
331
   ) => RNFetchBlobStream | null;
366
      * Convert result to text.
364
      * Convert result to text.
367
      * @return {string} Decoded base64 string.
365
      * @return {string} Decoded base64 string.
368
      */
366
      */
369
-    this.text = ():string => {
367
+    this.text = ():string | Promise<any> => {
370
       let res = this.data
368
       let res = this.data
371
       switch(this.type) {
369
       switch(this.type) {
372
         case 'base64':
370
         case 'base64':
373
           return base64.decode(this.data)
371
           return base64.decode(this.data)
374
-        break
375
         case 'path':
372
         case 'path':
376
           return fs.readFile(this.data, 'base64').then((b64) => Promise.resolve(base64.decode(b64)))
373
           return fs.readFile(this.data, 'base64').then((b64) => Promise.resolve(base64.decode(b64)))
377
-        break
378
         default:
374
         default:
379
           return this.data
375
           return this.data
380
-        break
381
       }
376
       }
382
     }
377
     }
383
     /**
378
     /**
388
       switch(this.type) {
383
       switch(this.type) {
389
         case 'base64':
384
         case 'base64':
390
           return JSON.parse(base64.decode(this.data))
385
           return JSON.parse(base64.decode(this.data))
391
-        break
392
         case 'path':
386
         case 'path':
393
           return fs.readFile(this.data, 'utf8')
387
           return fs.readFile(this.data, 'utf8')
394
                    .then((text) => Promise.resolve(JSON.parse(text)))
388
                    .then((text) => Promise.resolve(JSON.parse(text)))
395
-        break
396
         default:
389
         default:
397
           return JSON.parse(this.data)
390
           return JSON.parse(this.data)
398
-        break
399
       }
391
       }
400
     }
392
     }
401
     /**
393
     /**
402
      * Return BASE64 string directly.
394
      * Return BASE64 string directly.
403
      * @return {string} BASE64 string of response body.
395
      * @return {string} BASE64 string of response body.
404
      */
396
      */
405
-    this.base64 = ():string => {
397
+    this.base64 = ():string | Promise<any> => {
406
       switch(this.type) {
398
       switch(this.type) {
407
         case 'base64':
399
         case 'base64':
408
           return this.data
400
           return this.data
409
-        break
410
         case 'path':
401
         case 'path':
411
           return fs.readFile(this.data, 'base64')
402
           return fs.readFile(this.data, 'base64')
412
-        break
413
         default:
403
         default:
414
           return base64.encode(this.data)
404
           return base64.encode(this.data)
415
-        break
416
       }
405
       }
417
     }
406
     }
418
     /**
407
     /**
467
     this.readFile = (encode: 'base64' | 'utf8' | 'ascii') => {
456
     this.readFile = (encode: 'base64' | 'utf8' | 'ascii') => {
468
       if(this.type === 'path') {
457
       if(this.type === 'path') {
469
         encode = encode || 'utf8'
458
         encode = encode || 'utf8'
470
-
471
         return readFile(this.data, encode)
459
         return readFile(this.data, encode)
472
       }
460
       }
473
       else {
461
       else {

+ 27
- 0
src/ios/IOS7Polyfill.h Bestand weergeven

1
+//
2
+//  IOS7Polyfill.h
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/9/6.
6
+//  Copyright © 2016年 wkh237.github.io. All rights reserved.
7
+//
8
+
9
+#ifndef IOS7Polyfill_h
10
+#define IOS7Polyfill_h
11
+
12
+@interface NSString (Contains)
13
+
14
+- (BOOL)RNFBContainsString:(NSString*)other;
15
+
16
+@end
17
+
18
+@implementation NSString (Contains)
19
+
20
+- (BOOL)RNFBContainsString:(NSString*)other {
21
+    NSRange range = [self rangeOfString:other];
22
+    return range.length != 0;
23
+}
24
+
25
+
26
+@end
27
+#endif /* IOS7Polyfill_h */

+ 3
- 1
src/ios/RNFetchBlob.xcodeproj/project.pbxproj Bestand weergeven

39
 		A15C30131CD25C330074CB35 /* RNFetchBlob.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFetchBlob.m; path = RNFetchBlob/RNFetchBlob.m; sourceTree = "<group>"; };
39
 		A15C30131CD25C330074CB35 /* RNFetchBlob.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFetchBlob.m; path = RNFetchBlob/RNFetchBlob.m; sourceTree = "<group>"; };
40
 		A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobReqBuilder.h; sourceTree = "<group>"; };
40
 		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>"; };
41
 		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>"; };
42
 /* End PBXFileReference section */
43
 /* End PBXFileReference section */
43
 
44
 
44
 /* Begin PBXFrameworksBuildPhase section */
45
 /* Begin PBXFrameworksBuildPhase section */
62
 		A15C30051CD25C330074CB35 = {
63
 		A15C30051CD25C330074CB35 = {
63
 			isa = PBXGroup;
64
 			isa = PBXGroup;
64
 			children = (
65
 			children = (
66
+				A1F950181D7E9134002A95A6 /* IOS7Polyfill.h */,
65
 				A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */,
67
 				A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */,
66
 				A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */,
68
 				A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */,
67
 				A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */,
69
 				A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */,
112
 			isa = PBXProject;
114
 			isa = PBXProject;
113
 			attributes = {
115
 			attributes = {
114
 				LastUpgradeCheck = 730;
116
 				LastUpgradeCheck = 730;
115
-				ORGANIZATIONNAME = suzuri04x2;
117
+				ORGANIZATIONNAME = wkh237.github.io;
116
 				TargetAttributes = {
118
 				TargetAttributes = {
117
 					A15C300D1CD25C330074CB35 = {
119
 					A15C300D1CD25C330074CB35 = {
118
 						CreatedOnToolsVersion = 7.3;
120
 						CreatedOnToolsVersion = 7.3;

+ 2
- 1
src/ios/RNFetchBlob/RNFetchBlob.h Bestand weergeven

17
 }
17
 }
18
 
18
 
19
 @property (nonatomic) NSString * filePathPrefix;
19
 @property (nonatomic) NSString * filePathPrefix;
20
-@property (nonatomic, strong) UIDocumentInteractionController *documentController;
20
+
21
++ (RCTBridge *)getRCTBridge;
21
 
22
 
22
 @end
23
 @end
23
 
24
 

+ 107
- 46
src/ios/RNFetchBlob/RNFetchBlob.m Bestand weergeven

5
 //
5
 //
6
 
6
 
7
 #import "RNFetchBlob.h"
7
 #import "RNFetchBlob.h"
8
-#import "RCTConvert.h"
9
 #import "RCTLog.h"
8
 #import "RCTLog.h"
10
 #import "RCTBridge.h"
9
 #import "RCTBridge.h"
11
 #import "RCTEventDispatcher.h"
10
 #import "RCTEventDispatcher.h"
15
 #import "RNFetchBlobReqBuilder.h"
14
 #import "RNFetchBlobReqBuilder.h"
16
 
15
 
17
 
16
 
17
+RCTBridge * bridgeRef;
18
+dispatch_queue_t commonTaskQueue;
19
+dispatch_queue_t fsQueue;
20
+
18
 ////////////////////////////////////////
21
 ////////////////////////////////////////
19
 //
22
 //
20
 //  Exported native methods
23
 //  Exported native methods
30
 @synthesize bridge = _bridge;
33
 @synthesize bridge = _bridge;
31
 
34
 
32
 - (dispatch_queue_t) methodQueue {
35
 - (dispatch_queue_t) methodQueue {
33
-    return dispatch_queue_create("RNFetchBlob.queue", DISPATCH_QUEUE_SERIAL);
36
+    if(commonTaskQueue == nil)
37
+        commonTaskQueue = dispatch_queue_create("RNFetchBlob.queue", DISPATCH_QUEUE_SERIAL);
38
+    return commonTaskQueue;
39
+}
40
+
41
++ (RCTBridge *)getRCTBridge
42
+{
43
+    return bridgeRef;
34
 }
44
 }
35
 
45
 
36
 RCT_EXPORT_MODULE();
46
 RCT_EXPORT_MODULE();
38
 - (id) init {
48
 - (id) init {
39
     self = [super init];
49
     self = [super init];
40
     self.filePathPrefix = FILE_PREFIX;
50
     self.filePathPrefix = FILE_PREFIX;
51
+    if(commonTaskQueue == nil)
52
+        commonTaskQueue = dispatch_queue_create("RNFetchBlob.queue", DISPATCH_QUEUE_SERIAL);
53
+    if(fsQueue == nil)
54
+        fsQueue = dispatch_queue_create("RNFetchBlob.fs.queue", DISPATCH_QUEUE_SERIAL);
41
     BOOL isDir;
55
     BOOL isDir;
42
     // if temp folder not exists, create one
56
     // if temp folder not exists, create one
43
     if(![[NSFileManager defaultManager] fileExistsAtPath: [RNFetchBlobFS getTempPath] isDirectory:&isDir]) {
57
     if(![[NSFileManager defaultManager] fileExistsAtPath: [RNFetchBlobFS getTempPath] isDirectory:&isDir]) {
44
         [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
58
         [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
45
     }
59
     }
60
+    bridgeRef = _bridge;
46
     return self;
61
     return self;
47
 }
62
 }
48
 
63
 
87
     }];
102
     }];
88
 }
103
 }
89
 
104
 
105
+#pragma mark - fs.createFile
90
 RCT_EXPORT_METHOD(createFile:(NSString *)path data:(NSString *)data encoding:(NSString *)encoding callback:(RCTResponseSenderBlock)callback) {
106
 RCT_EXPORT_METHOD(createFile:(NSString *)path data:(NSString *)data encoding:(NSString *)encoding callback:(RCTResponseSenderBlock)callback) {
91
 
107
 
92
     NSFileManager * fm = [NSFileManager defaultManager];
108
     NSFileManager * fm = [NSFileManager defaultManager];
113
         callback(@[[NSString stringWithFormat:@"failed to create new file at path %@ please ensure the folder exists"]]);
129
         callback(@[[NSString stringWithFormat:@"failed to create new file at path %@ please ensure the folder exists"]]);
114
 
130
 
115
 }
131
 }
116
-
132
+#pragma mark - fs.createFileASCII
117
 // method for create file with ASCII content
133
 // method for create file with ASCII content
118
 RCT_EXPORT_METHOD(createFileASCII:(NSString *)path data:(NSArray *)dataArray callback:(RCTResponseSenderBlock)callback) {
134
 RCT_EXPORT_METHOD(createFileASCII:(NSString *)path data:(NSArray *)dataArray callback:(RCTResponseSenderBlock)callback) {
119
 
135
 
135
 
151
 
136
 }
152
 }
137
 
153
 
138
-
154
+#pragma mark - fs.exists
139
 RCT_EXPORT_METHOD(exists:(NSString *)path callback:(RCTResponseSenderBlock)callback) {
155
 RCT_EXPORT_METHOD(exists:(NSString *)path callback:(RCTResponseSenderBlock)callback) {
140
-    BOOL isDir = NO;
141
-    BOOL exists = NO;
142
-    exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
143
-    callback(@[@(exists), @(isDir)]);
144
-
156
+    [RNFetchBlobFS exists:path callback:callback];
145
 }
157
 }
146
 
158
 
159
+#pragma mark - fs.writeFile
147
 RCT_EXPORT_METHOD(writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
160
 RCT_EXPORT_METHOD(writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
148
-    
149
     [RNFetchBlobFS writeFile:path encoding:[NSString stringWithString:encoding] data:data append:append resolver:resolve rejecter:reject];
161
     [RNFetchBlobFS writeFile:path encoding:[NSString stringWithString:encoding] data:data append:append resolver:resolve rejecter:reject];
150
 })
162
 })
151
 
163
 
164
+#pragma mark - fs.writeArray
152
 RCT_EXPORT_METHOD(writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
165
 RCT_EXPORT_METHOD(writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
153
     [RNFetchBlobFS writeFileArray:path data:data append:append resolver:resolve rejecter:reject];
166
     [RNFetchBlobFS writeFileArray:path data:data append:append resolver:resolve rejecter:reject];
154
 })
167
 })
155
 
168
 
169
+#pragma mark - fs.writeStream
156
 RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding appendData:(BOOL)append callback:(RCTResponseSenderBlock)callback) {
170
 RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding appendData:(BOOL)append callback:(RCTResponseSenderBlock)callback) {
157
     RNFetchBlobFS * fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
171
     RNFetchBlobFS * fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
158
     NSFileManager * fm = [NSFileManager defaultManager];
172
     NSFileManager * fm = [NSFileManager defaultManager];
166
     callback(@[[NSNull null], streamId]);
180
     callback(@[[NSNull null], streamId]);
167
 }
181
 }
168
 
182
 
183
+#pragma mark - fs.writeArrayChunk
169
 RCT_EXPORT_METHOD(writeArrayChunk:(NSString *)streamId withArray:(NSArray *)dataArray callback:(RCTResponseSenderBlock) callback) {
184
 RCT_EXPORT_METHOD(writeArrayChunk:(NSString *)streamId withArray:(NSArray *)dataArray callback:(RCTResponseSenderBlock) callback) {
170
     RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
185
     RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
171
     char * bytes = (char *) malloc([dataArray count]);
186
     char * bytes = (char *) malloc([dataArray count]);
179
     callback(@[[NSNull null]]);
194
     callback(@[[NSNull null]]);
180
 }
195
 }
181
 
196
 
197
+#pragma mark - fs.writeChunk
182
 RCT_EXPORT_METHOD(writeChunk:(NSString *)streamId withData:(NSString *)data callback:(RCTResponseSenderBlock) callback) {
198
 RCT_EXPORT_METHOD(writeChunk:(NSString *)streamId withData:(NSString *)data callback:(RCTResponseSenderBlock) callback) {
183
     RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
199
     RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
184
     [fs writeEncodeChunk:data];
200
     [fs writeEncodeChunk:data];
185
     callback(@[[NSNull null]]);
201
     callback(@[[NSNull null]]);
186
 }
202
 }
187
 
203
 
204
+#pragma mark - fs.closeStream
188
 RCT_EXPORT_METHOD(closeStream:(NSString *)streamId callback:(RCTResponseSenderBlock) callback) {
205
 RCT_EXPORT_METHOD(closeStream:(NSString *)streamId callback:(RCTResponseSenderBlock) callback) {
189
     RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
206
     RNFetchBlobFS *fs = [[RNFetchBlobFS getFileStreams] valueForKey:streamId];
190
     [fs closeOutStream];
207
     [fs closeOutStream];
191
     callback(@[[NSNull null], @YES]);
208
     callback(@[[NSNull null], @YES]);
192
 }
209
 }
193
 
210
 
211
+#pragma mark - unlink
194
 RCT_EXPORT_METHOD(unlink:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
212
 RCT_EXPORT_METHOD(unlink:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
195
     NSError * error = nil;
213
     NSError * error = nil;
196
     NSString * tmpPath = nil;
214
     NSString * tmpPath = nil;
201
         callback(@[[NSString stringWithFormat:@"failed to unlink file or path at %@", path]]);
219
         callback(@[[NSString stringWithFormat:@"failed to unlink file or path at %@", path]]);
202
 }
220
 }
203
 
221
 
222
+#pragma mark - fs.removeSession
204
 RCT_EXPORT_METHOD(removeSession:(NSArray *)paths callback:(RCTResponseSenderBlock) callback) {
223
 RCT_EXPORT_METHOD(removeSession:(NSArray *)paths callback:(RCTResponseSenderBlock) callback) {
205
     NSError * error = nil;
224
     NSError * error = nil;
206
     NSString * tmpPath = nil;
225
     NSString * tmpPath = nil;
216
 
235
 
217
 }
236
 }
218
 
237
 
238
+#pragma mark - fs.ls
219
 RCT_EXPORT_METHOD(ls:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
239
 RCT_EXPORT_METHOD(ls:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
220
     NSFileManager* fm = [NSFileManager defaultManager];
240
     NSFileManager* fm = [NSFileManager defaultManager];
221
     BOOL exist = nil;
241
     BOOL exist = nil;
235
 
255
 
236
 }
256
 }
237
 
257
 
238
-RCT_EXPORT_METHOD(stat:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
239
-    NSFileManager* fm = [NSFileManager defaultManager];
240
-    BOOL exist = nil;
241
-    BOOL isDir = nil;
242
-    NSError * error = nil;
243
-
244
-    path = [RNFetchBlobFS getPathOfAsset:path];
245
-
246
-    exist = [fm fileExistsAtPath:path isDirectory:&isDir];
247
-    if(exist == NO) {
248
-        callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not exist", path]]);
249
-        return ;
250
-    }
251
-    NSData * res = [RNFetchBlobFS stat:path error:&error];
252
-
253
-    if(error == nil)
254
-        callback(@[[NSNull null], res]);
255
-    else
256
-        callback(@[[error localizedDescription], [NSNull null]]);
258
+#pragma mark - fs.stat
259
+RCT_EXPORT_METHOD(stat:(NSString *)target callback:(RCTResponseSenderBlock) callback) {
260
+    
261
+    [RNFetchBlobFS getPathFromUri:target completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
262
+        __block NSMutableArray * result;
263
+        if(path != nil)
264
+        {
265
+            NSFileManager* fm = [NSFileManager defaultManager];
266
+            BOOL exist = nil;
267
+            BOOL isDir = nil;
268
+            NSError * error = nil;
269
+            
270
+            exist = [fm fileExistsAtPath:path isDirectory:&isDir];
271
+            if(exist == NO) {
272
+                callback(@[[NSString stringWithFormat:@"failed to stat path `%@` for it is not exist or it is not exist", path]]);
273
+                return ;
274
+            }
275
+            result = [RNFetchBlobFS stat:path error:&error];
276
+            
277
+            if(error == nil)
278
+                callback(@[[NSNull null], result]);
279
+            else
280
+                callback(@[[error localizedDescription], [NSNull null]]);
257
 
281
 
282
+        }
283
+        else if(asset != nil)
284
+        {
285
+            __block NSNumber * size = [NSNumber numberWithLong:[asset size]];
286
+            result = [asset metadata];
287
+            [result setValue:size forKey:@"size"];
288
+            callback(@[[NSNull null], result]);
289
+        }
290
+        else
291
+        {
292
+            callback(@[@"failed to stat path, could not resolve URI", [NSNull null]]);
293
+        }
294
+    }];
258
 }
295
 }
259
 
296
 
297
+#pragma mark - fs.lstat
260
 RCT_EXPORT_METHOD(lstat:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
298
 RCT_EXPORT_METHOD(lstat:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
261
     NSFileManager* fm = [NSFileManager defaultManager];
299
     NSFileManager* fm = [NSFileManager defaultManager];
262
     BOOL exist = nil;
300
     BOOL exist = nil;
290
 
328
 
291
 }
329
 }
292
 
330
 
293
-RCT_EXPORT_METHOD(cp:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
294
-    NSError * error = nil;
295
-    path = [RNFetchBlobFS getPathOfAsset:path];
296
-    BOOL result = [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
297
-
298
-    if(error == nil)
299
-        callback(@[[NSNull null], @YES]);
300
-    else
301
-        callback(@[[error localizedDescription], @NO]);
302
-
331
+#pragma mark - fs.cp
332
+RCT_EXPORT_METHOD(cp:(NSString*)src toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
333
+    
334
+//    path = [RNFetchBlobFS getPathOfAsset:path];
335
+    [RNFetchBlobFS getPathFromUri:src completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
336
+        NSError * error = nil;
337
+        if(path == nil)
338
+        {
339
+            [RNFetchBlobFS writeAssetToPath:asset dest:dest];
340
+            callback(@[[NSNull null], @YES]);
341
+        }
342
+        else
343
+        {
344
+            BOOL result = [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
345
+            
346
+            if(error == nil)
347
+                callback(@[[NSNull null], @YES]);
348
+            else
349
+                callback(@[[error localizedDescription], @NO]);
350
+        }
351
+    }];
352
+    
303
 }
353
 }
304
 
354
 
355
+
356
+#pragma mark - fs.mv
305
 RCT_EXPORT_METHOD(mv:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
357
 RCT_EXPORT_METHOD(mv:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
306
     NSError * error = nil;
358
     NSError * error = nil;
307
     BOOL result = [[NSFileManager defaultManager] moveItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
359
     BOOL result = [[NSFileManager defaultManager] moveItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
313
 
365
 
314
 }
366
 }
315
 
367
 
368
+#pragma mark - fs.mkdir
316
 RCT_EXPORT_METHOD(mkdir:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
369
 RCT_EXPORT_METHOD(mkdir:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
317
-    if([RNFetchBlobFS exists:path]) {
370
+    if([[NSFileManager defaultManager] fileExistsAtPath:path]) {
318
         callback(@[@"mkdir failed, folder already exists"]);
371
         callback(@[@"mkdir failed, folder already exists"]);
319
         return;
372
         return;
320
     }
373
     }
323
     callback(@[[NSNull null]]);
376
     callback(@[[NSNull null]]);
324
 }
377
 }
325
 
378
 
379
+#pragma mark - fs.readFile
326
 RCT_EXPORT_METHOD(readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
380
 RCT_EXPORT_METHOD(readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
327
 
381
 
328
     [RNFetchBlobFS readFile:path encoding:encoding resolver:resolve rejecter:reject onComplete:nil];
382
     [RNFetchBlobFS readFile:path encoding:encoding resolver:resolve rejecter:reject onComplete:nil];
329
 })
383
 })
330
 
384
 
331
-RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
332
-
333
-    RNFetchBlobFS *fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
385
+#pragma mark - fs.readStream
386
+RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize tick:(int)tick streamId:(NSString *)streamId
387
+{
334
     if(bufferSize == nil) {
388
     if(bufferSize == nil) {
335
         if([[encoding lowercaseString] isEqualToString:@"base64"])
389
         if([[encoding lowercaseString] isEqualToString:@"base64"])
336
             bufferSize = 4095;
390
             bufferSize = 4095;
337
         else
391
         else
338
             bufferSize = 4096;
392
             bufferSize = 4096;
339
     }
393
     }
340
-    // read asset stream
341
-    [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
342
-}
394
+    
395
+    dispatch_async(fsQueue, ^{
396
+        [RNFetchBlobFS readStream:path encoding:encoding bufferSize:bufferSize tick:tick streamId:streamId bridgeRef:_bridge];
397
+    });
398
+})
343
 
399
 
400
+#pragma mark - fs.getEnvionmentDirs
344
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
401
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
345
 
402
 
346
     callback(@[
403
     callback(@[
349
                ]);
406
                ]);
350
 }
407
 }
351
 
408
 
409
+#pragma mark - net.cancelRequest
352
 RCT_EXPORT_METHOD(cancelRequest:(NSString *)taskId callback:(RCTResponseSenderBlock)callback) {
410
 RCT_EXPORT_METHOD(cancelRequest:(NSString *)taskId callback:(RCTResponseSenderBlock)callback) {
353
     [RNFetchBlobNetwork cancelRequest:taskId];
411
     [RNFetchBlobNetwork cancelRequest:taskId];
354
     callback(@[[NSNull null], taskId]);
412
     callback(@[[NSNull null], taskId]);
355
 
413
 
356
 }
414
 }
357
 
415
 
416
+#pragma mark - net.enableProgressReport
358
 RCT_EXPORT_METHOD(enableProgressReport:(NSString *)taskId {
417
 RCT_EXPORT_METHOD(enableProgressReport:(NSString *)taskId {
359
     [RNFetchBlobNetwork enableProgressReport:taskId];
418
     [RNFetchBlobNetwork enableProgressReport:taskId];
360
 })
419
 })
361
 
420
 
421
+#pragma mark - net.enableUploadProgressReport
362
 RCT_EXPORT_METHOD(enableUploadProgressReport:(NSString *)taskId {
422
 RCT_EXPORT_METHOD(enableUploadProgressReport:(NSString *)taskId {
363
     [RNFetchBlobNetwork enableUploadProgress:taskId];
423
     [RNFetchBlobNetwork enableUploadProgress:taskId];
364
 })
424
 })
365
 
425
 
426
+#pragma mark - fs.slice
366
 RCT_EXPORT_METHOD(slice:(NSString *)src dest:(NSString *)dest start:(nonnull NSNumber *)start end:(nonnull NSNumber *)end resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
427
 RCT_EXPORT_METHOD(slice:(NSString *)src dest:(NSString *)dest start:(nonnull NSNumber *)start end:(nonnull NSNumber *)end resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
367
 {
428
 {
368
     [RNFetchBlobFS slice:src dest:dest start:start end:end encode:@"" resolver:resolve rejecter:reject];
429
     [RNFetchBlobFS slice:src dest:dest start:start end:end encode:@"" resolver:resolve rejecter:reject];

+ 4
- 4
src/ios/RNFetchBlobFS.h Bestand weergeven

50
 + (RNFetchBlobFS *) getFileStreams;
50
 + (RNFetchBlobFS *) getFileStreams;
51
 + (BOOL) mkdir:(NSString *) path;
51
 + (BOOL) mkdir:(NSString *) path;
52
 + (NSDictionary *) stat:(NSString *) path error:(NSError **) error;
52
 + (NSDictionary *) stat:(NSString *) path error:(NSError **) error;
53
-+ (BOOL) exists:(NSString *) path;
53
++ (void) exists:(NSString *) path callback:(RCTResponseSenderBlock)callback;
54
 + (void) writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
54
 + (void) writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
55
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
55
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
56
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject onComplete:(void (^)(NSData * content))onComplete;
56
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject onComplete:(void (^)(NSData * content))onComplete;
57
 + (void) readAssetFile:(NSData *)assetUrl completionBlock:(void(^)(NSData * content))completionBlock failBlock:(void(^)(NSError * err))failBlock;
57
 + (void) readAssetFile:(NSData *)assetUrl completionBlock:(void(^)(NSData * content))completionBlock failBlock:(void(^)(NSError * err))failBlock;
58
-+ (void)slice:(NSString *)path
58
++ (void) slice:(NSString *)path
59
          dest:(NSString *)dest
59
          dest:(NSString *)dest
60
         start:(nonnull NSNumber *)start
60
         start:(nonnull NSNumber *)start
61
           end:(nonnull NSNumber *)end
61
           end:(nonnull NSNumber *)end
63
      resolver:(RCTPromiseResolveBlock)resolve
63
      resolver:(RCTPromiseResolveBlock)resolve
64
      rejecter:(RCTPromiseRejectBlock)reject;
64
      rejecter:(RCTPromiseRejectBlock)reject;
65
 //+ (void) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append;
65
 //+ (void) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append;
66
++ (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest;
67
++ (void) readStream:(NSString *)uri encoding:(NSString * )encoding bufferSize:(int)bufferSize tick:(int)tick streamId:(NSString *)streamId bridgeRef:(RCTBridge *)bridgeRef;
66
 
68
 
67
 // constructor
69
 // constructor
68
 - (id) init;
70
 - (id) init;
72
 // file stream
74
 // file stream
73
 - (void) openWithDestination;
75
 - (void) openWithDestination;
74
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
76
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
75
-- (void) startAssetReadStream:(NSData *)assetUrl;
76
 
77
 
77
 // file stream write data
78
 // file stream write data
78
 - (void)write:(NSData *) chunk;
79
 - (void)write:(NSData *) chunk;
79
 - (void)writeEncodeChunk:(NSString *) chunk;
80
 - (void)writeEncodeChunk:(NSString *) chunk;
80
-- (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize;
81
 
81
 
82
 - (void) closeInStream;
82
 - (void) closeInStream;
83
 - (void) closeOutStream;
83
 - (void) closeOutStream;

+ 303
- 301
src/ios/RNFetchBlobFS.m Bestand weergeven

1
-//
1
+
2
 //  RNFetchBlobFS.m
2
 //  RNFetchBlobFS.m
3
 //  RNFetchBlob
3
 //  RNFetchBlob
4
 //
4
 //
7
 //
7
 //
8
 
8
 
9
 
9
 
10
-#import "RCTConvert.h"
11
-#import "RCTLog.h"
12
 #import <Foundation/Foundation.h>
10
 #import <Foundation/Foundation.h>
13
 #import "RCTBridge.h"
11
 #import "RCTBridge.h"
12
+#import "RNFetchBlob.h"
14
 #import "RCTEventDispatcher.h"
13
 #import "RCTEventDispatcher.h"
15
 #import "RNFetchBlobFS.h"
14
 #import "RNFetchBlobFS.h"
16
 #import "RNFetchBlobConst.h"
15
 #import "RNFetchBlobConst.h"
16
+#import "IOS7Polyfill.h"
17
 @import AssetsLibrary;
17
 @import AssetsLibrary;
18
 
18
 
19
 
19
 
40
 @synthesize appendData;
40
 @synthesize appendData;
41
 @synthesize bufferSize;
41
 @synthesize bufferSize;
42
 
42
 
43
+- (id)init {
44
+    self = [super init];
45
+    return self;
46
+}
47
+
48
+- (id)initWithCallback:(RCTResponseSenderBlock)callback {
49
+    self = [super init];
50
+    self.callback = callback;
51
+    return self;
52
+}
53
+
54
+- (id)initWithBridgeRef:(RCTBridge *)bridgeRef {
55
+    self = [super init];
56
+    self.bridge = bridgeRef;
57
+    return self;
58
+}
59
+
43
 // static member getter
60
 // static member getter
44
 + (NSArray *) getFileStreams {
61
 + (NSArray *) getFileStreams {
45
     
62
     
103
     return tempPath;
120
     return tempPath;
104
 }
121
 }
105
 
122
 
106
-#pragma mark - read asset stream
123
+#pragma margk - readStream 
107
 
124
 
108
-- (void) startAssetReadStream:(NSString *)assetUrl
125
++ (void) readStream:(NSString *)uri
126
+           encoding:(NSString * )encoding
127
+         bufferSize:(int)bufferSize
128
+               tick:(int)tick
129
+           streamId:(NSString *)streamId
130
+          bridgeRef:(RCTBridge *)bridgeRef
109
 {
131
 {
110
-    ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
111
-    {
112
-        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
113
-        dispatch_async(queue, ^ {
114
-            NSString * streamEventCode = [NSString stringWithFormat:@"RNFetchBlobStream+%@", self.path];
115
-            ALAssetRepresentation *rep = [myasset defaultRepresentation];
116
-            Byte *buffer = (Byte*)malloc(self.bufferSize);
117
-            NSUInteger cursor = [rep getBytes:buffer fromOffset:0 length:self.bufferSize error:nil];
118
-            while(cursor > 0)
132
+    [[self class] getPathFromUri:uri completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
133
+    
134
+        __block RCTEventDispatcher * event = bridgeRef.eventDispatcher;
135
+        __block int read = 0;
136
+        __block int backoff = tick *1000;
137
+        __block int chunkSize = bufferSize;
138
+        // allocate buffer in heap instead of stack
139
+        uint8_t * buffer;
140
+        @try
141
+        {
142
+            buffer = (uint8_t *) malloc(bufferSize);
143
+            if(path != nil)
119
             {
144
             {
120
-                cursor += [rep getBytes:buffer fromOffset:cursor length:self.bufferSize error:nil];
121
-                NSData * chunkData = [NSData dataWithBytes:buffer length:self.bufferSize];
122
-                NSString * encodedChunk = @"";
123
-                // emit data
124
-                if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
125
-                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
145
+                if([[NSFileManager defaultManager] fileExistsAtPath:path] == NO)
146
+                {
147
+                    NSString * message = [NSString stringWithFormat:@"File not exists at path %@", path];
148
+                    NSDictionary * payload = @{ @"event": FS_EVENT_ERROR, @"detail": message };
149
+                    [event sendDeviceEventWithName:streamId body:payload];
150
+                    free(buffer);
151
+                    return ;
126
                 }
152
                 }
127
-                // when encoding is ASCII, send byte array data
128
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"ascii"] ) {
129
-                    // RCTBridge only emits string data, so we have to create JSON byte array string
130
-                    NSMutableArray * asciiArray = [NSMutableArray array];
131
-                    unsigned char *bytePtr;
132
-                    if (chunkData.length > 0)
153
+                NSInputStream * stream = [[NSInputStream alloc] initWithFileAtPath:path];
154
+                [stream open];
155
+                while((read = [stream read:buffer maxLength:bufferSize]) > 0)
156
+                {
157
+                    [[self class] emitDataChunks:[NSData dataWithBytes:buffer length:read] encoding:encoding streamId:streamId event:event];
158
+                    if(tick > 0)
133
                     {
159
                     {
134
-                        bytePtr = (unsigned char *)[chunkData bytes];
135
-                        NSInteger byteLen = chunkData.length/sizeof(uint8_t);
136
-                        for (int i = 0; i < byteLen; i++)
137
-                        {
138
-                            [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
139
-                        }
160
+                        usleep(backoff);
140
                     }
161
                     }
141
-                    
142
-                    [self.bridge.eventDispatcher
143
-                     sendDeviceEventWithName:streamEventCode
144
-                     body: @{
145
-                             @"event": FS_EVENT_DATA,
146
-                             @"detail": asciiArray
147
-                             }
148
-                     ];
149
-                    return;
150
                 }
162
                 }
151
-                // convert byte array to base64 data chunks
152
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"base64"] ) {
153
-                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
154
-                }
155
-                // unknown encoding, send error event
156
-                else {
157
-                    [self.bridge.eventDispatcher
158
-                     sendDeviceEventWithName:streamEventCode
159
-                     body:@{
160
-                            @"event": FS_EVENT_ERROR,
161
-                            @"detail": @"unrecognized encoding"
162
-                            }
163
-                     ];
164
-                    return;
163
+                [stream close];
164
+            }
165
+            else if (asset != nil)
166
+            {
167
+                int cursor = 0;
168
+                NSError * err;
169
+                while((read = [asset getBytes:buffer fromOffset:cursor length:bufferSize error:&err]) > 0)
170
+                {
171
+                    cursor += read;
172
+                    [[self class] emitDataChunks:[NSData dataWithBytes:buffer length:read] encoding:encoding streamId:streamId event:event];
173
+                    if(tick > 0)
174
+                    {
175
+                        usleep(backoff);
176
+                    }
165
                 }
177
                 }
166
-                
167
-                [self.bridge.eventDispatcher
168
-                 sendDeviceEventWithName:streamEventCode
169
-                 body:@{
170
-                        @"event": FS_EVENT_DATA,
171
-                        @"detail": encodedChunk
172
-                        }
173
-                 ];
174
             }
178
             }
175
-            free(buffer);
176
-        });
179
+            else
180
+            {
181
+                NSDictionary * payload = @{ @"event": FS_EVENT_ERROR, @"detail": @"RNFetchBlob.readStream unable to resolve URI" };
182
+                [event sendDeviceEventWithName:streamId body:payload];
183
+            }
184
+            // release buffer
185
+            if(buffer != nil)
186
+                free(buffer);
187
+            
188
+        }
189
+        @catch (NSError * err)
190
+        {
191
+            NSDictionary * payload = @{ @"event": FS_EVENT_ERROR, @"detail": [NSString stringWithFormat:@"RNFetchBlob.readStream error %@", [err description]] };
192
+            [event sendDeviceEventWithName:streamId body:payload];
193
+        }
194
+        @finally
195
+        {
196
+            NSDictionary * payload = @{ @"event": FS_EVENT_END, @"detail": @"" };
197
+            [event sendDeviceEventWithName:streamId body:payload];
198
+        }
177
         
199
         
178
-    };
200
+    }];
179
     
201
     
180
-    ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *error)
202
+}
203
+
204
+// send read stream chunks via native event emitter
205
++ (void) emitDataChunks:(NSData *)data encoding:(NSString *) encoding streamId:(NSString *)streamId event:(RCTEventDispatcher *)event
206
+{
207
+    NSString * encodedChunk = @"";
208
+    if([[encoding lowercaseString] isEqualToString:@"utf8"])
181
     {
209
     {
182
-        
183
-    };
184
-    
185
-    if(assetUrl && [assetUrl length])
210
+        NSDictionary * payload = @{ @"event": FS_EVENT_DATA,  @"detail" : [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] };
211
+        [event sendDeviceEventWithName:streamId body:payload];
212
+    }
213
+    else if ([[encoding lowercaseString] isEqualToString:@"base64"])
186
     {
214
     {
187
-        NSURL *asseturl = [NSURL URLWithString:assetUrl];
188
-        ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
189
-        [assetslibrary assetForURL:asseturl
190
-                       resultBlock:resultblock
191
-                      failureBlock:failureblock];
215
+        NSDictionary * payload = @{ @"event": FS_EVENT_DATA,  @"detail" : [data base64EncodedStringWithOptions:0] };
216
+        [event sendDeviceEventWithName:streamId body:payload];
217
+    }
218
+    else if([[encoding lowercaseString] isEqualToString:@"ascii"])
219
+    {
220
+        // RCTBridge only emits string data, so we have to create JSON byte array string
221
+        NSMutableArray * asciiArray = [NSMutableArray array];
222
+        unsigned char *bytePtr;
223
+        if (data.length > 0)
224
+        {
225
+            bytePtr = (unsigned char *)[data bytes];
226
+            NSInteger byteLen = data.length/sizeof(uint8_t);
227
+            for (int i = 0; i < byteLen; i++)
228
+            {
229
+                [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
230
+            }
231
+        }
232
+
233
+        NSDictionary * payload = @{ @"event": FS_EVENT_DATA,  @"detail" : asciiArray };
234
+        [event sendDeviceEventWithName:streamId body:payload];
192
     }
235
     }
236
+    
237
+    
193
 }
238
 }
194
 
239
 
195
 # pragma write file from file
240
 # pragma write file from file
196
 
241
 
197
-+ (NSNumber *) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append
242
++ (NSNumber *) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append callback:(void(^)(NSString * errMsg, NSNumber *size))callback
198
 {
243
 {
199
-    NSInputStream * is = [[NSInputStream alloc] initWithFileAtPath:src];
200
-    NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:append];
201
-    [is open];
202
-    [os open];
203
-    uint8_t buffer[10240];
204
-    long written = 0;
205
-    int read = [is read:buffer maxLength:10240];
206
-    written += read;
207
-    while(read > 0) {
208
-        [os write:buffer maxLength:read];
209
-        read = [is read:buffer maxLength:10240];
210
-        written += read;
211
-    }
212
-    [os close];
213
-    [is close];
214
-    return [NSNumber numberWithLong:written];
244
+    [[self class] getPathFromUri:src completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
245
+        if(path != nil)
246
+        {
247
+            __block NSInputStream * is = [[NSInputStream alloc] initWithFileAtPath:path];
248
+            __block NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:append];
249
+            [is open];
250
+            [os open];
251
+            uint8_t buffer[10240];
252
+            __block long written = 0;
253
+            int read = [is read:buffer maxLength:10240];
254
+            written += read;
255
+            while(read > 0) {
256
+                [os write:buffer maxLength:read];
257
+                read = [is read:buffer maxLength:10240];
258
+                written += read;
259
+            }
260
+            [os close];
261
+            [is close];
262
+            __block NSNumber * size = [NSNumber numberWithLong:written];
263
+            callback(nil, size);
264
+        }
265
+        else if(asset != nil)
266
+        {
267
+            
268
+            __block NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:append];
269
+            int read = 0;
270
+            int cursor = 0;
271
+            __block long written = 0;
272
+            uint8_t buffer[10240];
273
+            [os open];
274
+            while((read = [asset getBytes:buffer fromOffset:cursor length:10240 error:nil]) > 0)
275
+            {
276
+                cursor += read;
277
+                [os write:buffer maxLength:read];
278
+            }
279
+            __block NSNumber * size = [NSNumber numberWithLong:written];
280
+            [os close];
281
+            callback(nil, size);
282
+        }
283
+        else
284
+            callback(@"failed to resolve path", nil);
285
+    }];
286
+    
287
+    return 0;
215
 }
288
 }
216
 
289
 
217
 # pragma mark - write file
290
 # pragma mark - write file
230
             [fm createFileAtPath:path contents:nil attributes:nil];
303
             [fm createFileAtPath:path contents:nil attributes:nil];
231
         }
304
         }
232
         if(err != nil) {
305
         if(err != nil) {
233
-            reject(@"RNFetchBlob writeFile Error", @"could not create file at path", path);
306
+            reject(@"RNFetchBlob writeFile Error", @"could not create file at path", nil);
234
             return;
307
             return;
235
         }
308
         }
236
         NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
309
         NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
237
         NSData * content = nil;
310
         NSData * content = nil;
238
-        if([encoding containsString:@"base64"]) {
311
+        if([encoding RNFBContainsString:@"base64"]) {
239
             content = [[NSData alloc] initWithBase64EncodedString:data options:0];
312
             content = [[NSData alloc] initWithBase64EncodedString:data options:0];
240
         }
313
         }
241
         else if([encoding isEqualToString:@"uri"]) {
314
         else if([encoding isEqualToString:@"uri"]) {
242
-            NSNumber* size = [[self class] writeFileFromFile:data toFile:path append:append];
243
-            resolve(size);
315
+            NSNumber* size = [[self class] writeFileFromFile:data toFile:path append:append callback:^(NSString *errMsg, NSNumber *size) {
316
+                if(errMsg != nil)
317
+                    reject(@"RNFetchBlob writeFile Error", errMsg, nil);
318
+                else
319
+                    resolve(size);
320
+            }];
244
             return;
321
             return;
245
         }
322
         }
246
         else {
323
         else {
337
             {
414
             {
338
                 BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path];
415
                 BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path];
339
                 if(!exists) {
416
                 if(!exists) {
340
-                    reject(@"RNFetchBlobFS readFile error", @"file not exists", path);
417
+                    reject(@"RNFetchBlobFS readFile error", @"file not exists", [[NSError alloc]init]);
341
                     return;
418
                     return;
342
                 }
419
                 }
343
                 fileContent = [NSData dataWithContentsOfFile:path];
420
                 fileContent = [NSData dataWithContentsOfFile:path];
392
 # pragma mark - stat
469
 # pragma mark - stat
393
 
470
 
394
 + (NSDictionary *) stat:(NSString *) path error:(NSError **) error {
471
 + (NSDictionary *) stat:(NSString *) path error:(NSError **) error {
395
-    NSMutableDictionary *stat = [[NSMutableDictionary alloc] init];
472
+
473
+    
396
     BOOL isDir = NO;
474
     BOOL isDir = NO;
397
     NSFileManager * fm = [NSFileManager defaultManager];
475
     NSFileManager * fm = [NSFileManager defaultManager];
398
     if([fm fileExistsAtPath:path isDirectory:&isDir] == NO) {
476
     if([fm fileExistsAtPath:path isDirectory:&isDir] == NO) {
409
              @"path" : path,
487
              @"path" : path,
410
              @"lastModified" : [NSString stringWithFormat:@"%d", [lastModified timeIntervalSince1970]],
488
              @"lastModified" : [NSString stringWithFormat:@"%d", [lastModified timeIntervalSince1970]],
411
              @"type" : isDir ? @"directory" : @"file"
489
              @"type" : isDir ? @"directory" : @"file"
412
-             };
490
+            };
491
+    
413
 }
492
 }
414
 
493
 
415
 # pragma mark - exists
494
 # pragma mark - exists
416
 
495
 
417
-+ (BOOL) exists:(NSString *) path {
418
-    return [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:NULL];
419
-}
420
-
421
-- (id)init {
422
-    self = [super init];
423
-    return self;
424
-}
425
-
426
-- (id)initWithCallback:(RCTResponseSenderBlock)callback {
427
-    self = [super init];
428
-    self.callback = callback;
429
-    return self;
430
-}
431
-
432
-- (id)initWithBridgeRef:(RCTBridge *)bridgeRef {
433
-    self = [super init];
434
-    self.bridge = bridgeRef;
435
-    return self;
496
++ (void) exists:(NSString *) path callback:(RCTResponseSenderBlock)callback
497
+{
498
+    [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
499
+        if(path != nil)
500
+        {
501
+            BOOL isDir = NO;
502
+            BOOL exists = NO;
503
+            exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory: &isDir];
504
+            callback(@[@(exists), @(isDir)]);
505
+        }
506
+        else if(asset != nil)
507
+        {
508
+            callback(@[@YES, @NO]);
509
+        }
510
+        else
511
+        {
512
+            callback(@[@NO, @NO]);
513
+        }
514
+    }];
436
 }
515
 }
437
 
516
 
438
 # pragma mark - open file stream
517
 # pragma mark - open file stream
498
     
577
     
499
 }
578
 }
500
 
579
 
501
-- (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize {
502
-    
503
-    self.inStream = [[NSInputStream alloc] initWithFileAtPath:path];
504
-    self.inStream.delegate = self;
505
-    self.encoding = encoding;
506
-    self.path = path;
507
-    self.bufferSize = bufferSize;
508
-    
509
-    if([path hasPrefix:AL_PREFIX])
510
-    {
511
-        [self startAssetReadStream:path];
512
-        return;
513
-    }
514
-    
515
-    // normalize file path
516
-    path = [[self class] getPathOfAsset:path];
517
-    
518
-    // NSStream needs a runloop so let's create a run loop for it
519
-    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
520
-    // start NSStream is a runloop
521
-    dispatch_async(queue, ^ {
522
-        [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
523
-                            forMode:NSDefaultRunLoopMode];
524
-        [inStream open];
525
-        [[NSRunLoop currentRunLoop] run];
526
-        
527
-    });
528
-}
529
-
530
 // Slice a file into another file, generally for support Blob implementation.
580
 // Slice a file into another file, generally for support Blob implementation.
531
 + (void)slice:(NSString *)path
581
 + (void)slice:(NSString *)path
532
          dest:(NSString *)dest
582
          dest:(NSString *)dest
536
      resolver:(RCTPromiseResolveBlock)resolve
586
      resolver:(RCTPromiseResolveBlock)resolve
537
      rejecter:(RCTPromiseRejectBlock)reject
587
      rejecter:(RCTPromiseRejectBlock)reject
538
 {
588
 {
539
-    long expected = [end longValue] - [start longValue];
540
-    long read = 0;
541
-    NSFileHandle * handle = [NSFileHandle fileHandleForReadingAtPath:path];
542
-    NSFileManager * fm = [NSFileManager defaultManager];
543
-    NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
544
-    [os open];
545
-    // abort for the source file not exists
546
-    if([fm fileExistsAtPath:path] == NO)
547
-    {
548
-        reject(@"RNFetchBlob slice failed : the file does not exists", path, nil);
549
-        return;
550
-    }
551
-    long size = [fm attributesOfItemAtPath:path error:nil].fileSize;
552
-    long max = MIN(size, [end longValue]);
553
-    
554
-    if(![fm fileExistsAtPath:dest]) {
555
-        [fm createFileAtPath:dest contents:@"" attributes:nil];
556
-    }
557
-    [handle seekToFileOffset:[start longValue]];
558
-    while(read < expected)
589
+    [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset)
559
     {
590
     {
560
-        
561
-        NSData * chunk;
562
-        long chunkSize = 0;
563
-        if([start longValue] + read + 10240 > max)
591
+        if(path != nil)
592
+        {
593
+            long expected = [end longValue] - [start longValue];
594
+            long read = 0;
595
+            NSFileHandle * handle = [NSFileHandle fileHandleForReadingAtPath:path];
596
+            NSFileManager * fm = [NSFileManager defaultManager];
597
+            NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
598
+            [os open];
599
+            // abort for the source file not exists
600
+            if([fm fileExistsAtPath:path] == NO)
601
+            {
602
+                reject(@"RNFetchBlob slice failed : the file does not exists", path, nil);
603
+                return;
604
+            }
605
+            long size = [fm attributesOfItemAtPath:path error:nil].fileSize;
606
+            long max = MIN(size, [end longValue]);
607
+            
608
+            if(![fm fileExistsAtPath:dest]) {
609
+                [fm createFileAtPath:dest contents:@"" attributes:nil];
610
+            }
611
+            [handle seekToFileOffset:[start longValue]];
612
+            while(read < expected)
613
+            {
614
+                
615
+                NSData * chunk;
616
+                long chunkSize = 0;
617
+                if([start longValue] + read + 10240 > max)
618
+                {
619
+                    NSLog(@"read chunk %lu", max - read - [start longValue]);
620
+                    chunkSize = max - read - [start longValue];
621
+                    chunk = [handle readDataOfLength:chunkSize];
622
+                }
623
+                else
624
+                {
625
+                    NSLog(@"read chunk %lu", 10240);
626
+                    chunkSize = 10240;
627
+                    chunk = [handle readDataOfLength:10240];
628
+                }
629
+                if([chunk length] <= 0)
630
+                    break;
631
+                long remain = expected - read;
632
+                
633
+                [os write:[chunk bytes] maxLength:chunkSize];
634
+                read += [chunk length];
635
+            }
636
+            [handle closeFile];
637
+            [os close];
638
+            resolve(dest);
639
+        }
640
+        else if (asset != nil)
564
         {
641
         {
565
-            NSLog(@"read chunk %lu", max - read - [start longValue]);
566
-            chunkSize = max - read - [start longValue];
567
-            chunk = [handle readDataOfLength:chunkSize];
642
+            long expected = [end longValue] - [start longValue];
643
+            long read = 0;
644
+            long chunkRead = 0;
645
+            NSOutputStream * os = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
646
+            [os open];
647
+            long size = asset.size;
648
+            long max = MIN(size, [end longValue]);
649
+            
650
+            while(read < expected)
651
+            {
652
+                
653
+                uint8_t * chunk[10240];
654
+                long chunkSize = 0;
655
+                if([start longValue] + read + 10240 > max)
656
+                {
657
+                    NSLog(@"read chunk %lu", max - read - [start longValue]);
658
+                    chunkSize = max - read - [start longValue];
659
+                    chunkRead = [asset getBytes:chunk fromOffset:[start longValue] + read length:chunkSize error:nil];
660
+                }
661
+                else
662
+                {
663
+                    NSLog(@"read chunk %lu", 10240);
664
+                    chunkSize = 10240;
665
+                    chunkRead = [asset getBytes:chunk fromOffset:[start longValue] + read length:chunkSize error:nil];
666
+                }
667
+                if( chunkRead <= 0)
668
+                    break;
669
+                long remain = expected - read;
670
+                
671
+                [os write:chunk maxLength:chunkSize];
672
+                read += chunkRead;
673
+            }
674
+            [os close];
675
+            resolve(dest);
568
         }
676
         }
569
         else
677
         else
570
         {
678
         {
571
-            NSLog(@"read chunk %lu", 10240);
572
-            chunkSize = 10240;
573
-            chunk = [handle readDataOfLength:10240];
679
+            reject(@"slice error",  [NSString stringWithFormat: @"could not resolve URI %@", path ], nil);
574
         }
680
         }
575
-        if([chunk length] <= 0)
576
-            break;
577
-        long remain = expected - read;
578
-    
579
-        [os write:[chunk bytes] maxLength:chunkSize];
580
-        read += [chunk length];
581
-    }
582
-    [handle closeFile];
583
-    [os close];
584
-    resolve(dest);
585
-    
681
+        
682
+    }];
586
 }
683
 }
587
 
684
 
588
 // close file read stream
685
 // close file read stream
597
     
694
     
598
 }
695
 }
599
 
696
 
600
-#pragma mark RNFetchBlobFS read stream delegate
601
-
602
-- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
603
-    
604
-    NSString * streamEventCode = [NSString stringWithFormat:@"RNFetchBlobStream+%@", self.path];
605
-    
606
-    switch(eventCode) {
607
-            
608
-            // write stream event
609
-        case NSStreamEventHasSpaceAvailable:
610
-        {
611
-            
612
-            
613
-        }
614
-            
615
-            // read stream incoming chunk
616
-        case NSStreamEventHasBytesAvailable:
617
-        {
618
-            NSMutableData * chunkData = [[NSMutableData alloc] init];
619
-            NSInteger chunkSize = 4096;
620
-            if([[self.encoding lowercaseString] isEqualToString:@"base64"])
621
-                chunkSize = 4095;
622
-            if(self.bufferSize > 0)
623
-                chunkSize = self.bufferSize;
624
-            uint8_t buf[chunkSize];
625
-            unsigned int len = 0;
626
-            len = [(NSInputStream *)stream read:buf maxLength:chunkSize];
627
-            // still have data in stream
628
-            if(len) {
629
-                [chunkData appendBytes:buf length:len];
630
-                // dispatch data event
631
-                NSString * encodedChunk = [NSString alloc];
632
-                if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
633
-                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
634
-                }
635
-                // when encoding is ASCII, send byte array data
636
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"ascii"] ) {
637
-                    // RCTBridge only emits string data, so we have to create JSON byte array string
638
-                    NSMutableArray * asciiArray = [NSMutableArray array];
639
-                    unsigned char *bytePtr;
640
-                    if (chunkData.length > 0)
641
-                    {
642
-                        bytePtr = (unsigned char *)[chunkData bytes];
643
-                        NSInteger byteLen = chunkData.length/sizeof(uint8_t);
644
-                        for (int i = 0; i < byteLen; i++)
645
-                        {
646
-                            [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
647
-                        }
648
-                    }
649
-                    
650
-                    [self.bridge.eventDispatcher
651
-                     sendDeviceEventWithName:streamEventCode
652
-                     body: @{
653
-                             @"event": FS_EVENT_DATA,
654
-                             @"detail": asciiArray
655
-                             }
656
-                     ];
657
-                    return;
658
-                }
659
-                // convert byte array to base64 data chunks
660
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"base64"] ) {
661
-                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
662
-                }
663
-                // unknown encoding, send error event
664
-                else {
665
-                    [self.bridge.eventDispatcher
666
-                     sendDeviceEventWithName:streamEventCode
667
-                     body:@{
668
-                            @"event": FS_EVENT_ERROR,
669
-                            @"detail": @"unrecognized encoding"
670
-                            }
671
-                     ];
672
-                    return;
673
-                }
674
-                
675
-                [self.bridge.eventDispatcher
676
-                 sendDeviceEventWithName:streamEventCode
677
-                 body:@{
678
-                        @"event": FS_EVENT_DATA,
679
-                        @"detail": encodedChunk
680
-                        }
681
-                 ];
682
-            }
683
-            // end of stream
684
-            else {
685
-                [self.bridge.eventDispatcher
686
-                 sendDeviceEventWithName:streamEventCode
687
-                 body:@{
688
-                        @"event": FS_EVENT_END,
689
-                        @"detail": @""
690
-                        }
691
-                 ];
692
-            }
693
-            break;
694
-        }
695
-            
696
-            // stream error
697
-        case NSStreamEventErrorOccurred:
698
-        {
699
-            [self.bridge.eventDispatcher
700
-             sendDeviceEventWithName:streamEventCode
701
-             body:@{
702
-                    @"event": FS_EVENT_ERROR,
703
-                    @"detail": @"RNFetchBlob error when read file with stream, file may not exists"
704
-                    }
705
-             ];
706
-            break;
707
-        }
708
-            
709
-    }
710
-    
711
-}
712
 
697
 
713
 # pragma mark - get absolute path of resource
698
 # pragma mark - get absolute path of resource
714
 
699
 
733
     }
718
     }
734
 }
719
 }
735
 
720
 
721
++ (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest
722
+{
723
+    int read = 0;
724
+    int cursor = 0;
725
+    Byte * buffer = (Byte *)malloc(10240);
726
+    NSOutputStream * ostream = [[NSOutputStream alloc] initToFileAtPath:dest append:NO];
727
+    [ostream open];
728
+    while((read = [rep getBytes:buffer fromOffset:cursor length:10240 error:nil]) > 0)
729
+    {
730
+        cursor+=10240;
731
+        [ostream write:buffer maxLength:read];
732
+    }
733
+    [ostream close];
734
+    free(buffer);
735
+    return;
736
+}
737
+
736
 @end
738
 @end

+ 15
- 8
src/ios/RNFetchBlobNetwork.m Bestand weergeven

6
 //  Copyright © 2016 wkh237. All rights reserved.
6
 //  Copyright © 2016 wkh237. All rights reserved.
7
 //
7
 //
8
 
8
 
9
-#import "RCTConvert.h"
10
 #import "RCTLog.h"
9
 #import "RCTLog.h"
11
 #import <Foundation/Foundation.h>
10
 #import <Foundation/Foundation.h>
12
 #import "RCTBridge.h"
11
 #import "RCTBridge.h"
15
 #import "RNFetchBlobNetwork.h"
14
 #import "RNFetchBlobNetwork.h"
16
 #import "RNFetchBlobConst.h"
15
 #import "RNFetchBlobConst.h"
17
 #import "RNFetchBlobReqBuilder.h"
16
 #import "RNFetchBlobReqBuilder.h"
17
+#import "IOS7Polyfill.h"
18
 #import <CommonCrypto/CommonDigest.h>
18
 #import <CommonCrypto/CommonDigest.h>
19
 
19
 
20
 ////////////////////////////////////////
20
 ////////////////////////////////////////
36
     long bodyLength;
36
     long bodyLength;
37
     NSMutableDictionary * respInfo;
37
     NSMutableDictionary * respInfo;
38
     NSInteger respStatus;
38
     NSInteger respStatus;
39
-    BOOL isInrement;
39
+    NSMutableArray * redirects;
40
 }
40
 }
41
 
41
 
42
 @end
42
 @end
126
     self.expectedBytes = 0;
126
     self.expectedBytes = 0;
127
     self.receivedBytes = 0;
127
     self.receivedBytes = 0;
128
     self.options = options;
128
     self.options = options;
129
-    isInrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
129
+    redirects = [[NSMutableArray alloc] init];
130
+    [redirects addObject:req.URL.absoluteString];
130
 
131
 
131
     NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
132
     NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
132
     NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
133
     NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
207
         if(respCType != nil)
208
         if(respCType != nil)
208
         {
209
         {
209
             NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
210
             NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
210
-            if([respCType containsString:@"text/"])
211
+            if([respCType RNFBContainsString:@"text/"])
211
             {
212
             {
212
                 respType = @"text";
213
                 respType = @"text";
213
             }
214
             }
214
-            else if([respCType containsString:@"application/json"])
215
+            else if([respCType RNFBContainsString:@"application/json"])
215
             {
216
             {
216
                 respType = @"json";
217
                 respType = @"json";
217
             }
218
             }
219
             else if( extraBlobCTypes !=  nil) {
220
             else if( extraBlobCTypes !=  nil) {
220
                 for(NSString * substr in extraBlobCTypes)
221
                 for(NSString * substr in extraBlobCTypes)
221
                 {
222
                 {
222
-                    if([respCType containsString:[substr lowercaseString]])
223
+                    if([respCType RNFBContainsString:[substr lowercaseString]])
223
                     {
224
                     {
224
                         respType = @"blob";
225
                         respType = @"blob";
225
                         respFile = YES;
226
                         respFile = YES;
244
                      @"taskId": taskId,
245
                      @"taskId": taskId,
245
                      @"state": @"2",
246
                      @"state": @"2",
246
                      @"headers": headers,
247
                      @"headers": headers,
248
+                     @"redirects": redirects,
247
                      @"respType" : respType,
249
                      @"respType" : respType,
248
                      @"timeout" : @NO,
250
                      @"timeout" : @NO,
249
                      @"status": [NSString stringWithFormat:@"%d", statusCode ]
251
                      @"status": [NSString stringWithFormat:@"%d", statusCode ]
287
     NSNumber * received = [NSNumber numberWithLong:[data length]];
289
     NSNumber * received = [NSNumber numberWithLong:[data length]];
288
     receivedBytes += [received longValue];
290
     receivedBytes += [received longValue];
289
     NSString * chunkString = @"";
291
     NSString * chunkString = @"";
290
-    
292
+
291
     if(isInrement == YES)
293
     if(isInrement == YES)
292
     {
294
     {
293
         chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
295
         chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
294
     }
296
     }
295
-    
297
+
296
     if(respFile == NO)
298
     if(respFile == NO)
297
     {
299
     {
298
         [respData appendData:data];
300
         [respData appendData:data];
450
     }
452
     }
451
 }
453
 }
452
 
454
 
455
+- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
456
+{
457
+    [redirects addObject:[request.URL absoluteString]];
458
+    completionHandler(request);
459
+}
453
 
460
 
454
 @end
461
 @end

+ 28
- 9
src/ios/RNFetchBlobReqBuilder.m Bestand weergeven

12
 #import "RNFetchBlobConst.h"
12
 #import "RNFetchBlobConst.h"
13
 #import "RNFetchBlobFS.h"
13
 #import "RNFetchBlobFS.h"
14
 #import "RCTLog.h"
14
 #import "RCTLog.h"
15
+#import "IOS7Polyfill.h"
15
 
16
 
16
 @interface RNFetchBlobReqBuilder()
17
 @interface RNFetchBlobReqBuilder()
17
 {
18
 {
36
     
37
     
37
     // send request
38
     // send request
38
     __block NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString: encodedUrl]];
39
     __block NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString: encodedUrl]];
39
-    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[RNFetchBlobNetwork normalizeHeaders:headers]];
40
+    __block NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[RNFetchBlobNetwork normalizeHeaders:headers]];
40
     NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
41
     NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
41
     NSNumber * timeStampObj = [NSNumber numberWithDouble: timeStamp];
42
     NSNumber * timeStampObj = [NSNumber numberWithDouble: timeStamp];
42
 
43
 
43
     // generate boundary
44
     // generate boundary
44
-    NSString * boundary = [NSString stringWithFormat:@"RNFetchBlob%d", timeStampObj];
45
+    __block NSString * boundary = [NSString stringWithFormat:@"RNFetchBlob%d", timeStampObj];
45
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
46
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
46
         __block NSMutableData * postData = [[NSMutableData alloc] init];
47
         __block NSMutableData * postData = [[NSMutableData alloc] init];
47
         // combine multipart/form-data body
48
         // combine multipart/form-data body
90
             // generate octet-stream body
91
             // generate octet-stream body
91
             if(body != nil) {
92
             if(body != nil) {
92
                 __block NSString * cType = [[self class] getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
93
                 __block NSString * cType = [[self class] getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
94
+                __block NSString * transferEncoding = [[self class] getHeaderIgnoreCases:@"transfer-encoding" fromHeaders:mheaders];
93
                 // when headers does not contain a key named "content-type" (case ignored), use default content type
95
                 // when headers does not contain a key named "content-type" (case ignored), use default content type
94
                 if(cType == nil)
96
                 if(cType == nil)
95
                 {
97
                 {
111
                         return;
113
                         return;
112
                     }
114
                     }
113
                     size = [[[NSFileManager defaultManager] attributesOfItemAtPath:orgPath error:nil] fileSize];
115
                     size = [[[NSFileManager defaultManager] attributesOfItemAtPath:orgPath error:nil] fileSize];
114
-                    [request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
116
+                    if(transferEncoding != nil && [[transferEncoding lowercaseString] isEqualToString:@"chunked"])
117
+                    {
118
+                        [request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
119
+                    }
120
+                    else
121
+                    {
122
+                        [request setHTTPBody:[NSData dataWithContentsOfFile:orgPath ]];
123
+                    }
115
                 }
124
                 }
116
                 // otherwise convert it as BASE64 data string
125
                 // otherwise convert it as BASE64 data string
117
                 else {
126
                 else {
118
                     
127
                     
119
                     __block NSString * cType = [[self class]getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
128
                     __block NSString * cType = [[self class]getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
120
                     // when content-type is application/octet* decode body string using BASE64 decoder
129
                     // when content-type is application/octet* decode body string using BASE64 decoder
121
-                    if([[cType lowercaseString] hasPrefix:@"application/octet"] || [[cType lowercaseString] containsString:@";base64"])
130
+                    if([[cType lowercaseString] hasPrefix:@"application/octet"] || [[cType lowercaseString] RNFBContainsString:@";base64"])
122
                     {
131
                     {
123
                         __block NSString * ncType = [[cType stringByReplacingOccurrencesOfString:@";base64" withString:@""]stringByReplacingOccurrencesOfString:@";BASE64" withString:@""];
132
                         __block NSString * ncType = [[cType stringByReplacingOccurrencesOfString:@";base64" withString:@""]stringByReplacingOccurrencesOfString:@";BASE64" withString:@""];
124
                         if([mheaders valueForKey:@"content-type"] != nil)
133
                         if([mheaders valueForKey:@"content-type"] != nil)
148
 
157
 
149
 +(void) buildFormBody:(NSArray *)form boundary:(NSString *)boundary onComplete:(void(^)(NSData * formData))onComplete
158
 +(void) buildFormBody:(NSArray *)form boundary:(NSString *)boundary onComplete:(void(^)(NSData * formData))onComplete
150
 {
159
 {
151
-    NSMutableData * formData = [[NSMutableData alloc] init];
160
+    __block NSMutableData * formData = [[NSMutableData alloc] init];
152
     if(form == nil)
161
     if(form == nil)
153
         onComplete(nil);
162
         onComplete(nil);
154
     else
163
     else
159
         void __block (^getFieldData)(id field) = ^(id field)
168
         void __block (^getFieldData)(id field) = ^(id field)
160
         {
169
         {
161
             NSString * name = [field valueForKey:@"name"];
170
             NSString * name = [field valueForKey:@"name"];
162
-            NSString * content = [field valueForKey:@"data"];
171
+            __block NSString * content = [field valueForKey:@"data"];
163
             NSString * contentType = [field valueForKey:@"type"];
172
             NSString * contentType = [field valueForKey:@"type"];
164
             // skip when the form field `name` or `data` is empty
173
             // skip when the form field `name` or `data` is empty
165
             if(content == nil || name == nil)
174
             if(content == nil || name == nil)
197
                             i++;
206
                             i++;
198
                             if(i < count)
207
                             if(i < count)
199
                             {
208
                             {
200
-                                getFieldData([form objectAtIndex:i]);
209
+                                __block NSDictionary * nextField = [form objectAtIndex:i];
210
+                                getFieldData(nextField);
201
                             }
211
                             }
202
                             else
212
                             else
213
+                            {
203
                                 onComplete(formData);
214
                                 onComplete(formData);
215
+                                getFieldData = nil;
216
+                            }
204
                         }];
217
                         }];
205
                         return ;
218
                         return ;
206
                     }
219
                     }
213
                 [formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
226
                 [formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
214
                 [formData appendData:blobData];
227
                 [formData appendData:blobData];
215
                 [formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
228
                 [formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
229
+                blobData = nil;
216
             }
230
             }
217
             i++;
231
             i++;
218
             if(i < count)
232
             if(i < count)
219
             {
233
             {
220
-                getFieldData([form objectAtIndex:i]);
234
+                __block NSDictionary * nextField = [form objectAtIndex:i];
235
+                getFieldData(nextField);
221
             }
236
             }
222
             else
237
             else
238
+            {
223
                 onComplete(formData);
239
                 onComplete(formData);
240
+                getFieldData = nil;
241
+            }
224
 
242
 
225
         };
243
         };
226
-        getFieldData([form objectAtIndex:i]);
244
+        __block NSDictionary * nextField = [form objectAtIndex:i];
245
+        getFieldData(nextField);
227
     }
246
     }
228
 }
247
 }
229
 
248
 

+ 3
- 2
src/package.json Bestand weergeven

1
 {
1
 {
2
   "name": "react-native-fetch-blob",
2
   "name": "react-native-fetch-blob",
3
-  "version": "0.9.4-beta.1",
3
+  "version": "0.9.4",
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": {
7
     "test": "echo \"Error: no test specified\" && exit 1"
7
     "test": "echo \"Error: no test specified\" && exit 1"
8
   },
8
   },
9
   "dependencies": {
9
   "dependencies": {
10
-    "base-64": "0.1.0"
10
+    "base-64": "0.1.0",
11
+    "glob": "^7.0.6"
11
   },
12
   },
12
   "keywords": [
13
   "keywords": [
13
     "react-native",
14
     "react-native",

+ 22
- 6
src/polyfill/Blob.js Bestand weergeven

50
     return this._ref
50
     return this._ref
51
   }
51
   }
52
 
52
 
53
+  static setLog(level:number) {
54
+    if(number === -1)
55
+      log.disable()
56
+    else
57
+      log.level(level)
58
+  }
59
+
53
   /**
60
   /**
54
    * RNFetchBlob Blob polyfill, create a Blob directly from file path, BASE64
61
    * RNFetchBlob Blob polyfill, create a Blob directly from file path, BASE64
55
    * encoded data, and string. The conversion is done implicitly according to
62
    * encoded data, and string. The conversion is done implicitly according to
58
    * @param  {any} data Content of Blob object
65
    * @param  {any} data Content of Blob object
59
    * @param  {any} mime Content type settings of Blob object, `text/plain`
66
    * @param  {any} mime Content type settings of Blob object, `text/plain`
60
    *                    by default
67
    *                    by default
68
+   * @param  {boolean} defer When this argument set to `true`, blob constructor
69
+   *                         will not invoke blob created event automatically.
61
    */
70
    */
62
-  constructor(data:any, cType:any) {
71
+  constructor(data:any, cType:any, defer:boolean) {
63
     super()
72
     super()
64
     cType = cType || {}
73
     cType = cType || {}
65
     this.cacheName = getBlobName()
74
     this.cacheName = getBlobName()
75
       let size = 0
84
       let size = 0
76
       this._ref = String(data.getRNFetchBlobRef())
85
       this._ref = String(data.getRNFetchBlobRef())
77
       let orgPath = this._ref
86
       let orgPath = this._ref
87
+
78
       p = fs.exists(orgPath)
88
       p = fs.exists(orgPath)
79
             .then((exist) =>  {
89
             .then((exist) =>  {
80
               if(exist)
90
               if(exist)
121
       log.verbose('create Blob cache file from file path', data)
131
       log.verbose('create Blob cache file from file path', data)
122
       this._ref = String(data).replace('RNFetchBlob-file://', '')
132
       this._ref = String(data).replace('RNFetchBlob-file://', '')
123
       let orgPath = this._ref
133
       let orgPath = this._ref
124
-      p = fs.stat(orgPath)
125
-            .then((stat) =>  {
134
+      if(defer)
135
+        return
136
+      else {
137
+        p = fs.stat(orgPath)
138
+              .then((stat) =>  {
126
                 return Promise.resolve(stat.size)
139
                 return Promise.resolve(stat.size)
127
-            })
140
+              })
141
+      }
128
     }
142
     }
129
     // content from variable need create file
143
     // content from variable need create file
130
     else if(typeof data === 'string') {
144
     else if(typeof data === 'string') {
217
     let resPath = blobCacheDir + getBlobName()
231
     let resPath = blobCacheDir + getBlobName()
218
     let pass = false
232
     let pass = false
219
     log.debug('fs.slice new blob will at', resPath)
233
     log.debug('fs.slice new blob will at', resPath)
220
-    let result = new Blob(RNFetchBlob.wrap(resPath), { type : contentType })
234
+    let result = new Blob(RNFetchBlob.wrap(resPath), { type : contentType }, true)
221
     fs.slice(this._ref, resPath, start, end).then((dest) => {
235
     fs.slice(this._ref, resPath, start, end).then((dest) => {
222
       log.debug('fs.slice done', dest)
236
       log.debug('fs.slice done', dest)
223
       result._invokeOnCreateEvent()
237
       result._invokeOnCreateEvent()
252
     if(this._closed)
266
     if(this._closed)
253
       return Promise.reject('Blob has been released.')
267
       return Promise.reject('Blob has been released.')
254
     this._closed = true
268
     this._closed = true
255
-    return fs.unlink(this._ref)
269
+    return fs.unlink(this._ref).catch((err) => {
270
+      console.warn(err)
271
+    })
256
   }
272
   }
257
 
273
 
258
   _invokeOnCreateEvent() {
274
   _invokeOnCreateEvent() {

+ 1
- 1
src/polyfill/File.js Bestand weergeven

9
 
9
 
10
   name : string = '';
10
   name : string = '';
11
 
11
 
12
-  static build(name:string, data:any, cType):Promise<File> {
12
+  static build(name:string, data:any, cType:string):Promise<File> {
13
     return new Promise((resolve, reject) => {
13
     return new Promise((resolve, reject) => {
14
       new File(data, cType).onCreated((f) => {
14
       new File(data, cType).onCreated((f) => {
15
         f.name = name
15
         f.name = name

+ 7
- 0
src/polyfill/XMLHttpRequest.js Bestand weergeven

86
     return DONE
86
     return DONE
87
   }
87
   }
88
 
88
 
89
+  static setLog(level:number) {
90
+    if(number === -1)
91
+      log.disable()
92
+    else
93
+      log.level(level)
94
+  }
95
+
89
   static addBinaryContentType(substr:string) {
96
   static addBinaryContentType(substr:string) {
90
     for(let i in XMLHttpRequest.binaryContentTypes) {
97
     for(let i in XMLHttpRequest.binaryContentTypes) {
91
       if(new RegExp(substr,'i').test(XMLHttpRequest.binaryContentTypes[i])) {
98
       if(new RegExp(substr,'i').test(XMLHttpRequest.binaryContentTypes[i])) {

+ 13
- 0
src/react-native-fetch-blob.podspec Bestand weergeven

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

+ 57
- 62
src/scripts/prelink.js Bestand weergeven

1
-var fs = require('fs');
1
+try {
2
+  var fs = require('fs');
3
+  var glob = require('glob');
4
+  var addAndroidPermissions = process.env.RNFB_ANDROID_PERMISSIONS == 'true';
5
+  var MANIFEST_PATH = glob.sync(process.cwd() + '/android/app/src/main/**/AndroidManifest.xml')[0];
6
+  var PACKAGE_JSON = process.cwd() + '/package.json';
7
+  var package = JSON.parse(fs.readFileSync(PACKAGE_JSON));
8
+  var APP_NAME = package.name;
9
+  var PACKAGE_GRADLE = process.cwd() + '/node_modules/react-native-fetch-blob/android/build.gradle'
10
+  var VERSION = checkVersion();
2
 
11
 
3
-var MANIFEST_PATH = process.cwd() + '/android/app/src/main/AndroidManifest.xml';
4
-var PACKAGE_JSON = process.cwd() + '/package.json';
12
+  console.log('RNFetchBlob detected app version => ' + VERSION);
5
 
13
 
6
-var hasNecessaryFile = fs.existsSync(MANIFEST_PATH) && fs.existsSync(PACKAGE_JSON);
7
-
8
-if (!hasNecessaryFile) {
9
-  throw 'RNFetchBlob could not link Android automatically, some files could not be found.'
10
-}
11
-
12
-var package = JSON.parse(fs.readFileSync(PACKAGE_JSON));
13
-var APP_NAME = package.name;
14
-var APPLICATION_MAIN = process.cwd() + '/android/app/src/main/java/com/' + APP_NAME.toLocaleLowerCase() + '/MainApplication.java';
15
-var PACKAGE_GRADLE = process.cwd() + '/node_modules/react-native-fetch-blob/android/build.gradle'
16
-
17
-var VERSION = checkVersion();
18
-console.log('RNFetchBlob detected app version .. ' + VERSION);
19
-
20
-if(VERSION >= 0.29) {
21
-  console.log('RNFetchBlob patching MainApplication.java .. ');
22
-  if(!fs.existsSync(APPLICATION_MAIN)) {
23
-    throw 'RNFetchBlob could not link Android automatically, MainApplication.java not found in path : ' + APPLICATION_MAIN
24
-  }
25
-  var main = fs.readFileSync(APPLICATION_MAIN);
26
-  if(String(main).match('new RNFetchBlobPackage()') !== null) {
27
-    console.log('skipped');
28
-    return
14
+  if(VERSION < 0.28) {
15
+    console.log('You project version is '+ VERSION + ' which may not compatible to react-native-fetch-blob 7.0+, please consider upgrade your application template to react-native 0.27+.')
16
+    // add OkHttp3 dependency fo pre 0.28 project
17
+    var main = fs.readFileSync(PACKAGE_GRADLE);
18
+    console.log('adding OkHttp3 dependency to pre 0.28 project .. ')
19
+    main = String(main).replace('//{RNFetchBlob_PRE_0.28_DEPDENDENCY}', "compile 'com.squareup.okhttp3:okhttp:3.4.1'");
20
+    fs.writeFileSync(PACKAGE_GRADLE, main);
21
+    console.log('adding OkHttp3 dependency to pre 0.28 project .. ok')
29
   }
22
   }
30
-  main = String(main).replace('new MainReactPackage()', 'new RNFetchBlobPackage(),\n           new MainReactPackage()');
31
-  main = String(main).replace('import com.facebook.react.ReactApplication;', 'import com.facebook.react.ReactApplication;\nimport com.RNFetchBlob.RNFetchBlobPackage;')
32
 
23
 
33
-  fs.writeFileSync(APPLICATION_MAIN, main);
34
-  console.log('RNFetchBlob patching MainApplication.java .. ok')
24
+  console.log('Add Android permissions => ' + (addAndroidPermissions == "true"))
35
 
25
 
36
-}
26
+  if(addAndroidPermissions) {
37
 
27
 
38
-if(VERSION < 0.28) {
39
-  console.log('You project version is '+ VERSION + 'which does not meet requirement of react-native-fetch-blob 7.0+, please upgrade your application template to react-native 0.27+, otherwise Android application will not working.')
40
-  // add OkHttp3 dependency fo 0.28- project
41
-  var main = fs.readFileSync(PACKAGE_GRADLE);
42
-  console.log('adding OkHttp3 dependency to pre 0.28 project .. ')
43
-  main = String(main).replace('//{RNFetchBlob_PRE_0.28_DEPDENDENCY}', "compile 'com.squareup.okhttp3:okhttp:3.4.1'");
44
-  fs.writeFileSync(PACKAGE_GRADLE, main);
45
-  console.log('adding OkHttp3 dependency to pre 0.28 project .. ok')
46
-}
28
+    // set file access permission for Android < 6.0
29
+    fs.readFile(MANIFEST_PATH, function(err, data) {
47
 
30
 
48
-// set file access permission for Android < 6.0
49
-fs.readFile(MANIFEST_PATH, function(err, data) {
31
+      if(err)
32
+        console.log('failed to locate AndroidManifest.xml file, you may have to add file access permission manually.');
33
+      else {
50
 
34
 
51
-  if(err)
52
-    console.log('failed to locate AndroidManifest.xml file, you may have to add file access permission manually.');
53
-  else {
35
+        console.log('RNFetchBlob patching AndroidManifest.xml .. ');
36
+        // append fs permission
37
+        data = String(data).replace(
38
+          '<uses-permission android:name="android.permission.INTERNET" />',
39
+          '<uses-permission android:name="android.permission.INTERNET" />\n    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> '
40
+        )
41
+        // append DOWNLOAD_COMPLETE intent permission
42
+        data = String(data).replace(
43
+          '<category android:name="android.intent.category.LAUNCHER" />',
44
+          '<category android:name="android.intent.category.LAUNCHER" />\n     <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>'
45
+        )
46
+        fs.writeFileSync(MANIFEST_PATH, data);
47
+        console.log('RNFetchBlob patching AndroidManifest.xml .. ok');
54
 
48
 
55
-    console.log('RNFetchBlob patching AndroidManifest.xml .. ');
56
-    // append fs permission
57
-    data = String(data).replace(
58
-      '<uses-permission android:name="android.permission.INTERNET" />',
59
-      '<uses-permission android:name="android.permission.INTERNET" />\n    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> '
60
-    )
61
-    // append DOWNLOAD_COMPLETE intent permission
62
-    data = String(data).replace(
63
-      '<category android:name="android.intent.category.LAUNCHER" />',
64
-      '<category android:name="android.intent.category.LAUNCHER" />\n     <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>'
65
-    )
66
-    fs.writeFileSync(MANIFEST_PATH, data);
67
-    console.log('RNFetchBlob patching AndroidManifest.xml .. ok');
49
+      }
68
 
50
 
51
+    })
52
+  }
53
+  else {
54
+    console.log(
55
+      '\033[95mreact-native-fetch-blob \033[97mwill not automatically add Android permissions after \033[92m0.9.4 '+
56
+      '\033[97mplease run the following command if you want to add default permissions :\n\n' +
57
+      '\033[96m\tRNFB_ANDROID_PERMISSION=true react-native link \n')
69
   }
58
   }
70
 
59
 
71
-})
60
+  function checkVersion() {
61
+    console.log('RNFetchBlob checking app version ..');
62
+    return parseFloat(/\d\.\d+(?=\.)/.exec(package.dependencies['react-native']));
63
+  }
72
 
64
 
73
-function checkVersion() {
74
-  console.log('RNFetchBlob checking app version ..');
75
-  return parseFloat(/\d\.\d+(?=\.)/.exec(package.dependencies['react-native']));
65
+} catch(err) {
66
+  console.log(
67
+    '\033[95mreact-native-fetch-blob\033[97m link \033[91mFAILED \033[97m\nCould not automatically link package :'+
68
+    err.stack +
69
+    'please follow the instructions to manually link the library : ' +
70
+    '\033[4mhttps://github.com/wkh237/react-native-fetch-blob/wiki/Manually-Link-Package\n')
76
 }
71
 }

+ 2
- 1
src/utils/log.js Bestand weergeven

9
   }
9
   }
10
 
10
 
11
   level(val:number) {
11
   level(val:number) {
12
+    this._isEnable = true
12
     this._level = val
13
     this._level = val
13
   }
14
   }
14
 
15
 
33
   }
34
   }
34
 
35
 
35
   error(...args) {
36
   error(...args) {
36
-    this._isEnable && this._level > -1 && console.log(this._name, 'error:', ...args)
37
+    this._isEnable && this._level > -1 && console.warn(this._name, 'error:', ...args)
37
   }
38
   }
38
 
39
 
39
 }
40
 }

BIN
test-server/public/1600k-img-dummy.jpg Bestand weergeven


+ 6
- 1
test-server/server.js Bestand weergeven

65
   next();
65
   next();
66
 })
66
 })
67
 
67
 
68
+app.all('/upload', (req, res) => {
69
+  console.log(req.headers)
70
+  res.send(req.headers)
71
+})
72
+
68
 app.get('/unicode', (req, res) => {
73
 app.get('/unicode', (req, res) => {
69
   res.send({ data:'你好!'})
74
   res.send({ data:'你好!'})
70
 })
75
 })
149
   res.send(req.headers)
154
   res.send(req.headers)
150
 })
155
 })
151
 
156
 
152
-app.post('/upload', bodyParser.urlencoded({ extended : true }), (req, res) => {
157
+app.post('/upload_urlencode', bodyParser.urlencoded({ extended : true }), (req, res) => {
153
   console.log(JSON.stringify(req.headers))
158
   console.log(JSON.stringify(req.headers))
154
   console.log(JSON.stringify(req.body))
159
   console.log(JSON.stringify(req.body))
155
   res.status(200).send(req.body)
160
   res.status(200).send(req.body)

+ 2
- 0
test/test-0.7.0.js Bestand weergeven

45
     if(Date.now() - deb < 1000)
45
     if(Date.now() - deb < 1000)
46
       return
46
       return
47
     deb = Date.now()
47
     deb = Date.now()
48
+    console.log('download', now, total)
48
     report(<Info uid="200" key="progress">
49
     report(<Info uid="200" key="progress">
49
       <Text>
50
       <Text>
50
         {`download ${now} / ${total} bytes (${Math.floor(now / (Date.now() - begin))} kb/s) ${(100*now/total).toFixed(2)}%`}
51
         {`download ${now} / ${total} bytes (${Math.floor(now / (Date.now() - begin))} kb/s) ${(100*now/total).toFixed(2)}%`}
72
       if(Date.now() - deb < 1000)
73
       if(Date.now() - deb < 1000)
73
         return
74
         return
74
       deb = Date.now()
75
       deb = Date.now()
76
+      console.log('upload', now, total)
75
       report(<Info uid="300" key="upload progress">
77
       report(<Info uid="300" key="upload progress">
76
         <Text>
78
         <Text>
77
           {`upload ${now} / ${total} bytes (${Math.floor(now / (Date.now() - begin))} kb/s) ${(100*now/total).toFixed(2)}%`}
79
           {`upload ${now} / ${total} bytes (${Math.floor(now / (Date.now() - begin))} kb/s) ${(100*now/total).toFixed(2)}%`}

+ 127
- 0
test/test-0.9.4.js Bestand weergeven

9
   Platform,
9
   Platform,
10
   Dimensions,
10
   Dimensions,
11
   Image,
11
   Image,
12
+  TouchableOpacity,
12
 } from 'react-native';
13
 } from 'react-native';
13
 
14
 
14
 window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
15
 window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
61
     })
62
     })
62
 
63
 
63
 })
64
 })
65
+
66
+describe('issue #111 get redirect destination', (report, done) => {
67
+  RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/redirect`)
68
+  .then((res) => {
69
+    console.log(res.info())
70
+    report(
71
+      <Assert key="redirect history should tracable"
72
+        expect={2}
73
+        actual={res.info().redirects.length}/>,
74
+      <Assert key="redirect history verify"
75
+        expect={[`${TEST_SERVER_URL}/redirect`, `${TEST_SERVER_URL}/public/github.png`]}
76
+        comparer={Comparer.equalToArray}
77
+        actual={res.info().redirects}/>,
78
+    )
79
+    done()
80
+  })
81
+})
82
+
83
+describe('chunked encoding option test', (report, done) => {
84
+
85
+  let path = null
86
+  let base64 = null
87
+
88
+  RNFetchBlob
89
+    // .config({ fileCache : true })
90
+    .fetch('GET', `${TEST_SERVER_URL}/public/1600k-img-dummy.jpg`)
91
+    .then((res) => {
92
+      base64 = res.base64()
93
+      return RNFetchBlob
94
+        .fetch('POST', `${TEST_SERVER_URL}/upload`, {
95
+          'Content-Type' : 'application/octet-stream;BASE64'
96
+        }, base64)
97
+    })
98
+    .then((res) => {
99
+      let headers = res.info().headers
100
+      console.log(res.text())
101
+      report(<Assert key="request should not use chunked encoding"
102
+        expect={undefined}
103
+        actual={headers['transfer-encoding']}/>)
104
+      fs.unlink(path)
105
+      done()
106
+    })
107
+})
108
+
109
+describe('#118 readStream performance prepare the file', (report, done) => {
110
+  let cache = null
111
+  let size = 0
112
+  let size2 = 0
113
+  let tick = Date.now()
114
+  let tick2 = Date.now()
115
+  let start = -1
116
+  let start2 = -1
117
+  let count = 0
118
+
119
+  let task = RNFetchBlob.config({fileCache : true})
120
+    .fetch('GET', `${TEST_SERVER_URL}/public/22mb-dummy`)
121
+  task.progress((current, total) => {
122
+    report(<Info key="prepare file" uid="prepare">
123
+      <Text key="pg"> {Math.floor(current/total*100)}% </Text>
124
+    </Info>)
125
+  })
126
+  task.then((res) => {
127
+      report(<Info key="preparation complete"><Text>start in 3 seconds</Text></Info>)
128
+      cache = res.path()
129
+      setTimeout(readFile, 2500)
130
+      function readFile() {
131
+        fs.readStream(cache, 'utf8', 102400, 10)
132
+          .then((stream) => {
133
+            stream.open()
134
+            start = Date.now()
135
+            stream.onData((chunk) => {
136
+              count++
137
+              size += chunk.length
138
+              if(Date.now() - tick > 500) {
139
+                console.log(size, ' read')
140
+                tick = Date.now()
141
+                report(
142
+                  <Info key="size" uid="100">
143
+                    <Text key="AA">File 1 {size}/22000000 bytes read</Text>
144
+                    <Text key="BB">File 2 {size2}/22000000 bytes read</Text>
145
+                  </Info>)
146
+              }
147
+            })
148
+            stream.onEnd(() => {
149
+              report(
150
+                <Info key="size" uid="100"><Text>{size} bytes read</Text></Info>,
151
+                <Info key="elapsed"><Text>{Date.now() - start} ms</Text></Info>,
152
+                <Info key="events"><Text>{count} times</Text></Info>,
153
+                <Assert key="JS thread is not blocked" expect={true} actual={true}/>,)
154
+              fs.stat(cache).then((stat) => {
155
+                report(
156
+                  <Info key="info"><Text>{JSON.stringify(stat)}</Text></Info>)
157
+                fs.unlink(cache)
158
+                done()
159
+              })
160
+            })
161
+          })
162
+      }
163
+    })
164
+})
165
+
166
+describe('issue #120 upload progress should work when sending body as-is', (report, done) => {
167
+
168
+  let data = RNTest.prop('image')
169
+  let tick = 0
170
+
171
+  let task = RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
172
+    Authorization : `Bearer ${DROPBOX_TOKEN}`,
173
+    'Dropbox-API-Arg': '{\"path\": \"/rn-upload/'+FILENAME+'\",\"mode\": \"add\",\"autorename\": true,\"mute\": false}',
174
+    'Content-Type' : 'text/plain; charset=dropbox-cors-hack',
175
+  }, data)
176
+
177
+  task.uploadProgress((current, total) => {
178
+    tick ++
179
+  })
180
+  .then((res) => {
181
+    let resp = res.json()
182
+    report(
183
+      <Info key="viewer"><Text>{JSON.stringify(resp)}</Text></Info>,
184
+      <Assert key="event should be triggered"
185
+        expect={tick}
186
+        comparer={Comparer.greater} actual={0}/>)
187
+    done()
188
+  })
189
+
190
+})

+ 60
- 11
test/test-firebase.js Bestand weergeven

57
     <Info key="user content" uid="user data">
57
     <Info key="user content" uid="user data">
58
       <Text>{JSON.stringify(user)}</Text>
58
       <Text>{JSON.stringify(user)}</Text>
59
     </Info>)
59
     </Info>)
60
-    done()
60
+    if(user)
61
+      done()
61
   })
62
   })
62
 })
63
 })
63
 
64
 
108
       report(<Info key="test image">
109
       report(<Info key="test image">
109
         <Image style={styles.image} source={{uri : prefix + resp.path()}}/>
110
         <Image style={styles.image} source={{uri : prefix + resp.path()}}/>
110
       </Info>)
111
       </Info>)
111
-      let blob = new Blob(RNFetchBlob.wrap(resp.path()), { type : 'image/jpg' })
112
-      blob.onCreated(() => {
113
-        firebase.storage().ref('rnfbtest')
114
-          .child(tier2FileName)
115
-          .put(blob, { contentType : 'image/jpg' })
116
-          .then(() => {
117
-            report(<Assert key="upload finished" />)
118
-            done()
119
-          })
120
-      })
112
+      return Blob.build(RNFetchBlob.wrap(resp.path()), { type : 'image/jpg' })
113
+    })
114
+    .then((blob) => {
115
+      return firebase.storage().ref('rnfbtest')
116
+        .child(tier2FileName)
117
+        .put(blob, { contentType : 'image/jpg' })
118
+    })
119
+    .then(() => {
120
+      report(<Assert key="upload finished" />)
121
+      done()
121
     })
122
     })
122
 })
123
 })
123
 
124
 
175
     console.log(err)
176
     console.log(err)
176
   }
177
   }
177
 })
178
 })
179
+
180
+Platform.OS === 'ios' && describe('upload from CameraRoll', (report, done) => {
181
+
182
+    CameraRoll.getPhotos({first : 10})
183
+    .then((resp) => {
184
+      let url = resp.edges[0].node.image.uri
185
+      console.log('CameraRoll',url)
186
+      return Blob.build(RNFetchBlob.wrap(url), {type:'image/jpg'})
187
+    })
188
+    .then((b) => {
189
+      blob = b
190
+      console.log('start upload ..')
191
+      return firebase.storage()
192
+        .ref('rnfbtest').child(`camra-roll-${Platform.OS}-${Date.now()}.jpg`)
193
+        .put(b, {contentType : 'image/jpg'})
194
+    })
195
+    .then((snapshot) => {
196
+      report(<Assert key="upload sucess" expect={true} actual={true}/>)
197
+      done()
198
+    })
199
+})
200
+
201
+
202
+Platform.OS === 'android' && describe('upload from CameraRoll', (report, done) => {
203
+
204
+  let blob
205
+  RNFetchBlob.config({
206
+      addAndroidDownloads : { useDownloadManager : true }
207
+    })
208
+    .fetch('GET', `${TEST_SERVER_URL}/public/1600k-img-dummy.jpg`)
209
+    .then((res) => CameraRoll.getPhotos({first : 10}))
210
+    .then((resp) => {
211
+      let url = resp.edges[0].node.image.uri
212
+      console.log('CameraRoll',url)
213
+      return Blob.build(RNFetchBlob.wrap(url), {type:'image/jpg'})
214
+    })
215
+    .then((b) => {
216
+      blob = b
217
+      return firebase.storage()
218
+        .ref('rnfbtest').child(`camra-roll-${Platform.OS}-${Date.now()}.jpg`)
219
+        .put(b, {contentType : 'image/jpg'})
220
+    })
221
+    .then((snapshot) => {
222
+      report(<Assert key="upload sucess" expect={true} actual={true}/>)
223
+      blob.close()
224
+      done()
225
+    })
226
+})

+ 16
- 17
test/test-init.js Bestand weergeven

59
 })
59
 })
60
 
60
 
61
 
61
 
62
-// require('./test-0.1.x-0.4.x')
63
-// require('./test-0.5.1')
64
-// require('./test-0.5.2')
65
-// require('./test-0.6.0')
66
-// require('./test-0.6.2')
67
-// require('./test-0.7.0')
68
-// require('./test-0.8.0')
69
-// require('./test-0.9.0')
70
-// require('./test-0.9.2')
71
-require('./test-0.10.0')
72
-// require('./test-0.9.4')
73
-// require('./test-fetch')
74
-// require('./test-fs')
75
-// require('./test-xmlhttp')
76
-// require('./test-blob')
77
-// require('./test-firebase')
78
-// require('./test-android')
62
+require('./test-0.1.x-0.4.x')
63
+require('./test-0.5.1')
64
+require('./test-0.5.2')
65
+require('./test-0.6.0')
66
+require('./test-0.6.2')
67
+require('./test-0.7.0')
68
+require('./test-0.8.0')
69
+require('./test-0.9.0')
70
+require('./test-0.9.2')
71
+require('./test-0.9.4')
72
+require('./test-fetch')
73
+require('./test-fs')
74
+require('./test-xmlhttp')
75
+require('./test-blob')
76
+require('./test-firebase')
77
+require('./test-android')
79
 // require('./test-stress')
78
 // require('./test-stress')
80
 // require('./benchmark')
79
 // require('./benchmark')

+ 2
- 2
test/test-xmlhttp.js Bestand weergeven

157
     if(this.readyState == 4) {
157
     if(this.readyState == 4) {
158
       report(<Assert key="headers should be cleared by open()"
158
       report(<Assert key="headers should be cleared by open()"
159
         expect={"200"}
159
         expect={"200"}
160
-        actual={this.response.value}/>)
160
+        actual={JSON.parse(this.response).value}/>)
161
       done()
161
       done()
162
     }
162
     }
163
   }
163
   }
243
       report(
243
       report(
244
         <Assert key="reponse should correct"
244
         <Assert key="reponse should correct"
245
           expect={time}
245
           expect={time}
246
-          actual={Math.floor(xhr.response.time)}/>,
246
+          actual={Math.floor(JSON.parse(xhr.response).time)}/>,
247
         <Assert key="responseType should correct"
247
         <Assert key="responseType should correct"
248
           expect={'json'}
248
           expect={'json'}
249
           actual={xhr.responseType}/>)
249
           actual={xhr.responseType}/>)