Quellcode durchsuchen

Merge branch 'master' into 0.7.0

Ben Hsieh vor 8 Jahren
Ursprung
Commit
2c35f522df

+ 23
- 12
README.md Datei anzeigen

1
-# react-native-fetch-blob [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![npm](https://img.shields.io/npm/l/express.svg?maxAge=2592000&style=flat-square)]() [![npm](https://img.shields.io/badge/inProgress-0.7.0-yellow.svg?style=flat-square)]()
1
+# react-native-fetch-blob [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![npm](https://img.shields.io/npm/l/express.svg?maxAge=2592000&style=flat-square)]() [![npm](https://img.shields.io/badge/inProgress-0.7.0-yellow.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/milestones)
2
 
2
 
3
 A module provides upload, download, and files access API. Supports file stream read/write for process large files.
3
 A module provides upload, download, and files access API. Supports file stream read/write for process large files.
4
 
4
 
5
 **Why do we need this**
5
 **Why do we need this**
6
 
6
 
7
-React Native does not support `Blob` object at this moment, which means if you're going to send/receive binary data via `fetch` API, that might not work as you expect. See [[fetch] Does fetch with blob() marshal data across the bridge?](https://github.com/facebook/react-native/issues/854).
7
+React Native does not support `Blob` object at this moment, which means if you're going to send/receive binary data via `fetch` API, that might not work as you expect. See [facebook/react-native#854](https://github.com/facebook/react-native/issues/854).
8
 
8
 
9
 For some use cases, you might get into trouble. For example, displaying an image that requires a specific field in headers (ex. "Authorization : Bearer ...") or body, so you can't just pass the image uri to `Image` component because that will probably returns a 401 response. Or you're going to upload binary data which generated from JS, the server will get an empry body due to [this issue](https://github.com/facebook/react-native/issues/854). With help of APIs provided by this module, you can send HTTP request with any headers, and decide how to handle the response/reqeust data without worry about if it is not supported by `fetch` API. The response data can be just simply converted into BASE64 string, or stored to a file directly so that you can read it by using file access APIs such as readFile, readStream.
9
 For some use cases, you might get into trouble. For example, displaying an image that requires a specific field in headers (ex. "Authorization : Bearer ...") or body, so you can't just pass the image uri to `Image` component because that will probably returns a 401 response. Or you're going to upload binary data which generated from JS, the server will get an empry body due to [this issue](https://github.com/facebook/react-native/issues/854). With help of APIs provided by this module, you can send HTTP request with any headers, and decide how to handle the response/reqeust data without worry about if it is not supported by `fetch` API. The response data can be just simply converted into BASE64 string, or stored to a file directly so that you can read it by using file access APIs such as readFile, readStream.
10
 
10
 
11
 This module was designed to be a substitution of `Blob`, there's a set of APIs including basic file system CRUD method, and file stream reader/writer. Also it has a special `fetch` implementation that supports binary request/response body.
11
 This module was designed to be a substitution of `Blob`, there's a set of APIs including basic file system CRUD method, and file stream reader/writer. Also it has a special `fetch` implementation that supports binary request/response body.
12
 
12
 
13
-**Pre v0.5.0 Users**
13
+**Backward Compatible**
14
 
14
 
15
-All updates are `backward-compatible` generally you don't have to change existing code unless you're going to use new APIs. In latest version (v0.5.0), new APIs can either `upload` or `download` files simply using a file path. It's much more memory efficent in some use case. We've also introduced `fs` APIs for access files, and `file stream` API that helps you read/write files (especially for **large ones**), see [Examples](#user-content-usage) bellow. This module implements native methods, supports both Android (uses awesome native library  [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client])) and IOS.
15
+All updates are `backward-compatible` generally you don't have to change existing code unless you're going to use new APIs. But we recommend pre `0.5.0` users consider upgrade the package to latest version, since we have introduced new APIs can either `upload` or `download` files simply using a file path. It's much more memory efficent in some use case. We've also introduced `fs` APIs for access files, and `file stream` API that helps you read/write files (especially for **large ones**), see [Examples](#user-content-recipes) bellow. This module implements native methods, supports both Android (uses awesome native library  [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client])) and IOS.
16
 
16
 
17
 ## TOC
17
 ## TOC
18
 
18
 
19
 * [Installation](#user-content-installation)
19
 * [Installation](#user-content-installation)
20
-* [Guide](#user-content-guide)
20
+* [Recipes](#user-content-recipes)
21
  * [Download file](#user-content-download-example--fetch-files-that-needs-authorization-token)
21
  * [Download file](#user-content-download-example--fetch-files-that-needs-authorization-token)
22
  * [Upload file](#user-content-upload-example--dropbox-files-upload-api)
22
  * [Upload file](#user-content-upload-example--dropbox-files-upload-api)
23
  * [Multipart/form upload](#user-content-multipartform-data-example--post-form-data-with-file-and-data)
23
  * [Multipart/form upload](#user-content-multipartform-data-example--post-form-data-with-file-and-data)
79
 
79
 
80
 Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. So adding permissions in `AndroidManifest.xml` won't work in Android 6.0 devices. To grant permissions in runtime, you might use modules like [react-native-android-permissions](https://github.com/lucasferreira/react-native-android-permissions).
80
 Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. So adding permissions in `AndroidManifest.xml` won't work in Android 6.0 devices. To grant permissions in runtime, you might use modules like [react-native-android-permissions](https://github.com/lucasferreira/react-native-android-permissions).
81
 
81
 
82
-## Guide
82
+## Recipes
83
 
83
 
84
 ```js
84
 ```js
85
 import RNFetchBlob from 'react-native-fetch-blob'
85
 import RNFetchBlob from 'react-native-fetch-blob'
256
   })
256
   })
257
 ```
257
 ```
258
 
258
 
259
-What if you want to upload a file in some field ? 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`)
259
+What if you want to upload a file in some field ? 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.
260
 
260
 
261
 ```js
261
 ```js
262
 
262
 
274
       // Or simply wrap the file path with RNFetchBlob.wrap().
274
       // Or simply wrap the file path with RNFetchBlob.wrap().
275
       data: RNFetchBlob.wrap(PATH_TO_THE_FILE)
275
       data: RNFetchBlob.wrap(PATH_TO_THE_FILE)
276
     },
276
     },
277
+    {
278
+      name : 'ringtone',
279
+      filename : 'ring.mp3',
280
+      // use custom MIME type
281
+      type : 'application/mp3',
282
+      // upload a file from asset is also possible in version >= 0.6.2
283
+      data : RNFetchBlob.wrap(RNFetchBlob.fs.asset('default-ringtone.mp3'))
284
+    }
277
     // elements without property `filename` will be sent as plain text
285
     // elements without property `filename` will be sent as plain text
278
     { name : 'name', data : 'user'},
286
     { name : 'name', data : 'user'},
279
     { name : 'info', data : JSON.stringify({
287
     { name : 'info', data : JSON.stringify({
289
 
297
 
290
 #### Upload/Download progress
298
 #### Upload/Download progress
291
 
299
 
292
-In `version >= 0.4.2` it is possible to know the upload/download progress.
300
+In `version >= 0.4.2` it is possible to know the upload/download progress. On Anroid, only download progress is supported. See [wiki](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetchprogresseventlistenerpromisernfetchblobresponse) for more information.
293
 
301
 
294
 ```js
302
 ```js
295
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
303
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
391
 
399
 
392
 #### File Access
400
 #### File Access
393
 
401
 
394
-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 I realized that, it's hard to find a great solution to manage cached files, every one who use this moudle may need those APIs for there cases.
402
+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.
395
 
403
 
396
-File Access APIs
404
+Before get started using file APIs we recommend read [Differences between File Source](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#differences-between-file-source) first.
397
 
405
 
406
+File Access APIs
407
+- [asset (0.6.2)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#assetfilenamestringstring)
398
 - [dirs](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs)
408
 - [dirs](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs)
399
 - [createFile](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#createfilepath-data-encodingpromise)
409
 - [createFile](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#createfilepath-data-encodingpromise)
400
 - [writeFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise)
410
 - [writeFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise)
423
 
433
 
424
 ```js
434
 ```js
425
 let data = ''
435
 let data = ''
426
-RNFetchBlob.readStream(
436
+RNFetchBlob.fs.readStream(
427
     // encoding, should be one of `base64`, `utf8`, `ascii`
437
     // encoding, should be one of `base64`, `utf8`, `ascii`
428
     'base64',
438
     'base64',
429
     // file path
439
     // file path
450
 When use `writeStream`, the stream is also opened immediately, but you have to `write`, and `close` by yourself.
460
 When use `writeStream`, the stream is also opened immediately, but you have to `write`, and `close` by yourself.
451
 
461
 
452
 ```js
462
 ```js
453
-RNFetchBlob.writeStream(
463
+RNFetchBlob.fs.writeStream(
454
     PATH_TO_FILE,
464
     PATH_TO_FILE,
455
     // encoding, should be one of `base64`, `utf8`, `ascii`
465
     // encoding, should be one of `base64`, `utf8`, `ascii`
456
     'utf8',
466
     'utf8',
539
 
549
 
540
 | Version | |
550
 | Version | |
541
 |---|---|
551
 |---|---|
552
+| 0.6.2 | Add support of asset file and camera roll files, Support custom MIME type when sending multipart request, thanks @smartt |
542
 | 0.6.1 | Fix #37 progress report API issue on IOS |
553
 | 0.6.1 | Fix #37 progress report API issue on IOS |
543
 | 0.6.0 | Add readFile and writeFile API for easier file access, also added Android download manager support. |
554
 | 0.6.0 | Add readFile and writeFile API for easier file access, also added Android download manager support. |
544
 | 0.5.8 | Fix #33 PUT request will always be sent as POST on Android |
555
 | 0.5.8 | Fix #33 PUT request will always be sent as POST on Android |

+ 1
- 1
package.json Datei anzeigen

1
 {
1
 {
2
   "name": "fetchblob",
2
   "name": "fetchblob",
3
-  "version": "0.6.1",
3
+  "version": "0.6.2",
4
   "private": true,
4
   "private": true,
5
   "scripts": {
5
   "scripts": {
6
     "start": "node node_modules/react-native/local-cli/cli.js start",
6
     "start": "node node_modules/react-native/local-cli/cli.js start",

+ 25
- 12
src/README.md Datei anzeigen

1
-# react-native-fetch-blob [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![npm](https://img.shields.io/npm/l/express.svg?maxAge=2592000&style=flat-square)]()
1
+# react-native-fetch-blob [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![npm](https://img.shields.io/npm/l/express.svg?maxAge=2592000&style=flat-square)]() [![npm](https://img.shields.io/badge/inProgress-0.7.0-yellow.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/milestones)
2
 
2
 
3
 A module provides upload, download, and files access API. Supports file stream read/write for process large files.
3
 A module provides upload, download, and files access API. Supports file stream read/write for process large files.
4
 
4
 
5
+## [Please check our github for updated document](https://github.com/wkh237/react-native-fetch-blob)
6
+
5
 **Why do we need this**
7
 **Why do we need this**
6
 
8
 
7
-React Native does not support `Blob` object at this moment, which means if you're going to send/receive binary data via `fetch` API, that might not work as you expect. See [[fetch] Does fetch with blob() marshal data across the bridge?](https://github.com/facebook/react-native/issues/854).
9
+React Native does not support `Blob` object at this moment, which means if you're going to send/receive binary data via `fetch` API, that might not work as you expect. See [facebook/react-native#854](https://github.com/facebook/react-native/issues/854).
8
 
10
 
9
 For some use cases, you might get into trouble. For example, displaying an image that requires a specific field in headers (ex. "Authorization : Bearer ...") or body, so you can't just pass the image uri to `Image` component because that will probably returns a 401 response. Or you're going to upload binary data which generated from JS, the server will get an empry body due to [this issue](https://github.com/facebook/react-native/issues/854). With help of APIs provided by this module, you can send HTTP request with any headers, and decide how to handle the response/reqeust data without worry about if it is not supported by `fetch` API. The response data can be just simply converted into BASE64 string, or stored to a file directly so that you can read it by using file access APIs such as readFile, readStream.
11
 For some use cases, you might get into trouble. For example, displaying an image that requires a specific field in headers (ex. "Authorization : Bearer ...") or body, so you can't just pass the image uri to `Image` component because that will probably returns a 401 response. Or you're going to upload binary data which generated from JS, the server will get an empry body due to [this issue](https://github.com/facebook/react-native/issues/854). With help of APIs provided by this module, you can send HTTP request with any headers, and decide how to handle the response/reqeust data without worry about if it is not supported by `fetch` API. The response data can be just simply converted into BASE64 string, or stored to a file directly so that you can read it by using file access APIs such as readFile, readStream.
10
 
12
 
11
 This module was designed to be a substitution of `Blob`, there's a set of APIs including basic file system CRUD method, and file stream reader/writer. Also it has a special `fetch` implementation that supports binary request/response body.
13
 This module was designed to be a substitution of `Blob`, there's a set of APIs including basic file system CRUD method, and file stream reader/writer. Also it has a special `fetch` implementation that supports binary request/response body.
12
 
14
 
13
-**Pre v0.5.0 Users**
15
+**Backward Compatible**
14
 
16
 
15
-All updates are `backward-compatible` generally you don't have to change existing code unless you're going to use new APIs. In latest version (v0.5.0), new APIs can either `upload` or `download` files simply using a file path. It's much more memory efficent in some use case. We've also introduced `fs` APIs for access files, and `file stream` API that helps you read/write files (especially for **large ones**), see [Examples](#user-content-usage) bellow. This module implements native methods, supports both Android (uses awesome native library  [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client])) and IOS.
17
+All updates are `backward-compatible` generally you don't have to change existing code unless you're going to use new APIs. But we recommend pre `0.5.0` users consider upgrade the package to latest version, since we have introduced new APIs can either `upload` or `download` files simply using a file path. It's much more memory efficent in some use case. We've also introduced `fs` APIs for access files, and `file stream` API that helps you read/write files (especially for **large ones**), see [Examples](#user-content-recipes) bellow. This module implements native methods, supports both Android (uses awesome native library  [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client])) and IOS.
16
 
18
 
17
 ## TOC
19
 ## TOC
18
 
20
 
19
 * [Installation](#user-content-installation)
21
 * [Installation](#user-content-installation)
20
-* [Guide](#user-content-guide)
22
+* [Recipes](#user-content-recipes)
21
  * [Download file](#user-content-download-example--fetch-files-that-needs-authorization-token)
23
  * [Download file](#user-content-download-example--fetch-files-that-needs-authorization-token)
22
  * [Upload file](#user-content-upload-example--dropbox-files-upload-api)
24
  * [Upload file](#user-content-upload-example--dropbox-files-upload-api)
23
  * [Multipart/form upload](#user-content-multipartform-data-example--post-form-data-with-file-and-data)
25
  * [Multipart/form upload](#user-content-multipartform-data-example--post-form-data-with-file-and-data)
79
 
81
 
80
 Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. So adding permissions in `AndroidManifest.xml` won't work in Android 6.0 devices. To grant permissions in runtime, you might use modules like [react-native-android-permissions](https://github.com/lucasferreira/react-native-android-permissions).
82
 Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. So adding permissions in `AndroidManifest.xml` won't work in Android 6.0 devices. To grant permissions in runtime, you might use modules like [react-native-android-permissions](https://github.com/lucasferreira/react-native-android-permissions).
81
 
83
 
82
-## Guide
84
+## Recipes
83
 
85
 
84
 ```js
86
 ```js
85
 import RNFetchBlob from 'react-native-fetch-blob'
87
 import RNFetchBlob from 'react-native-fetch-blob'
256
   })
258
   })
257
 ```
259
 ```
258
 
260
 
259
-What if you want to upload a file in some field ? 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`)
261
+What if you want to upload a file in some field ? 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.
260
 
262
 
261
 ```js
263
 ```js
262
 
264
 
274
       // Or simply wrap the file path with RNFetchBlob.wrap().
276
       // Or simply wrap the file path with RNFetchBlob.wrap().
275
       data: RNFetchBlob.wrap(PATH_TO_THE_FILE)
277
       data: RNFetchBlob.wrap(PATH_TO_THE_FILE)
276
     },
278
     },
279
+    {
280
+      name : 'ringtone',
281
+      filename : 'ring.mp3',
282
+      // use custom MIME type
283
+      type : 'application/mp3',
284
+      // upload a file from asset is also possible in version >= 0.6.2
285
+      data : RNFetchBlob.wrap(RNFetchBlob.fs.asset('default-ringtone.mp3'))
286
+    }
277
     // elements without property `filename` will be sent as plain text
287
     // elements without property `filename` will be sent as plain text
278
     { name : 'name', data : 'user'},
288
     { name : 'name', data : 'user'},
279
     { name : 'info', data : JSON.stringify({
289
     { name : 'info', data : JSON.stringify({
289
 
299
 
290
 #### Upload/Download progress
300
 #### Upload/Download progress
291
 
301
 
292
-In `version >= 0.4.2` it is possible to know the upload/download progress.
302
+In `version >= 0.4.2` it is possible to know the upload/download progress. On Anroid, only download progress is supported. See [wiki](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetchprogresseventlistenerpromisernfetchblobresponse) for more information.
293
 
303
 
294
 ```js
304
 ```js
295
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
305
   RNFetchBlob.fetch('POST', 'http://www.example.com/upload', {
391
 
401
 
392
 #### File Access
402
 #### File Access
393
 
403
 
394
-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 I realized that, it's hard to find a great solution to manage cached files, every one who use this moudle may need those APIs for there cases.
404
+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.
395
 
405
 
396
-File Access APIs
406
+Before get started using file APIs we recommend read [Differences between File Source](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#differences-between-file-source) first.
397
 
407
 
408
+File Access APIs
409
+- [asset (0.6.2)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#assetfilenamestringstring)
398
 - [dirs](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs)
410
 - [dirs](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs)
399
 - [createFile](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#createfilepath-data-encodingpromise)
411
 - [createFile](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#createfilepath-data-encodingpromise)
400
 - [writeFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise)
412
 - [writeFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise)
423
 
435
 
424
 ```js
436
 ```js
425
 let data = ''
437
 let data = ''
426
-RNFetchBlob.readStream(
438
+RNFetchBlob.fs.readStream(
427
     // encoding, should be one of `base64`, `utf8`, `ascii`
439
     // encoding, should be one of `base64`, `utf8`, `ascii`
428
     'base64',
440
     'base64',
429
     // file path
441
     // file path
450
 When use `writeStream`, the stream is also opened immediately, but you have to `write`, and `close` by yourself.
462
 When use `writeStream`, the stream is also opened immediately, but you have to `write`, and `close` by yourself.
451
 
463
 
452
 ```js
464
 ```js
453
-RNFetchBlob.writeStream(
465
+RNFetchBlob.fs.writeStream(
454
     PATH_TO_FILE,
466
     PATH_TO_FILE,
455
     // encoding, should be one of `base64`, `utf8`, `ascii`
467
     // encoding, should be one of `base64`, `utf8`, `ascii`
456
     'utf8',
468
     'utf8',
539
 
551
 
540
 | Version | |
552
 | Version | |
541
 |---|---|
553
 |---|---|
554
+| 0.6.2 | Add support of asset file and camera roll files, Support custom MIME type when sending multipart request, thanks @smartt |
542
 | 0.6.1 | Fix #37 progress report API issue on IOS |
555
 | 0.6.1 | Fix #37 progress report API issue on IOS |
543
 | 0.6.0 | Add readFile and writeFile API for easier file access, also added Android download manager support. |
556
 | 0.6.0 | Add readFile and writeFile API for easier file access, also added Android download manager support. |
544
 | 0.5.8 | Fix #33 PUT request will always be sent as POST on Android |
557
 | 0.5.8 | Fix #33 PUT request will always be sent as POST on Android |

+ 3
- 0
src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java Datei anzeigen

13
 public class RNFetchBlob extends ReactContextBaseJavaModule {
13
 public class RNFetchBlob extends ReactContextBaseJavaModule {
14
 
14
 
15
     String filePathPrefix = "RNFetchBlob-file://";
15
     String filePathPrefix = "RNFetchBlob-file://";
16
+    static ReactApplicationContext RCTContext;
16
 
17
 
17
     public RNFetchBlob(ReactApplicationContext reactContext) {
18
     public RNFetchBlob(ReactApplicationContext reactContext) {
19
+
18
         super(reactContext);
20
         super(reactContext);
21
+        RCTContext = reactContext;
19
     }
22
     }
20
 
23
 
21
     @Override
24
     @Override

+ 164
- 43
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java Datei anzeigen

1
 package com.RNFetchBlob;
1
 package com.RNFetchBlob;
2
 
2
 
3
+import android.app.Application;
4
+import android.content.CursorLoader;
3
 import android.content.Intent;
5
 import android.content.Intent;
6
+import android.content.res.AssetFileDescriptor;
7
+import android.content.res.AssetManager;
8
+import android.database.Cursor;
4
 import android.media.MediaScannerConnection;
9
 import android.media.MediaScannerConnection;
5
 import android.net.Uri;
10
 import android.net.Uri;
6
 import android.os.AsyncTask;
11
 import android.os.AsyncTask;
7
 import android.os.Environment;
12
 import android.os.Environment;
13
+import android.provider.MediaStore;
8
 
14
 
9
 import com.facebook.react.bridge.Arguments;
15
 import com.facebook.react.bridge.Arguments;
10
 import com.facebook.react.bridge.Callback;
16
 import com.facebook.react.bridge.Callback;
43
     ReactApplicationContext mCtx;
49
     ReactApplicationContext mCtx;
44
     DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
50
     DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
45
     String encoding = "base64";
51
     String encoding = "base64";
52
+    static final String assetPrefix = "bundle-assets://";
46
     boolean append = false;
53
     boolean append = false;
47
     OutputStream writeStreamInstance = null;
54
     OutputStream writeStreamInstance = null;
48
     static HashMap<String, RNFetchBlobFS> fileStreams = new HashMap<>();
55
     static HashMap<String, RNFetchBlobFS> fileStreams = new HashMap<>();
134
      * @param promise
141
      * @param promise
135
      */
142
      */
136
     static public void readFile(String path, String encoding, final Promise promise ) {
143
     static public void readFile(String path, String encoding, final Promise promise ) {
144
+        path = normalizePath(path);
137
         AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
145
         AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
138
             @Override
146
             @Override
139
             protected Integer doInBackground(String... strings) {
147
             protected Integer doInBackground(String... strings) {
141
                 try {
149
                 try {
142
                     String path = strings[0];
150
                     String path = strings[0];
143
                     String encoding = strings[1];
151
                     String encoding = strings[1];
144
-                    File f = new File(path);
145
-                    int length = (int) f.length();
146
-                    byte[] bytes = new byte[length];
147
-                    FileInputStream in = new FileInputStream(f);
148
-                    in.read(bytes);
149
-                    in.close();
152
+                    byte[] bytes;
153
+
154
+                    if(path.startsWith(assetPrefix)) {
155
+                        String assetName = path.replace(assetPrefix, "");
156
+                        long length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
157
+                        bytes = new byte[(int) length];
158
+                        InputStream in = RNFetchBlob.RCTContext.getAssets().open(assetName);
159
+                        in.read(bytes, 0, (int) length);
160
+                        in.close();
161
+                    }
162
+                    else {
163
+                        File f = new File(path);
164
+                        int length = (int) f.length();
165
+                        bytes = new byte[length];
166
+                        FileInputStream in = new FileInputStream(f);
167
+                        in.read(bytes);
168
+                        in.close();
169
+                    }
170
+
150
                     switch (encoding.toLowerCase()) {
171
                     switch (encoding.toLowerCase()) {
151
                         case "base64" :
172
                         case "base64" :
152
                             promise.resolve(Base64.encodeToString(bytes, 0));
173
                             promise.resolve(Base64.encodeToString(bytes, 0));
209
      * @param bufferSize    Buffer size of read stream, default to 4096 (4095 when encode is `base64`)
230
      * @param bufferSize    Buffer size of read stream, default to 4096 (4095 when encode is `base64`)
210
      */
231
      */
211
     public void readStream( String path, String encoding, int bufferSize) {
232
     public void readStream( String path, String encoding, int bufferSize) {
233
+        path = normalizePath(path);
212
         AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
234
         AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
213
             @Override
235
             @Override
214
             protected Integer doInBackground(String ... args) {
236
             protected Integer doInBackground(String ... args) {
221
                     int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096;
243
                     int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096;
222
                     if(bufferSize > 0)
244
                     if(bufferSize > 0)
223
                         chunkSize = bufferSize;
245
                         chunkSize = bufferSize;
224
-                    FileInputStream fs = new FileInputStream(new File(path));
246
+
247
+                    InputStream fs;
248
+                    if(path.startsWith(assetPrefix)) {
249
+                        fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
250
+                    }
251
+                    else {
252
+                        fs = new FileInputStream(new File(path));
253
+                    }
254
+
225
                     byte[] buffer = new byte[chunkSize];
255
                     byte[] buffer = new byte[chunkSize];
226
                     int cursor = 0;
256
                     int cursor = 0;
227
                     boolean error = false;
257
                     boolean error = false;
314
         try {
344
         try {
315
             stream.write(chunk);
345
             stream.write(chunk);
316
             callback.invoke();
346
             callback.invoke();
317
-            chunk = null;
318
         } catch (Exception e) {
347
         } catch (Exception e) {
319
             callback.invoke(e.getLocalizedMessage());
348
             callback.invoke(e.getLocalizedMessage());
320
         }
349
         }
344
     }
373
     }
345
 
374
 
346
     /**
375
     /**
347
-     * Close file stream by ID
376
+     * Close file write stream by ID
348
      * @param streamId Stream ID
377
      * @param streamId Stream ID
349
      * @param callback JS context callback
378
      * @param callback JS context callback
350
      */
379
      */
395
      * @param callback  JS context callback
424
      * @param callback  JS context callback
396
      */
425
      */
397
     static void cp(String path, String dest, Callback callback) {
426
     static void cp(String path, String dest, Callback callback) {
427
+        path = normalizePath(path);
398
         InputStream in = null;
428
         InputStream in = null;
399
         OutputStream out = null;
429
         OutputStream out = null;
400
 
430
 
401
         try {
431
         try {
402
 
432
 
403
-            String destFolder = new File(dest).getPath();
404
-            if(!new File(path).exists()) {
433
+            if(!isPathExists(path)) {
405
                 callback.invoke("cp error: source file at path`" + path + "` not exists");
434
                 callback.invoke("cp error: source file at path`" + path + "` not exists");
406
                 return;
435
                 return;
407
             }
436
             }
409
             if(!new File(dest).exists())
438
             if(!new File(dest).exists())
410
                 new File(dest).createNewFile();
439
                 new File(dest).createNewFile();
411
 
440
 
412
-            in = new FileInputStream(path);
441
+            in = inputStreamFromPath(path);
413
             out = new FileOutputStream(dest);
442
             out = new FileOutputStream(dest);
414
 
443
 
415
             byte[] buf = new byte[1024];
444
             byte[] buf = new byte[1024];
454
      * @param callback  JS context callback
483
      * @param callback  JS context callback
455
      */
484
      */
456
     static void exists(String path, Callback callback) {
485
     static void exists(String path, Callback callback) {
457
-        boolean exist = new File(path).exists();
458
-        boolean isDir = new File(path).isDirectory();;
459
-        callback.invoke(exist, isDir);
486
+        path = normalizePath(path);
487
+        if(isAsset(path)) {
488
+            try {
489
+                String filename = path.replace(assetPrefix, "");
490
+                AssetFileDescriptor fd = RNFetchBlob.RCTContext.getAssets().openFd(filename);
491
+                callback.invoke(true, false);
492
+            } catch (IOException e) {
493
+                callback.invoke(false, false);
494
+            }
495
+        }
496
+        else {
497
+            boolean exist = new File(path).exists();
498
+            boolean isDir = new File(path).isDirectory();
499
+            callback.invoke(exist, isDir);
500
+        }
501
+
460
     }
502
     }
461
 
503
 
462
     /**
504
     /**
465
      * @param callback  JS context callback
507
      * @param callback  JS context callback
466
      */
508
      */
467
     static void ls(String path, Callback callback) {
509
     static void ls(String path, Callback callback) {
510
+        path = normalizePath(path);
468
         File src = new File(path);
511
         File src = new File(path);
469
-        if(!src.exists() || !src.isDirectory()) {
512
+        if (!src.exists() || !src.isDirectory()) {
470
             callback.invoke("ls error: failed to list path `" + path + "` for it is not exist or it is not a folder");
513
             callback.invoke("ls error: failed to list path `" + path + "` for it is not exist or it is not a folder");
471
             return;
514
             return;
472
         }
515
         }
473
-        String [] files = new File(path).list();
516
+        String[] files = new File(path).list();
474
         WritableArray arg = Arguments.createArray();
517
         WritableArray arg = Arguments.createArray();
475
-        for(String i : files) {
518
+        for (String i : files) {
476
             arg.pushString(i);
519
             arg.pushString(i);
477
         }
520
         }
478
         callback.invoke(null, arg);
521
         callback.invoke(null, arg);
479
     }
522
     }
480
 
523
 
481
     static void lstat(String path, final Callback callback) {
524
     static void lstat(String path, final Callback callback) {
482
-        File src = new File(path);
525
+        path = normalizePath(path);
526
+
483
         new AsyncTask<String, Integer, Integer>() {
527
         new AsyncTask<String, Integer, Integer>() {
484
             @Override
528
             @Override
485
             protected Integer doInBackground(String ...args) {
529
             protected Integer doInBackground(String ...args) {
511
      */
555
      */
512
     static void stat(String path, Callback callback) {
556
     static void stat(String path, Callback callback) {
513
         try {
557
         try {
514
-            File target = new File(path);
515
-            if (!target.exists()) {
516
-                callback.invoke("stat error: file " + path + " does not exists");
517
-                return;
518
-            }
519
-            WritableMap stat = Arguments.createMap();
520
-            stat.putString("filename", target.getName());
521
-            stat.putString("path", target.getPath());
522
-            stat.putString("type", target.isDirectory() ? "directory" : "file");
523
-            stat.putString("size", String.valueOf(target.length()));
524
-            String lastModified = String.valueOf(target.lastModified());
525
-            stat.putString("lastModified", lastModified);
526
-            callback.invoke(null, stat);
558
+            WritableMap result = statFile(path);
559
+            if(result == null)
560
+                callback.invoke("stat error: failed to list path `" + path + "` for it is not exist or it is not a folder", null);
561
+            else
562
+                callback.invoke(null, result);
527
         } catch(Exception err) {
563
         } catch(Exception err) {
528
             callback.invoke(err.getLocalizedMessage());
564
             callback.invoke(err.getLocalizedMessage());
529
         }
565
         }
530
     }
566
     }
531
 
567
 
568
+    /**
569
+     * Basic stat method
570
+     * @param path
571
+     * @return Stat result of a file or path
572
+     */
573
+    static WritableMap statFile(String path) {
574
+        try {
575
+            path = normalizePath(path);
576
+            WritableMap stat = Arguments.createMap();
577
+            if(isAsset(path)) {
578
+                String name = path.replace(assetPrefix, "");
579
+                AssetFileDescriptor fd = RNFetchBlob.RCTContext.getAssets().openFd(name);
580
+                stat.putString("filename", name);
581
+                stat.putString("path", path);
582
+                stat.putString("type", "asset");
583
+                stat.putString("size", String.valueOf(fd.getLength()));
584
+                stat.putString("lastModified", "0");
585
+            }
586
+            else {
587
+                File target = new File(path);
588
+                if (!target.exists()) {
589
+                    return null;
590
+                }
591
+                stat.putString("filename", target.getName());
592
+                stat.putString("path", target.getPath());
593
+                stat.putString("type", target.isDirectory() ? "directory" : "file");
594
+                stat.putString("size", String.valueOf(target.length()));
595
+                String lastModified = String.valueOf(target.lastModified());
596
+                stat.putString("lastModified", lastModified);
597
+
598
+            }
599
+            return stat;
600
+        } catch(Exception err) {
601
+            return null;
602
+        }
603
+    }
604
+
605
+    /**
606
+     * Media scanner scan file
607
+     * @param path
608
+     * @param mimes
609
+     * @param callback
610
+     */
532
     void scanFile(String [] path, String[] mimes, final Callback callback) {
611
     void scanFile(String [] path, String[] mimes, final Callback callback) {
533
         try {
612
         try {
534
             MediaScannerConnection.scanFile(mCtx, path, mimes, new MediaScannerConnection.OnScanCompletedListener() {
613
             MediaScannerConnection.scanFile(mCtx, path, mimes, new MediaScannerConnection.OnScanCompletedListener() {
669
         this.emitter.emit("RNFetchBlobStream" + taskId, eventData);
748
         this.emitter.emit("RNFetchBlobStream" + taskId, eventData);
670
     }
749
     }
671
 
750
 
672
-    static WritableMap statFile(String path) {
673
-        File target = new File(path);
674
-        if(!target.exists()) {
675
-            return null;
751
+    /**
752
+     * Get input stream of the given path, when the path is a string starts with bundle-assets://
753
+     * the stream is created by Assets Manager, otherwise use FileInputStream.
754
+     * @param path
755
+     * @return
756
+     * @throws IOException
757
+     */
758
+    static InputStream inputStreamFromPath(String path) throws IOException {
759
+        if (path.startsWith(assetPrefix)) {
760
+            return RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
761
+        }
762
+        return new FileInputStream(new File(path));
763
+    }
764
+
765
+    /**
766
+     * Check if the asset or the file exists
767
+     * @param path
768
+     * @return
769
+     */
770
+    static boolean isPathExists(String path) {
771
+        if(path.startsWith(assetPrefix)) {
772
+            try {
773
+                RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
774
+            } catch (IOException e) {
775
+                return false;
776
+            }
777
+            return true;
778
+        }
779
+        else {
780
+            return new File(path).exists();
781
+        }
782
+
783
+    }
784
+
785
+    static boolean isAsset(String path) {
786
+        return path.startsWith(assetPrefix);
787
+    }
788
+
789
+    static String normalizePath(String path) {
790
+        if(path.startsWith(assetPrefix)) {
791
+            return path;
792
+        }
793
+        else if (path.startsWith("content://")) {
794
+            String[] proj = { MediaStore.Images.Media.DATA };
795
+            Uri contentUri = Uri.parse(path);
796
+            CursorLoader loader = new CursorLoader(RNFetchBlob.RCTContext, contentUri, proj, null, null, null);
797
+            Cursor cursor = loader.loadInBackground();
798
+            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
799
+            cursor.moveToFirst();
800
+            String result = cursor.getString(column_index);
801
+            cursor.close();
802
+            return result;
676
         }
803
         }
677
-        WritableMap stat = Arguments.createMap();
678
-        stat.putString("filename", target.getName());
679
-        stat.putString("path", target.getPath());
680
-        stat.putString("type", target.isDirectory() ? "directory" : "file");
681
-        stat.putInt("size", (int)target.length());
682
-        stat.putInt("lastModified", (int)target.lastModified());
683
-        return stat;
804
+        return path;
684
     }
805
     }
685
 
806
 
686
 }
807
 }

+ 45
- 5
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java Datei anzeigen

19
 import com.loopj.android.http.MySSLSocketFactory;
19
 import com.loopj.android.http.MySSLSocketFactory;
20
 
20
 
21
 import java.io.File;
21
 import java.io.File;
22
+import java.io.IOException;
23
+import java.io.InputStream;
22
 import java.security.KeyStore;
24
 import java.security.KeyStore;
23
 
25
 
24
 import cz.msebera.android.httpclient.HttpEntity;
26
 import cz.msebera.android.httpclient.HttpEntity;
111
 
113
 
112
             req = new AsyncHttpClient();
114
             req = new AsyncHttpClient();
113
 
115
 
116
+            req.setLoggingEnabled(false);
117
+
114
             // use trusty SSL socket
118
             // use trusty SSL socket
115
             if(this.options.trusty) {
119
             if(this.options.trusty) {
116
                 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
120
                 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
196
                 String data = map.getString("data");
200
                 String data = map.getString("data");
197
                 // file field
201
                 // file field
198
                 if(map.hasKey("filename")) {
202
                 if(map.hasKey("filename")) {
203
+                    String mime = map.hasKey("type") ? map.getString("type") : ContentType.APPLICATION_OCTET_STREAM.getMimeType();
199
                     String filename = map.getString("filename");
204
                     String filename = map.getString("filename");
200
                     // upload from storage
205
                     // upload from storage
201
                     if(data.startsWith(filePathPrefix)) {
206
                     if(data.startsWith(filePathPrefix)) {
202
-                        File file = new File(data.substring(filePathPrefix.length()));
203
-                        form.addBinaryBody(name, file, ContentType.APPLICATION_OCTET_STREAM, filename);
207
+                        String orgPath = data.substring(filePathPrefix.length());
208
+                        orgPath = RNFetchBlobFS.normalizePath(orgPath);
209
+                        // path starts with content://
210
+                        if(RNFetchBlobFS.isAsset(orgPath)) {
211
+                            try {
212
+                                String assetName = orgPath.replace(RNFetchBlobFS.assetPrefix, "");
213
+                                InputStream in = RNFetchBlob.RCTContext.getAssets().open(assetName);
214
+                                long length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
215
+                                byte [] bytes = new byte[(int) length];
216
+                                in.read(bytes, 0, (int) length);
217
+                                in.close();
218
+                                form.addBinaryBody(name, bytes, ContentType.create(mime), filename);
219
+                            } catch (IOException e) {
220
+//                                e.printStackTrace();
221
+                            }
222
+                        }
223
+                        else {
224
+                            File file = new File(RNFetchBlobFS.normalizePath(orgPath));
225
+                            form.addBinaryBody(name, file, ContentType.create(mime), filename);
226
+                        }
204
                     }
227
                     }
205
                     // base64 embedded file content
228
                     // base64 embedded file content
206
                     else {
229
                     else {
207
-                        form.addBinaryBody(name, Base64.decode(data, 0), ContentType.APPLICATION_OCTET_STREAM, filename);
230
+                        form.addBinaryBody(name, Base64.decode(data, 0), ContentType.create(mime), filename);
208
                     }
231
                     }
209
                 }
232
                 }
210
                 // data field
233
                 // data field
227
             byte [] blob;
250
             byte [] blob;
228
             // upload from storage
251
             // upload from storage
229
             if(body.startsWith(filePathPrefix)) {
252
             if(body.startsWith(filePathPrefix)) {
230
-                String filePath = body.substring(filePathPrefix.length());
231
-                entity = new FileEntity(new File(filePath));
253
+                String orgPath = body.substring(filePathPrefix.length());
254
+                orgPath = RNFetchBlobFS.normalizePath(orgPath);
255
+                // handle
256
+                if(RNFetchBlobFS.isAsset(orgPath)) {
257
+                    try {
258
+                        String assetName = orgPath.replace(RNFetchBlobFS.assetPrefix, "");
259
+                        InputStream in = RNFetchBlob.RCTContext.getAssets().open(assetName);
260
+                        long length = 0;
261
+                        length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
262
+                        byte [] bytes = new byte[(int) length];
263
+                        in.read(bytes, 0, (int) length);
264
+                        in.close();
265
+                        entity = new ByteArrayEntity(bytes);
266
+                    } catch (IOException e) {
267
+//                        e.printStackTrace();
268
+                    }
269
+                }
270
+                else
271
+                    entity = new FileEntity(new File(RNFetchBlobFS.normalizePath(orgPath)));
232
             }
272
             }
233
             else {
273
             else {
234
                 blob = Base64.decode(body, 0);
274
                 blob = Base64.decode(body, 0);

+ 12
- 1
src/fs.js Datei anzeigen

8
 import {
8
 import {
9
   NativeModules,
9
   NativeModules,
10
   DeviceEventEmitter,
10
   DeviceEventEmitter,
11
+  Platform,
11
   NativeAppEventEmitter,
12
   NativeAppEventEmitter,
12
 } from 'react-native'
13
 } from 'react-native'
13
 import RNFetchBlobSession from './class/RNFetchBlobSession'
14
 import RNFetchBlobSession from './class/RNFetchBlobSession'
47
   }
48
   }
48
 }
49
 }
49
 
50
 
51
+function asset(path:string):string {
52
+  if(Platform.OS === 'ios') {
53
+    // path from camera roll
54
+    if(/^assets-library\:\/\//.test(path))
55
+      return path
56
+  }
57
+  return 'bundle-assets://' + path
58
+}
59
+
50
 function createFile(path:string, data:string, encoding: 'base64' | 'ascii' | 'utf8'):Promise {
60
 function createFile(path:string, data:string, encoding: 'base64' | 'ascii' | 'utf8'):Promise {
51
   encoding = encoding || 'utf8'
61
   encoding = encoding || 'utf8'
52
   return new Promise((resolve, reject) => {
62
   return new Promise((resolve, reject) => {
324
   stat,
334
   stat,
325
   lstat,
335
   lstat,
326
   scanFile,
336
   scanFile,
327
-  dirs
337
+  dirs,
338
+  asset
328
 }
339
 }

+ 1
- 1
src/index.js Datei anzeigen

272
   config,
272
   config,
273
   session,
273
   session,
274
   fs,
274
   fs,
275
-  wrap,
275
+  wrap
276
 }
276
 }

+ 6
- 0
src/ios/RNFetchBlob.xcodeproj/project.pbxproj Datei anzeigen

12
 		A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */; };
12
 		A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */; };
13
 		A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = A15C30131CD25C330074CB35 /* RNFetchBlob.m */; };
13
 		A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = A15C30131CD25C330074CB35 /* RNFetchBlob.m */; };
14
 		A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */ = {isa = PBXBuildFile; fileRef = A15C30111CD25C330074CB35 /* RNFetchBlob.h */; };
14
 		A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */ = {isa = PBXBuildFile; fileRef = A15C30111CD25C330074CB35 /* RNFetchBlob.h */; };
15
+		A1AAE2991D300E4D0051D11C /* RNFetchBlobReqBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */; };
15
 /* End PBXBuildFile section */
16
 /* End PBXBuildFile section */
16
 
17
 
17
 /* Begin PBXCopyFilesBuildPhase section */
18
 /* Begin PBXCopyFilesBuildPhase section */
36
 		A15C300E1CD25C330074CB35 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFetchBlob.a; sourceTree = BUILT_PRODUCTS_DIR; };
37
 		A15C300E1CD25C330074CB35 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFetchBlob.a; sourceTree = BUILT_PRODUCTS_DIR; };
37
 		A15C30111CD25C330074CB35 /* RNFetchBlob.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFetchBlob.h; path = RNFetchBlob/RNFetchBlob.h; sourceTree = "<group>"; };
38
 		A15C30111CD25C330074CB35 /* RNFetchBlob.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFetchBlob.h; path = RNFetchBlob/RNFetchBlob.h; sourceTree = "<group>"; };
38
 		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>"; };
41
+		A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobReqBuilder.m; sourceTree = "<group>"; };
39
 /* End PBXFileReference section */
42
 /* End PBXFileReference section */
40
 
43
 
41
 /* Begin PBXFrameworksBuildPhase section */
44
 /* Begin PBXFrameworksBuildPhase section */
59
 		A15C30051CD25C330074CB35 = {
62
 		A15C30051CD25C330074CB35 = {
60
 			isa = PBXGroup;
63
 			isa = PBXGroup;
61
 			children = (
64
 			children = (
65
+				A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */,
66
+				A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */,
62
 				A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */,
67
 				A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */,
63
 				A158F42E1D0539CE006FFD38 /* RNFetchBlobNetwork.h */,
68
 				A158F42E1D0539CE006FFD38 /* RNFetchBlobNetwork.h */,
64
 				A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */,
69
 				A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */,
140
 				A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */,
145
 				A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */,
141
 				A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */,
146
 				A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */,
142
 				A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */,
147
 				A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */,
148
+				A1AAE2991D300E4D0051D11C /* RNFetchBlobReqBuilder.m in Sources */,
143
 				A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */,
149
 				A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */,
144
 			);
150
 			);
145
 			runOnlyForDeploymentPostprocessing = 0;
151
 			runOnlyForDeploymentPostprocessing = 0;

+ 37
- 121
src/ios/RNFetchBlob/RNFetchBlob.m Datei anzeigen

12
 #import "RNFetchBlobFS.h"
12
 #import "RNFetchBlobFS.h"
13
 #import "RNFetchBlobNetwork.h"
13
 #import "RNFetchBlobNetwork.h"
14
 #import "RNFetchBlobConst.h"
14
 #import "RNFetchBlobConst.h"
15
+#import "RNFetchBlobReqBuilder.h"
15
 
16
 
16
 
17
 
17
 ////////////////////////////////////////
18
 ////////////////////////////////////////
61
                   form:(NSArray *)form
62
                   form:(NSArray *)form
62
                   callback:(RCTResponseSenderBlock)callback)
63
                   callback:(RCTResponseSenderBlock)callback)
63
 {
64
 {
64
-    NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
65
-    // send request
66
-    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
67
-                                    initWithURL:[NSURL
68
-                                                 URLWithString: encodedUrl]];
69
     
65
     
70
-    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[ RNFetchBlobNetwork normalizeHeaders:headers]];
71
-    
72
-    
73
-    NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
74
-    NSNumber * timeStampObj = [NSNumber numberWithDouble: timeStamp];
75
-    
76
-    // generate boundary
77
-    NSString * boundary = [NSString stringWithFormat:@"RNFetchBlob%d", timeStampObj];
78
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
79
-        NSMutableData * postData = [[NSMutableData alloc] init];
80
-        // if method is POST or PUT, convert data string format
81
-        if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"]) {
82
-            
83
-            // combine multipart/form-data body
84
-            for(id field in form) {
85
-                NSString * name = [field valueForKey:@"name"];
86
-                NSString * content = [field valueForKey:@"data"];
87
-                // field is a text field
88
-                if([field valueForKey:@"filename"] == nil || content == [NSNull null]) {
89
-                    [postData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
90
-                    [postData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", name] dataUsingEncoding:NSUTF8StringEncoding]];
91
-                    [postData appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
92
-                    [postData appendData:[[NSString stringWithFormat:@"%@\r\n", content] dataUsingEncoding:NSUTF8StringEncoding]];
93
-                }
94
-                // field contains a file
95
-                else {
96
-                    NSMutableData * blobData;
97
-                    if(content != nil) {
98
-                        if([content hasPrefix:self.filePathPrefix]) {
99
-                            NSString * orgPath = [content substringFromIndex:[self.filePathPrefix length]];
100
-                            blobData = [[NSData alloc] initWithContentsOfFile:orgPath];
101
-                        }
102
-                        else
103
-                            blobData = [[NSData alloc] initWithBase64EncodedString:content options:0];
104
-                    }
105
-                    NSString * filename = [field valueForKey:@"filename"];
106
-                    [postData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
107
-                    [postData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
108
-                    [postData appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
109
-                    [postData appendData:blobData];
110
-                    [postData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
111
-                }
112
-                
113
-            }
114
-            
115
-            // close form data
116
-            [postData appendData: [[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
117
-            [request setHTTPBody:postData];
118
-            // set content-length
119
-            [mheaders setValue:[NSString stringWithFormat:@"%d",[postData length]] forKey:@"Content-Length"];
120
-            [mheaders setValue:[NSString stringWithFormat:@"100-continue",[postData length]] forKey:@"Expect"];
121
-            // appaned boundary to content-type
122
-            [mheaders setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forKey:@"content-type"];
123
-            
124
-        }
125
-        
126
-        [request setHTTPMethod: method];
127
-        [request setAllHTTPHeaderFields:mheaders];
128
-        
129
-        
66
+    [RNFetchBlobReqBuilder buildMultipartRequest:options taskId:taskId method:method url:url headers:headers form:form onComplete:^(NSURLRequest *req, long bodyLength) {
130
         // send HTTP request
67
         // send HTTP request
131
         RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init];
68
         RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init];
132
-        [utils sendRequest:options bridge:self.bridge taskId:taskId withRequest:request callback:callback];
69
+        [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback];
133
         utils = nil;
70
         utils = nil;
134
-    });
71
+    }];
72
+    
135
 }
73
 }
136
 
74
 
137
 // Fetch blob data request
75
 // Fetch blob data request
142
                   headers:(NSDictionary *)headers
80
                   headers:(NSDictionary *)headers
143
                   body:(NSString *)body callback:(RCTResponseSenderBlock)callback)
81
                   body:(NSString *)body callback:(RCTResponseSenderBlock)callback)
144
 {
82
 {
145
-    NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
146
-    // send request
147
-    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
148
-                                    initWithURL:[NSURL
149
-                                                 URLWithString: encodedUrl]];
150
-    
151
-    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[RNFetchBlobNetwork normalizeHeaders:headers]];
152
-    // move heavy task to another thread
153
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
154
-        NSMutableData * blobData;
155
-        // if method is POST or PUT, convert data string format
156
-        if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"]) {
157
-            // generate octet-stream body
158
-            if(body != nil) {
159
-                
160
-                // when body is a string contains file path prefix, try load file from the path
161
-                if([body hasPrefix:self.filePathPrefix]) {
162
-                    NSString * orgPath = [body substringFromIndex:[self.filePathPrefix length]];
163
-                    [request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
164
-                }
165
-                // otherwise convert it as BASE64 data string
166
-                else {
167
-                    blobData = [[NSData alloc] initWithBase64EncodedString:body options:0];
168
-                    [request setHTTPBody:blobData];
169
-                }
170
-                
171
-                [mheaders setValue:@"application/octet-stream" forKey:@"content-type"];
172
-                
173
-            }
174
-        }
175
-        
176
-        [request setHTTPMethod: method];
177
-        [request setAllHTTPHeaderFields:mheaders];
178
-        
83
+    [RNFetchBlobReqBuilder buildOctetRequest:options taskId:taskId method:method url:url headers:headers body:body onComplete:^(NSURLRequest *req, long bodyLength) {
179
         // send HTTP request
84
         // send HTTP request
180
         RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init];
85
         RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init];
181
-        [utils sendRequest:options bridge:self.bridge taskId:taskId withRequest:request callback:callback];
86
+        [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback];
182
         utils = nil;
87
         utils = nil;
183
-    });
88
+    }];
89
+    
184
 }
90
 }
185
 
91
 
186
 RCT_EXPORT_METHOD(createFile:(NSString *)path data:(NSString *)data encoding:(NSString *)encoding callback:(RCTResponseSenderBlock)callback) {
92
 RCT_EXPORT_METHOD(createFile:(NSString *)path data:(NSString *)data encoding:(NSString *)encoding callback:(RCTResponseSenderBlock)callback) {
236
     
142
     
237
 }
143
 }
238
 
144
 
239
-RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
240
-    RNFetchBlobFS *fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
241
-    if(bufferSize == nil) {
242
-        if([[encoding lowercaseString] isEqualToString:@"base64"])
243
-            bufferSize = 4095;
244
-        else
245
-            bufferSize = 4096;
246
-    }
247
-    [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
248
-}
145
+RCT_EXPORT_METHOD(writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
146
+    [RNFetchBlobFS writeFile:path encoding:encoding data:data append:append resolver:resolve rejecter:reject];
147
+})
148
+
149
+RCT_EXPORT_METHOD(writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
150
+    [RNFetchBlobFS writeFileArray:path data:data append:append resolver:resolve rejecter:reject];
151
+})
249
 
152
 
250
 RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding appendData:(BOOL)append callback:(RCTResponseSenderBlock)callback) {
153
 RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding appendData:(BOOL)append callback:(RCTResponseSenderBlock)callback) {
251
     RNFetchBlobFS * fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
154
     RNFetchBlobFS * fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
335
     BOOL exist = nil;
238
     BOOL exist = nil;
336
     BOOL isDir = nil;
239
     BOOL isDir = nil;
337
     NSError * error = nil;
240
     NSError * error = nil;
241
+    
242
+    path = [RNFetchBlobFS getPathOfAsset:path];
243
+    
338
     exist = [fm fileExistsAtPath:path isDirectory:&isDir];
244
     exist = [fm fileExistsAtPath:path isDirectory:&isDir];
339
     if(exist == NO) {
245
     if(exist == NO) {
340
         callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not exist", path]]);
246
         callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not exist", path]]);
353
     NSFileManager* fm = [NSFileManager defaultManager];
259
     NSFileManager* fm = [NSFileManager defaultManager];
354
     BOOL exist = nil;
260
     BOOL exist = nil;
355
     BOOL isDir = nil;
261
     BOOL isDir = nil;
262
+    
263
+    path = [RNFetchBlobFS getPathOfAsset:path];
264
+    
356
     exist = [fm fileExistsAtPath:path isDirectory:&isDir];
265
     exist = [fm fileExistsAtPath:path isDirectory:&isDir];
357
     if(exist == NO) {
266
     if(exist == NO) {
358
         callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not exist", path]]);
267
         callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not exist", path]]);
381
 
290
 
382
 RCT_EXPORT_METHOD(cp:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
291
 RCT_EXPORT_METHOD(cp:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
383
     NSError * error = nil;
292
     NSError * error = nil;
293
+    path = [RNFetchBlobFS getPathOfAsset:path];
384
     BOOL result = [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
294
     BOOL result = [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
385
     
295
     
386
     if(error == nil)
296
     if(error == nil)
412
 }
322
 }
413
 
323
 
414
 RCT_EXPORT_METHOD(readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
324
 RCT_EXPORT_METHOD(readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
415
-    [RNFetchBlobFS readFile:path encoding:encoding resolver:resolve rejecter:reject];
416
-})
417
-
418
-RCT_EXPORT_METHOD(writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
419
-    [RNFetchBlobFS writeFile:path encoding:encoding data:data append:append resolver:resolve rejecter:reject];
325
+    
326
+    [RNFetchBlobFS readFile:path encoding:encoding resolver:resolve rejecter:reject onComplete:nil];
420
 })
327
 })
421
 
328
 
422
-RCT_EXPORT_METHOD(writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
423
-    [RNFetchBlobFS writeFileArray:path data:data append:append resolver:resolve rejecter:reject];
424
-})
329
+RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
330
+    
331
+    RNFetchBlobFS *fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
332
+    if(bufferSize == nil) {
333
+        if([[encoding lowercaseString] isEqualToString:@"base64"])
334
+            bufferSize = 4095;
335
+        else
336
+            bufferSize = 4096;
337
+    }
338
+    // read asset stream
339
+    [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
340
+}
425
 
341
 
426
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
342
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
427
     
343
     

+ 3
- 0
src/ios/RNFetchBlobConst.h Datei anzeigen

16
 extern NSString *const MSG_EVENT_LOG;
16
 extern NSString *const MSG_EVENT_LOG;
17
 extern NSString *const MSG_EVENT_WARN;
17
 extern NSString *const MSG_EVENT_WARN;
18
 extern NSString *const MSG_EVENT_ERROR;
18
 extern NSString *const MSG_EVENT_ERROR;
19
+
19
 extern NSString *const FILE_PREFIX;
20
 extern NSString *const FILE_PREFIX;
21
+extern NSString *const ASSET_PREFIX;
22
+extern NSString *const AL_PREFIX;
20
 
23
 
21
 // config
24
 // config
22
 extern NSString *const CONFIG_USE_TEMP;
25
 extern NSString *const CONFIG_USE_TEMP;

+ 4
- 0
src/ios/RNFetchBlobConst.m Datei anzeigen

8
 #import "RNFetchBlobConst.h"
8
 #import "RNFetchBlobConst.h"
9
 
9
 
10
 extern NSString *const FILE_PREFIX = @"RNFetchBlob-file://";
10
 extern NSString *const FILE_PREFIX = @"RNFetchBlob-file://";
11
+extern NSString *const ASSET_PREFIX = @"bundle-assets://";
12
+extern NSString *const AL_PREFIX = @"assets-library://";
13
+
14
+
11
 
15
 
12
 // fetch configs
16
 // fetch configs
13
 extern NSString *const CONFIG_USE_TEMP = @"fileCache";
17
 extern NSString *const CONFIG_USE_TEMP = @"fileCache";

+ 6
- 1
src/ios/RNFetchBlobFS.h Datei anzeigen

11
 
11
 
12
 #import <Foundation/Foundation.h>
12
 #import <Foundation/Foundation.h>
13
 #import "RCTBridgeModule.h"
13
 #import "RCTBridgeModule.h"
14
+@import AssetsLibrary;
14
 
15
 
15
 @interface RNFetchBlobFS : NSObject <NSStreamDelegate>  {
16
 @interface RNFetchBlobFS : NSObject <NSStreamDelegate>  {
16
     NSOutputStream * outStream;
17
     NSOutputStream * outStream;
42
 + (NSString *) getCacheDir;
43
 + (NSString *) getCacheDir;
43
 + (NSString *) getDocumentDir;
44
 + (NSString *) getDocumentDir;
44
 + (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext;
45
 + (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext;
46
++ (NSString *) getPathOfAsset:(NSString *)assetURI;
47
++ (void) getPathFromUri:(NSString *)uri completionHandler:(void(^)(NSString * path, ALAssetRepresentation *asset)) onComplete;
45
 
48
 
46
 // fs methods
49
 // fs methods
47
 + (RNFetchBlobFS *) getFileStreams;
50
 + (RNFetchBlobFS *) getFileStreams;
50
 + (BOOL) exists:(NSString *) path;
53
 + (BOOL) exists:(NSString *) path;
51
 + (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;
52
 + (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;
53
-+ (void) readFile:(NSString *)path encoding:(NSString *)encoding 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;
57
++ (void) readAssetFile:(NSData *)assetUrl completionBlock:(void(^)(NSData * content))completionBlock failBlock:(void(^)(NSError * err))failBlock;
54
 
58
 
55
 // constructor
59
 // constructor
56
 - (id) init;
60
 - (id) init;
61
 - (void) openWithDestination;
65
 - (void) openWithDestination;
62
 - (void) openWithId;
66
 - (void) openWithId;
63
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
67
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
68
+- (void) startAssetReadStream:(NSData *)assetUrl;
64
 
69
 
65
 // file stream write data
70
 // file stream write data
66
 - (void)write:(NSData *) chunk;
71
 - (void)write:(NSData *) chunk;

+ 185
- 41
src/ios/RNFetchBlobFS.m Datei anzeigen

14
 #import "RCTEventDispatcher.h"
14
 #import "RCTEventDispatcher.h"
15
 #import "RNFetchBlobFS.h"
15
 #import "RNFetchBlobFS.h"
16
 #import "RNFetchBlobConst.h"
16
 #import "RNFetchBlobConst.h"
17
+@import AssetsLibrary;
18
+
17
 
19
 
18
 NSMutableDictionary *fileStreams = nil;
20
 NSMutableDictionary *fileStreams = nil;
19
 
21
 
49
     [fileStreams setValue:instance forKey:uuid];
51
     [fileStreams setValue:instance forKey:uuid];
50
 }
52
 }
51
 
53
 
54
++(NSString *) getPathOfAsset:(NSString *)assetURI
55
+{
56
+    // get file path of an app asset
57
+    if([assetURI hasPrefix:ASSET_PREFIX])
58
+    {
59
+        assetURI = [assetURI stringByReplacingOccurrencesOfString:ASSET_PREFIX withString:@""];
60
+        assetURI = [[NSBundle mainBundle] pathForResource: [assetURI stringByDeletingPathExtension]
61
+                                               ofType: [assetURI pathExtension]];
62
+    }
63
+    return assetURI;
64
+}
65
+
52
 + (NSString *) getCacheDir {
66
 + (NSString *) getCacheDir {
53
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
67
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
54
 }
68
 }
84
     return tempPath;
98
     return tempPath;
85
 }
99
 }
86
 
100
 
101
+- (void) startAssetReadStream:(NSString *)assetUrl
102
+{
103
+    ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
104
+    {
105
+        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
106
+        dispatch_async(queue, ^ {
107
+            NSString * streamEventCode = [NSString stringWithFormat:@"RNFetchBlobStream+%@", self.path];
108
+            ALAssetRepresentation *rep = [myasset defaultRepresentation];
109
+            Byte *buffer = (Byte*)malloc(self.bufferSize);
110
+            NSUInteger cursor = [rep getBytes:buffer fromOffset:0 length:self.bufferSize error:nil];
111
+            while(cursor > 0)
112
+            {
113
+                cursor += [rep getBytes:buffer fromOffset:cursor length:self.bufferSize error:nil];
114
+                NSData * chunkData = [NSData dataWithBytes:buffer length:self.bufferSize];
115
+                NSString * encodedChunk = @"";
116
+                // emit data
117
+                if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
118
+                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
119
+                }
120
+                // when encoding is ASCII, send byte array data
121
+                else if ( [[self.encoding lowercaseString] isEqualToString:@"ascii"] ) {
122
+                    // RCTBridge only emits string data, so we have to create JSON byte array string
123
+                    NSMutableArray * asciiArray = [NSMutableArray array];
124
+                    unsigned char *bytePtr;
125
+                    if (chunkData.length > 0)
126
+                    {
127
+                        bytePtr = (unsigned char *)[chunkData bytes];
128
+                        NSInteger byteLen = chunkData.length/sizeof(uint8_t);
129
+                        for (int i = 0; i < byteLen; i++)
130
+                        {
131
+                            [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
132
+                        }
133
+                    }
134
+                    
135
+                    [self.bridge.eventDispatcher
136
+                     sendDeviceEventWithName:streamEventCode
137
+                     body: @{
138
+                             @"event": FS_EVENT_DATA,
139
+                             @"detail": asciiArray
140
+                             }
141
+                     ];
142
+                    return;
143
+                }
144
+                // convert byte array to base64 data chunks
145
+                else if ( [[self.encoding lowercaseString] isEqualToString:@"base64"] ) {
146
+                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
147
+                }
148
+                // unknown encoding, send error event
149
+                else {
150
+                    [self.bridge.eventDispatcher
151
+                     sendDeviceEventWithName:streamEventCode
152
+                     body:@{
153
+                            @"event": FS_EVENT_ERROR,
154
+                            @"detail": @"unrecognized encoding"
155
+                            }
156
+                     ];
157
+                    return;
158
+                }
159
+                
160
+                [self.bridge.eventDispatcher
161
+                 sendDeviceEventWithName:streamEventCode
162
+                 body:@{
163
+                        @"event": FS_EVENT_DATA,
164
+                        @"detail": encodedChunk
165
+                        }
166
+                 ];
167
+            }
168
+            free(buffer);
169
+        });
170
+        
171
+    };
172
+    
173
+    ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *error)
174
+    {
175
+        
176
+    };
177
+    
178
+    if(assetUrl && [assetUrl length])
179
+    {
180
+        NSURL *asseturl = [NSURL URLWithString:assetUrl];
181
+        ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
182
+        [assetslibrary assetForURL:asseturl
183
+                       resultBlock:resultblock
184
+                      failureBlock:failureblock];
185
+    }
186
+}
187
+
87
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
188
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
88
     @try {
189
     @try {
89
         NSFileManager * fm = [NSFileManager defaultManager];
190
         NSFileManager * fm = [NSFileManager defaultManager];
172
     }
273
     }
173
 }
274
 }
174
 
275
 
175
-+ (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
276
++ (void) readFile:(NSString *)path encoding:(NSString *)encoding
277
+         resolver:(RCTPromiseResolveBlock)resolve
278
+         rejecter:(RCTPromiseRejectBlock)reject
279
+       onComplete:(void (^)(NSData * content))onComplete
280
+{
176
     @try
281
     @try
177
     {
282
     {
178
-        NSFileManager * fm = [NSFileManager defaultManager];
179
-        NSError *err = nil;
180
-        BOOL exists = [fm fileExistsAtPath:path];
181
-        if(!exists) {
182
-            @throw @"RNFetchBlobFS readFile error", @"file not exists", path;
183
-            return;
184
-        }
185
-        if([[encoding lowercaseString] isEqualToString:@"utf8"]) {
186
-            NSString * utf8Result = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err];
187
-            resolve(utf8Result);
188
-        }
189
-        else if ([[encoding lowercaseString] isEqualToString:@"base64"]) {
190
-            NSData * fileData = [NSData dataWithContentsOfFile:path];
191
-            resolve([fileData base64EncodedStringWithOptions:0]);
192
-        }
193
-        else if ([[encoding lowercaseString] isEqualToString:@"ascii"]) {
194
-            NSData * resultData = [NSData dataWithContentsOfFile:path];
195
-            NSMutableArray * resultArray = [NSMutableArray array];
196
-            char * bytes = [resultData bytes];
197
-            for(int i=0;i<[resultData length];i++) {
198
-                [resultArray addObject:[NSNumber numberWithChar:bytes[i]]];
283
+        [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
284
+            NSData * fileContent;
285
+            NSError * err;
286
+            Byte * buffer;
287
+            if(asset != nil)
288
+            {
289
+                buffer = malloc(asset.size);
290
+                [asset getBytes:buffer fromOffset:0 length:asset.size error:&err];
291
+                if(err != nil)
292
+                {
293
+                    reject(@"RNFetchBlobFS readFile error", @"failed to read asset", [err localizedDescription]);
294
+                    return;
295
+                }
296
+                fileContent = [NSData dataWithBytes:buffer length:asset.size];
297
+                free(buffer);
199
             }
298
             }
200
-            resolve(resultArray);
201
-        }
202
-
299
+            else
300
+            {
301
+                BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path];
302
+                if(!exists) {
303
+                    reject(@"RNFetchBlobFS readFile error", @"file not exists", path);
304
+                    return;
305
+                }
306
+                fileContent = [NSData dataWithContentsOfFile:path];
307
+                
308
+            }
309
+            if(onComplete != nil)
310
+                onComplete(fileContent);
311
+            
312
+            if([[encoding lowercaseString] isEqualToString:@"utf8"]) {
313
+                if(resolve != nil)
314
+                    resolve([[NSString alloc] initWithData:fileContent encoding:NSUTF8StringEncoding]);
315
+            }
316
+            else if ([[encoding lowercaseString] isEqualToString:@"base64"]) {
317
+                if(resolve != nil)
318
+                    resolve([fileContent base64EncodedStringWithOptions:0]);
319
+            }
320
+            else if ([[encoding lowercaseString] isEqualToString:@"ascii"]) {
321
+                NSMutableArray * resultArray = [NSMutableArray array];
322
+                char * bytes = [fileContent bytes];
323
+                for(int i=0;i<[fileContent length];i++) {
324
+                    [resultArray addObject:[NSNumber numberWithChar:bytes[i]]];
325
+                }
326
+                if(resolve != nil)
327
+                    resolve(resultArray);
328
+            }
329
+        }];
203
     }
330
     }
204
     @catch(NSException * e)
331
     @catch(NSException * e)
205
     {
332
     {
206
-        reject(@"RNFetchBlobFS readFile error", @"error", [e description]);
333
+        if(reject != nil)
334
+            reject(@"RNFetchBlobFS readFile error", @"error", [e description]);
207
     }
335
     }
208
 }
336
 }
209
 
337
 
325
     self.encoding = encoding;
453
     self.encoding = encoding;
326
     self.path = path;
454
     self.path = path;
327
     self.bufferSize = bufferSize;
455
     self.bufferSize = bufferSize;
328
-
456
+    
457
+    if([path hasPrefix:AL_PREFIX])
458
+    {
459
+        [self startAssetReadStream:path];
460
+        return;
461
+    }
462
+    
463
+    // normalize file path
464
+    path = [[self class] getPathOfAsset:path];
465
+    
329
     // NSStream needs a runloop so let's create a run loop for it
466
     // NSStream needs a runloop so let's create a run loop for it
330
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
467
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
331
     // start NSStream is a runloop
468
     // start NSStream is a runloop
334
                             forMode:NSDefaultRunLoopMode];
471
                             forMode:NSDefaultRunLoopMode];
335
         [inStream open];
472
         [inStream open];
336
         [[NSRunLoop currentRunLoop] run];
473
         [[NSRunLoop currentRunLoop] run];
337
-
474
+        
338
     });
475
     });
339
 }
476
 }
340
 
477
 
349
 
486
 
350
 }
487
 }
351
 
488
 
352
-void runOnMainQueueWithoutDeadlocking(void (^block)(void))
353
-{
354
-    if ([NSThread isMainThread])
355
-    {
356
-        block();
357
-    }
358
-    else
359
-    {
360
-        dispatch_sync(dispatch_get_main_queue(), block);
361
-    }
362
-}
363
-
364
-
365
 #pragma mark RNFetchBlobFS read stream delegate
489
 #pragma mark RNFetchBlobFS read stream delegate
366
 
490
 
367
 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
491
 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
475
 
599
 
476
 }
600
 }
477
 
601
 
602
++ (void) getPathFromUri:(NSString *)uri completionHandler:(void(^)(NSString * path, ALAssetRepresentation *asset)) onComplete
603
+{
604
+    if([uri hasPrefix:AL_PREFIX])
605
+    {
606
+        NSURL *asseturl = [NSURL URLWithString:uri];
607
+        ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
608
+        [assetslibrary assetForURL:asseturl
609
+                       resultBlock:^(ALAsset *asset) {
610
+                           onComplete(nil, [asset defaultRepresentation]);
611
+                       }
612
+                      failureBlock:^(NSError *error) {
613
+                          onComplete(nil, nil);
614
+                      }];
615
+    }
616
+    else
617
+    {
618
+        onComplete([[self class] getPathOfAsset:uri], nil);
619
+    }
620
+}
621
+
478
 @end
622
 @end

+ 2
- 1
src/ios/RNFetchBlobNetwork.h Datei anzeigen

11
 
11
 
12
 #import <Foundation/Foundation.h>
12
 #import <Foundation/Foundation.h>
13
 #import "RCTBridgeModule.h"
13
 #import "RCTBridgeModule.h"
14
+#import "RNFetchBlobFS.h"
14
 
15
 
15
 typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error);
16
 typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error);
16
 typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse * _Nullable response, NSError * _Nullable error);
17
 typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse * _Nullable response, NSError * _Nullable error);
34
 - (void) sendRequest;
35
 - (void) sendRequest;
35
 
36
 
36
 + (NSMutableDictionary  * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
37
 + (NSMutableDictionary  * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
37
-- (void) sendRequest:(NSDictionary  * _Nullable )options bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
38
+- (void) sendRequest:(NSDictionary  * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
38
 
39
 
39
 
40
 
40
 @end
41
 @end

+ 9
- 3
src/ios/RNFetchBlobNetwork.m Datei anzeigen

69
 }
69
 }
70
 
70
 
71
 // send HTTP request
71
 // send HTTP request
72
-- (void) sendRequest:(NSDictionary  * _Nullable )options bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback
72
+- (void) sendRequest:(NSDictionary  * _Nullable )options
73
+       contentLength:(long) contentLength
74
+              bridge:(RCTBridge * _Nullable)bridgeRef
75
+              taskId:(NSString * _Nullable)taskId
76
+         withRequest:(NSURLRequest * _Nullable)req
77
+            callback:(_Nullable RCTResponseSenderBlock) callback
73
 {
78
 {
74
     self.taskId = taskId;
79
     self.taskId = taskId;
75
     self.respData = [[NSMutableData alloc] initWithLength:0];
80
     self.respData = [[NSMutableData alloc] initWithLength:0];
83
     NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
88
     NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
84
     NSURLSession * session;
89
     NSURLSession * session;
85
     
90
     
86
-    bodyLength = [[req HTTPBody] length];
91
+    bodyLength = contentLength;
87
     
92
     
88
     // the session trust any SSL certification
93
     // the session trust any SSL certification
89
 
94
 
104
         respFile = NO;
109
         respFile = NO;
105
     }
110
     }
106
     NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
111
     NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
112
+    
107
     [task resume];
113
     [task resume];
108
     
114
     
109
     // network status indicator
115
     // network status indicator
191
      body:@{
197
      body:@{
192
             @"taskId": taskId,
198
             @"taskId": taskId,
193
             @"written": [NSString stringWithFormat:@"%d", totalBytesWritten],
199
             @"written": [NSString stringWithFormat:@"%d", totalBytesWritten],
194
-            @"total": [NSString stringWithFormat:@"%d", totalBytesExpectedToWrite]
200
+            @"total": [NSString stringWithFormat:@"%d", bodyLength]
195
             }
201
             }
196
      ];
202
      ];
197
 }
203
 }

+ 34
- 0
src/ios/RNFetchBlobReqBuilder.h Datei anzeigen

1
+//
2
+//  RNFetchBlobReqBuilder.h
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/7/9.
6
+//  Copyright © 2016年 wkh237. All rights reserved.
7
+//
8
+
9
+#ifndef RNFetchBlobReqBuilder_h
10
+#define RNFetchBlobReqBuilder_h
11
+
12
+#import <Foundation/Foundation.h>
13
+
14
+@interface RNFetchBlobReqBuilder : NSObject;
15
+
16
++(void) buildMultipartRequest:(NSDictionary *)options
17
+                       taskId:(NSString *)taskId
18
+                       method:(NSString *)method
19
+                          url:(NSString *)url
20
+                      headers:(NSDictionary *)headers
21
+                         form:(NSArray *)form
22
+                   onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete;
23
+
24
++(void) buildOctetRequest:(NSDictionary *)options
25
+                   taskId:(NSString *)taskId
26
+                   method:(NSString *)method
27
+                      url:(NSString *)url
28
+                  headers:(NSDictionary *)headers
29
+                     body:(NSString *)body
30
+               onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete;
31
+
32
+@end
33
+
34
+#endif /* RNFetchBlobReqBuilder_h */

+ 199
- 0
src/ios/RNFetchBlobReqBuilder.m Datei anzeigen

1
+//
2
+//  RNFetchBlobReqBuilder.m
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/7/9.
6
+//  Copyright © 2016年 wkh237. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RNFetchBlobReqBuilder.h"
11
+#import "RNFetchBlobNetwork.h"
12
+#import "RNFetchBlobConst.h"
13
+#import "RNFetchBlobFS.h"
14
+
15
+@interface RNFetchBlobReqBuilder()
16
+{
17
+    
18
+}
19
+@end
20
+
21
+@implementation RNFetchBlobReqBuilder
22
+
23
+
24
+// Fetch blob data request
25
++(void) buildMultipartRequest:(NSDictionary *)options
26
+                       taskId:(NSString *)taskId
27
+                       method:(NSString *)method
28
+                          url:(NSString *)url
29
+                      headers:(NSDictionary *)headers
30
+                         form:(NSArray *)form
31
+                   onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete
32
+{
33
+    NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
34
+    // send request
35
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString: encodedUrl]];
36
+    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[RNFetchBlobNetwork normalizeHeaders:headers]];
37
+    NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
38
+    NSNumber * timeStampObj = [NSNumber numberWithDouble: timeStamp];
39
+    
40
+    // generate boundary
41
+    NSString * boundary = [NSString stringWithFormat:@"RNFetchBlob%d", timeStampObj];
42
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
43
+        NSMutableData * postData = [[NSMutableData alloc] init];
44
+        // combine multipart/form-data body
45
+        [[self class] buildFormBody:form boundary:boundary onComplete:^(NSData *formData) {
46
+            if(formData != nil) {
47
+                [postData appendData:formData];
48
+                // close form data
49
+                [postData appendData: [[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
50
+                [request setHTTPBody:postData];
51
+            }
52
+            // set content-length
53
+            [mheaders setValue:[NSString stringWithFormat:@"%d",[postData length]] forKey:@"Content-Length"];
54
+            [mheaders setValue:[NSString stringWithFormat:@"100-continue",[postData length]] forKey:@"Expect"];
55
+            // appaned boundary to content-type
56
+            [mheaders setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forKey:@"content-type"];
57
+            [request setHTTPMethod: method];
58
+            [request setAllHTTPHeaderFields:mheaders];
59
+            onComplete(request, [formData length]);
60
+        }];
61
+        
62
+    });
63
+}
64
+
65
+// Fetch blob data request
66
++(void) buildOctetRequest:(NSDictionary *)options
67
+                   taskId:(NSString *)taskId
68
+                   method:(NSString *)method
69
+                      url:(NSString *)url
70
+                  headers:(NSDictionary *)headers
71
+                     body:(NSString *)body
72
+               onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete
73
+{
74
+    NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
75
+    // send request
76
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
77
+                                    initWithURL:[NSURL
78
+                                                 URLWithString: encodedUrl]];
79
+    
80
+    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[RNFetchBlobNetwork normalizeHeaders:headers]];
81
+    // move heavy task to another thread
82
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
83
+        NSMutableData * blobData;
84
+        long size = -1;
85
+        // if method is POST or PUT, convert data string format
86
+        if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"]) {
87
+            // generate octet-stream body
88
+            if(body != nil) {
89
+                
90
+                // when body is a string contains file path prefix, try load file from the path
91
+                if([body hasPrefix:FILE_PREFIX]) {
92
+                    NSString * orgPath = [body substringFromIndex:[FILE_PREFIX length]];
93
+                    orgPath = [RNFetchBlobFS getPathOfAsset:orgPath];
94
+                    if([orgPath hasPrefix:AL_PREFIX])
95
+                    {
96
+                        [RNFetchBlobFS readFile:orgPath encoding:@"utf8" resolver:nil rejecter:nil onComplete:^(NSData *content) {
97
+                            [request setHTTPBody:content];
98
+                            [mheaders setValue:@"application/octet-stream" forKey:@"content-type"];
99
+                            [request setHTTPMethod: method];
100
+                            [request setAllHTTPHeaderFields:mheaders];
101
+                            onComplete(request, [content length]);
102
+                        }];
103
+                        return;
104
+                    }
105
+                    size = [[[NSFileManager defaultManager] attributesOfItemAtPath:orgPath error:nil] fileSize];
106
+                    [request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
107
+                }
108
+                // otherwise convert it as BASE64 data string
109
+                else {
110
+                    blobData = [[NSData alloc] initWithBase64EncodedString:body options:0];
111
+                    [request setHTTPBody:blobData];
112
+                }
113
+                
114
+                [mheaders setValue:@"application/octet-stream" forKey:@"content-type"];
115
+                
116
+            }
117
+        }
118
+        
119
+        [request setHTTPMethod: method];
120
+        [request setAllHTTPHeaderFields:mheaders];
121
+        
122
+        onComplete(request, size);
123
+    });
124
+}
125
+
126
++(void) buildFormBody:(NSArray *)form boundary:(NSString *)boundary onComplete:(void(^)(NSData * formData))onComplete
127
+{
128
+    NSMutableData * formData = [[NSMutableData alloc] init];
129
+    if(form == nil)
130
+        onComplete(nil);
131
+    else
132
+    {
133
+        __block int i = 0;
134
+        __block int count = [form count];
135
+        void __block (^getFieldData)(id field) = ^(id field)
136
+        {
137
+            NSString * name = [field valueForKey:@"name"];
138
+            NSString * content = [field valueForKey:@"data"];
139
+            NSString * contentType = [field valueForKey:@"type"];
140
+            contentType = contentType == nil ? @"application/octet-stream" : contentType;
141
+            // field is a text field
142
+            if([field valueForKey:@"filename"] == nil || content == [NSNull null]) {
143
+                [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
144
+                [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", name] dataUsingEncoding:NSUTF8StringEncoding]];
145
+                [formData appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
146
+                [formData appendData:[[NSString stringWithFormat:@"%@\r\n", content] dataUsingEncoding:NSUTF8StringEncoding]];
147
+            }
148
+            // field contains a file
149
+            else {
150
+                NSMutableData * blobData;
151
+                if(content != nil)
152
+                {
153
+                    // append data from file asynchronously
154
+                    if([content hasPrefix:FILE_PREFIX])
155
+                    {
156
+                        NSString * orgPath = [content substringFromIndex:[FILE_PREFIX length]];
157
+                        orgPath = [RNFetchBlobFS getPathOfAsset:orgPath];
158
+                        [RNFetchBlobFS readFile:orgPath encoding:@"utf8" resolver:nil rejecter:nil onComplete:^(NSData *content) {
159
+                            NSString * filename = [field valueForKey:@"filename"];
160
+                            [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
161
+                            [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
162
+                            [formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
163
+                            [formData appendData:content];
164
+                            [formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
165
+                            i++;
166
+                            if(i < count)
167
+                            {
168
+                                getFieldData([form objectAtIndex:i]);
169
+                            }
170
+                            else
171
+                                onComplete(formData);
172
+                        }];
173
+                        return ;
174
+                    }
175
+                    else
176
+                        blobData = [[NSData alloc] initWithBase64EncodedString:content options:0];
177
+                }
178
+                NSString * filename = [field valueForKey:@"filename"];
179
+                [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
180
+                [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
181
+                [formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
182
+                [formData appendData:blobData];
183
+                [formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
184
+            }
185
+            i++;
186
+            if(i < count)
187
+            {
188
+                getFieldData([form objectAtIndex:i]);
189
+            }
190
+            else
191
+                onComplete(formData);
192
+            
193
+        };
194
+        getFieldData([form objectAtIndex:i]);
195
+    }
196
+}
197
+
198
+
199
+@end

+ 1
- 1
src/package.json Datei anzeigen

1
 {
1
 {
2
   "name": "react-native-fetch-blob",
2
   "name": "react-native-fetch-blob",
3
-  "version": "0.6.1",
3
+  "version": "0.6.2",
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": {

+ 12
- 0
test-server/server.js Datei anzeigen

84
   res.send('man')
84
   res.send('man')
85
 })
85
 })
86
 
86
 
87
+app.post('/mime', mimeCheck)
88
+app.put('/mime', mimeCheck)
89
+
90
+function mimeCheck(req, res) {
91
+  console.log(req.files)
92
+  var mimes = []
93
+  for(var i in req.files) {
94
+    mimes.push(req.files[i].mimetype)
95
+  }
96
+  res.send(mimes)
97
+}
98
+
87
 // handle multipart/form-data request
99
 // handle multipart/form-data request
88
 app.post('/upload-form', formUpload)
100
 app.post('/upload-form', formUpload)
89
 
101
 

+ 1
- 0
test.sh Datei anzeigen

33
 fi
33
 fi
34
 # copy js files to test app folder
34
 # copy js files to test app folder
35
 cp -R test/ "${TEST_APP_PATH}/"
35
 cp -R test/ "${TEST_APP_PATH}/"
36
+node -e "var fs=require('fs'); var pkg = JSON.parse(fs.readFileSync('./RNFetchBlobTest/package.json')); pkg.rnpm = {assets : ['assets']}; fs.writeFileSync('./RNFetchBlobTest/package.json', JSON.stringify(pkg, null, 4));"
36
 
37
 
37
 # install module
38
 # install module
38
 cd "${TEST_APP_PATH}"
39
 cd "${TEST_APP_PATH}"

+ 3
- 0
test/assets/test-asset1.json Datei anzeigen

1
+{
2
+  "secret" : "asset#1"
3
+}

BIN
test/assets/test-asset2.png Datei anzeigen


+ 1
- 0
test/index.test.js Datei anzeigen

10
   View,
10
   View,
11
   Platform,
11
   Platform,
12
   ScrollView,
12
   ScrollView,
13
+  CameraRoll,
13
   Image,
14
   Image,
14
 } from 'react-native';
15
 } from 'react-native';
15
 
16
 

+ 174
- 0
test/react-native-testkit/animate-text.js Datei anzeigen

1
+/**
2
+ * @author wkh237
3
+ * @version 0.1.1
4
+ */
5
+
6
+// @flow
7
+
8
+import React, { Component } from 'react';
9
+import {
10
+  Text,
11
+  View
12
+} from 'react-native';
13
+import Timer from 'react-timer-mixin';
14
+
15
+const HALF_RAD = Math.PI/2
16
+
17
+export default class AnimateNumber extends Component {
18
+
19
+  props : {
20
+    countBy? : ?number,
21
+    interval? : ?number,
22
+    steps? : ?number,
23
+    value : number,
24
+    timing : 'linear' | 'easeOut' | 'easeIn' | () => number,
25
+    formatter : () => {},
26
+    onProgress : () => {},
27
+    onFinish : () => {}
28
+  };
29
+
30
+  static defaultProps = {
31
+    interval : 14,
32
+    timing : 'linear',
33
+    steps : 45,
34
+    value : 0,
35
+    formatter : (val) => val,
36
+    onFinish : () => {}
37
+  };
38
+
39
+  static TimingFunctions = {
40
+
41
+    linear : (interval:number, progress:number):number => {
42
+      return interval
43
+    },
44
+
45
+    easeOut : (interval:number, progress:number):number => {
46
+      return interval * Math.sin(HALF_RAD*progress) * 5
47
+    },
48
+
49
+    easeIn : (interval:number, progress:number):number => {
50
+      return interval * Math.sin((HALF_RAD - HALF_RAD*progress)) * 5
51
+    },
52
+
53
+  };
54
+
55
+  state : {
56
+    value? : ?number,
57
+    displayValue? : ?number
58
+  };
59
+
60
+  /**
61
+   * Animation direction, true means positive, false means negative.
62
+   * @type {bool}
63
+   */
64
+  direction : bool;
65
+  /**
66
+   * Start value of last animation.
67
+   * @type {number}
68
+   */
69
+  startFrom : number;
70
+  /**
71
+  * End value of last animation.
72
+  * @type {number}
73
+   */
74
+  endWith : number;
75
+
76
+  constructor(props:any) {
77
+    super(props);
78
+    // default values of state and non-state variables
79
+    this.state = {
80
+      value : 0,
81
+      displayValue : 0
82
+    }
83
+    this.dirty = false;
84
+    this.startFrom = 0;
85
+    this.endWith = 0;
86
+  }
87
+
88
+  componentDidMount() {
89
+    this.startFrom = this.state.value
90
+    this.endWith = this.props.value
91
+    this.dirty = true
92
+    this.startAnimate()
93
+  }
94
+
95
+  componentWillUpdate(nextProps, nextState) {
96
+
97
+    // check if start an animation
98
+    if(this.props.value !== nextProps.value) {
99
+      this.startFrom = this.props.value
100
+      this.endWith = nextProps.value
101
+      this.dirty = true
102
+      this.startAnimate()
103
+      return
104
+    }
105
+    // Check if iterate animation frame
106
+    if(!this.dirty) {
107
+      return
108
+    }
109
+    if (this.direction === true) {
110
+      if(parseFloat(this.state.value) <= parseFloat(this.props.value)) {
111
+        this.startAnimate();
112
+      }
113
+    }
114
+    else if(this.direction === false){
115
+      if (parseFloat(this.state.value) >= parseFloat(this.props.value)) {
116
+        this.startAnimate();
117
+      }
118
+    }
119
+
120
+  }
121
+
122
+  render() {
123
+    return (
124
+      <Text {...this.props}>
125
+        {this.state.displayValue}
126
+      </Text>)
127
+  }
128
+
129
+  startAnimate() {
130
+
131
+    let progress = this.getAnimationProgress()
132
+
133
+    Timer.setTimeout(() => {
134
+
135
+      let value = (this.endWith - this.startFrom)/this.props.steps
136
+      if(this.props.countBy)
137
+        value = Math.sign(value)*Math.abs(this.props.countBy)
138
+      let total = parseFloat(this.state.value) + parseFloat(value)
139
+
140
+      this.direction = (value > 0)
141
+      // animation terminate conditions
142
+      if (((this.direction) ^ (total <= this.endWith)) === 1) {
143
+        this.dirty = false
144
+        total = this.endWith
145
+        this.props.onFinish(total, this.props.formatter(total))
146
+      }
147
+
148
+      if(this.props.onProgress)
149
+        this.props.onProgress(this.state.value, total)
150
+
151
+      this.setState({
152
+        value : total,
153
+        displayValue : this.props.formatter(total)
154
+      })
155
+
156
+    }, this.getTimingFunction(this.props.interval, progress))
157
+
158
+  }
159
+
160
+  getAnimationProgress():number {
161
+    return (this.state.value - this.startFrom) / (this.endWith - this.startFrom)
162
+  }
163
+
164
+  getTimingFunction(interval:number, progress:number) {
165
+    if(typeof this.props.timing === 'string') {
166
+      let fn = AnimateNumber.TimingFunctions[this.props.timing]
167
+      return fn(interval, progress)
168
+    } else if(typeof this.props.timing === 'function')
169
+      return this.props.timing(interval, progress)
170
+    else
171
+      return AnimateNumber.TimingFunctions['linear'](interval, progress)
172
+  }
173
+
174
+}

+ 59
- 20
test/react-native-testkit/components/reporter.js Datei anzeigen

9
   ListView,
9
   ListView,
10
   Image,
10
   Image,
11
   TouchableOpacity,
11
   TouchableOpacity,
12
+  Dimensions,
12
   RecyclerViewBackedScrollView,
13
   RecyclerViewBackedScrollView,
13
 } from 'react-native';
14
 } from 'react-native';
14
 
15
 
16
+import AnimateNumber from '../animate-text.js'
15
 import Assert from './assert.js'
17
 import Assert from './assert.js'
16
 import RNTEST from '../index.js'
18
 import RNTEST from '../index.js'
17
 
19
 
22
     this.tests = {
24
     this.tests = {
23
       common : []
25
       common : []
24
     }
26
     }
25
-    this.testGroups = ['common']
27
+    this.state = {
28
+      listHeight : 0
29
+    }
30
+    this.testGroups = ['summary','common']
26
     this.ds = null
31
     this.ds = null
27
     this.updateDataSource()
32
     this.updateDataSource()
28
 
33
 
34
 
39
 
35
   render() {
40
   render() {
36
 
41
 
42
+    let tests = RNTEST.TestContext.getTests()
43
+
44
+    let passed = 0
45
+    let executed = 0
46
+    let count = 0
47
+    for(let i in tests) {
48
+      if(tests[i].status !== 'skipped')
49
+        count++
50
+      if(tests[i].status !== 'waiting' && tests[i].status !== 'skipped')
51
+        executed++
52
+        passed += tests[i].status === 'pass' ? 1 : 0
53
+    }
54
+    let percent = passed / count
55
+    let color = `rgb(${Math.floor((1-percent) *255)},${Math.floor(percent *192)}, 0)`
56
+
37
     return (
57
     return (
38
-      <ListView
39
-        style={styles.container}
40
-        dataSource={this.ds}
41
-        renderRow={this.renderTest.bind(this)}
42
-        renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
43
-        renderSectionHeader={(data, id) => {
44
-          return (
45
-            <View style={styles.sectionHeader}>
46
-              <Text style={styles.sectionText}>{id}</Text>
47
-            </View>
48
-          )
49
-        }}
50
-      />)
58
+      <View style={{flex : 1}}>
59
+        <View style={{margin : 20}} onLayout={(e) => {
60
+          this.setState({
61
+            headerHeight : e.nativeEvent.layout.height,
62
+            listHeight : Dimensions.get('window').height - e.nativeEvent.layout.height
63
+          })
64
+        }}>
65
+          <Text>{`${executed} tests executed`}</Text>
66
+          <Text>{`${passed} test cases passed`}</Text>
67
+          <Text>{`${count} test cases`}</Text>
68
+          <View style={{flexDirection : 'row', alignSelf : 'center', alignItems : 'flex-end'}}>
69
+            <AnimateNumber style={{
70
+              color,
71
+              fontSize : 100,
72
+              textAlign : 'right'
73
+            }}
74
+              value={Math.floor(passed / count*100)}
75
+              countBy={1}/>
76
+            <Text style={{color, fontSize : 30, textAlign : 'left'}} >{`%`}</Text>
77
+          </View>
78
+        </View>
79
+        <ListView
80
+          style={[styles.container]}
81
+          dataSource={this.ds}
82
+          renderRow={this.renderTest.bind(this)}
83
+          renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
84
+          renderSectionHeader={(data, id) => {
85
+            return (
86
+              <View style={styles.sectionHeader}>
87
+                <Text style={styles.sectionText}>{id}</Text>
88
+              </View>
89
+            )
90
+          }}
91
+        />
92
+      </View>)
51
   }
93
   }
52
 
94
 
53
-  renderTest(t) {
95
+  renderTest(t, group) {
96
+
54
     let pass = true
97
     let pass = true
55
     let foundActions = false
98
     let foundActions = false
56
     let tests = RNTEST.TestContext.getTests()
99
     let tests = RNTEST.TestContext.getTests()
75
       t.status = 'waiting'
118
       t.status = 'waiting'
76
 
119
 
77
     return (
120
     return (
78
-      <TouchableOpacity onPress={()=>{
79
-          t.start(t.sn)
80
-        }}>
81
         <View key={'rn-test-' + t.desc} style={{
121
         <View key={'rn-test-' + t.desc} style={{
82
           borderBottomWidth : 1.5,
122
           borderBottomWidth : 1.5,
83
           borderColor : '#DDD',
123
           borderColor : '#DDD',
92
           <View key={t.desc + '-result'} style={{backgroundColor : '#F4F4F4'}}>
132
           <View key={t.desc + '-result'} style={{backgroundColor : '#F4F4F4'}}>
93
             {t.expand ? t.result : (t.status === 'pass' ? null : t.result)}
133
             {t.expand ? t.result : (t.status === 'pass' ? null : t.result)}
94
           </View>
134
           </View>
95
-        </View>
96
-      </TouchableOpacity>)
135
+        </View>)
97
   }
136
   }
98
 
137
 
99
   updateDataSource() {
138
   updateDataSource() {

+ 14
- 1
test/react-native-testkit/lib/test-context.js Datei anzeigen

4
 let RCTContext: ReactElement = null
4
 let RCTContext: ReactElement = null
5
 let props:any = {}
5
 let props:any = {}
6
 let timeout = 30000
6
 let timeout = 30000
7
+let summary = {}
7
 
8
 
8
 export default class TestContext {
9
 export default class TestContext {
9
 
10
 
41
       run : run === false ? false : true,
42
       run : run === false ? false : true,
42
       result : null,
43
       result : null,
43
       asserts : [],
44
       asserts : [],
44
-      timeout : timeout || 3000,
45
+      timeout : timeout || 15000,
45
       expired : false,
46
       expired : false,
46
       running : false,
47
       running : false,
47
       executed : false,
48
       executed : false,
127
           })
128
           })
128
           resolve(...res)
129
           resolve(...res)
129
         }
130
         }
131
+        RCTContext.forceUpdate()
130
       }).catch((err) => {
132
       }).catch((err) => {
131
         updateInternal({
133
         updateInternal({
132
           executed : true,
134
           executed : true,
146
   static update(i, ...data) {
148
   static update(i, ...data) {
147
     let test = tests[i]
149
     let test = tests[i]
148
     let result = test.result || []
150
     let result = test.result || []
151
+    // if new element have prop `uid`, we should replace it not appending it.
152
+    for(let i in data) {
153
+      if(data[i].props.uid) {
154
+        for(let j in result) {
155
+          if(result[j].uid === data[i].props.uid)
156
+          result[j] = data[i]
157
+          result.splice(j,1)
158
+          break
159
+        }
160
+      }
161
+    }
149
     Object.assign(test, {result : [...result, ...data]})
162
     Object.assign(test, {result : [...result, ...data]})
150
     RCTContext.forceUpdate()
163
     RCTContext.forceUpdate()
151
   }
164
   }

+ 39
- 5
test/test-0.5.1.js Datei anzeigen

119
 })
119
 })
120
 
120
 
121
 describe('Upload multipart data with file from storage', (report, done) => {
121
 describe('Upload multipart data with file from storage', (report, done) => {
122
+  try{
122
     let filename = 'test-from-storage-img-'+Date.now()+'.png'
123
     let filename = 'test-from-storage-img-'+Date.now()+'.png'
123
     RNFetchBlob.fetch('POST', `${TEST_SERVER_URL}/upload-form`, {
124
     RNFetchBlob.fetch('POST', `${TEST_SERVER_URL}/upload-form`, {
124
         'Content-Type' : 'multipart/form-data',
125
         'Content-Type' : 'multipart/form-data',
144
       </Info>)
145
       </Info>)
145
       done()
146
       done()
146
     })
147
     })
148
+  } catch(err) {
149
+    console.log(err)
150
+  }
147
 })
151
 })
148
 
152
 
149
 describe('Upload and download at the same time', (report, done) => {
153
 describe('Upload and download at the same time', (report, done) => {
179
         done()
183
         done()
180
       })
184
       })
181
     })
185
     })
182
-
183
 })
186
 })
184
 
187
 
185
 RNTest.config({
188
 RNTest.config({
186
   group : '0.5.1',
189
   group : '0.5.1',
187
   run : true,
190
   run : true,
188
   expand : false,
191
   expand : false,
189
-  timeout : 30000,
192
+  timeout : 600000,
190
 })('Upload and download large file', (report, done) => {
193
 })('Upload and download large file', (report, done) => {
191
   let filename = '22mb-dummy-' + Date.now()
194
   let filename = '22mb-dummy-' + Date.now()
195
+  let begin = -1
196
+  let begin2 = -1
197
+  let deb = Date.now()
192
   RNFetchBlob.config({
198
   RNFetchBlob.config({
193
     fileCache : true
199
     fileCache : true
194
   })
200
   })
195
   .fetch('GET', `${TEST_SERVER_URL}/public/22mb-dummy`)
201
   .fetch('GET', `${TEST_SERVER_URL}/public/22mb-dummy`)
202
+  .progress((now, total) => {
203
+    if(begin === -1)
204
+      begin = Date.now()
205
+    if(Date.now() - deb < 1000)
206
+      return
207
+    deb = Date.now()
208
+    report(<Info uid="200" key="progress">
209
+      <Text>
210
+        {`download ${now} / ${total} bytes (${Math.floor(now / (Date.now() - begin))} kb/s)`}
211
+      </Text>
212
+    </Info>)
213
+  })
196
   .then((res) => {
214
   .then((res) => {
197
-    return RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
215
+    deb = Date.now()
216
+    let promise =  RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
198
       Authorization : `Bearer ${DROPBOX_TOKEN}`,
217
       Authorization : `Bearer ${DROPBOX_TOKEN}`,
199
       'Dropbox-API-Arg': '{\"path\": \"/rn-upload/'+filename+'\",\"mode\": \"add\",\"autorename\": true,\"mute\": false}',
218
       'Dropbox-API-Arg': '{\"path\": \"/rn-upload/'+filename+'\",\"mode\": \"add\",\"autorename\": true,\"mute\": false}',
200
       'Content-Type' : 'application/octet-stream',
219
       'Content-Type' : 'application/octet-stream',
201
     }, RNFetchBlob.wrap(res.path()))
220
     }, RNFetchBlob.wrap(res.path()))
221
+    if(Platform.OS === 'ios')
222
+      promise.progress((now, total) => {
223
+        if(Date.now() - deb < 1000)
224
+          return
225
+        deb = Date.now()
226
+        if(begin2 === -1)
227
+          begin2 = Date.now()
228
+        let speed = Math.floor(now / (Date.now() - begin2))
229
+        report(<Info uid="100"  key="progress">
230
+          <Text>
231
+            {`upload ${now} / ${total} bytes (${speed} kb/s)`}
232
+            {` ${Math.floor((total-now)/speed/1000)} seconds left`}
233
+          </Text>
234
+        </Info>)
235
+      })
236
+    return promise
202
   })
237
   })
203
   .then((res) => {
238
   .then((res) => {
204
     report(<Assert
239
     report(<Assert
205
-      key="upload should success withou crashing app"
240
+      key="upload should success without crashing app"
206
       expect={filename}
241
       expect={filename}
207
       actual={res.json().name}/>)
242
       actual={res.json().name}/>)
208
     done()
243
     done()
302
       })
337
       })
303
 
338
 
304
   })
339
   })
305
-
306
 })
340
 })

+ 203
- 0
test/test-0.6.2.js Datei anzeigen

1
+import RNTest from './react-native-testkit/'
2
+import React from 'react'
3
+import RNFetchBlob from 'react-native-fetch-blob'
4
+
5
+import {
6
+  StyleSheet,
7
+  Text,
8
+  View,
9
+  ScrollView,
10
+  CameraRoll,
11
+  Platform,
12
+  Dimensions,
13
+  Image,
14
+} from 'react-native';
15
+
16
+const fs = RNFetchBlob.fs
17
+const { Assert, Comparer, Info, prop } = RNTest
18
+const describe = RNTest.config({
19
+  group : '0.6.2',
20
+  run : true,
21
+  expand : false,
22
+  timeout : 30000,
23
+})
24
+const { TEST_SERVER_URL, TEST_SERVER_URL_SSL, DROPBOX_TOKEN, styles } = prop()
25
+const  dirs = RNFetchBlob.fs.dirs
26
+
27
+let prefix = ((Platform.OS === 'android') ? 'file://' : '')
28
+let photo = null
29
+
30
+describe('upload asset from camera roll', (report, done) => {
31
+  let imgName = `image-from-camera-roll-${Platform.OS}.jpg`
32
+  let tick = Date.now()
33
+  CameraRoll.getPhotos({first : 10})
34
+    .then((resp) => {
35
+      let url = resp.edges[0].node.image.uri
36
+      photo = url
37
+      return RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
38
+        Authorization : `Bearer ${DROPBOX_TOKEN}`,
39
+        'Dropbox-API-Arg': `{\"path\": \"/rn-upload/${imgName}\",\"mode\": \"add\",\"autorename\": false,\"mute\": false}`,
40
+        'Content-Type' : 'application/octet-stream',
41
+      }, RNFetchBlob.wrap(url))
42
+      .progress((now, total) => {
43
+        if(Date.now() - tick < 1000)
44
+        return
45
+        report(<Info key="progress" uid="pg1">
46
+          <Text>{`upload ${now} / ${total} ${Math.floor(now/total*100)}% `}</Text>
47
+        </Info>)
48
+      })
49
+    })
50
+    .then((resp) => {
51
+      resp = resp.json()
52
+      report(
53
+        <Assert key="confirm the file has been uploaded" expect={imgName} actual={resp.name}/>
54
+      )
55
+      done()
56
+    })
57
+})
58
+//
59
+// describe('Upload multipart data with file from CameraRoll', (report, done) => {
60
+//     let filename = 'test-from-storage-img-'+Date.now()+'.png'
61
+//     RNFetchBlob.fetch('POST', `${TEST_SERVER_URL}/upload-form`, {
62
+//         'Content-Type' : 'multipart/form-data',
63
+//       }, [
64
+//         { name : 'test-img', filename : filename, data: RNFetchBlob.wrap(photo)},
65
+//         { name : 'test-text', filename : 'test-text.txt', data: RNFetchBlob.base64.encode('hello.txt')},
66
+//         { name : 'field1', data : 'hello !!'},
67
+//         { name : 'field2', data : 'hello2 !!'}
68
+//       ])
69
+//     .then((resp) => {
70
+//       resp = resp.json()
71
+//       report(
72
+//         <Assert key="check posted form data #1" expect="hello !!" actual={resp.fields.field1}/>,
73
+//         <Assert key="check posted form data #2" expect="hello2 !!" actual={resp.fields.field2}/>,
74
+//       )
75
+//       return RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/public/${filename}`)
76
+//     })
77
+//     .then((resp) => {
78
+//       report(<Info key="uploaded image">
79
+//         <Image
80
+//           style={styles.image}
81
+//           source={{ uri : 'data:image/png;base64, '+ resp.base64()}}/>
82
+//       </Info>)
83
+//       done()
84
+//     })
85
+// })
86
+//
87
+//
88
+// describe('access assets from camera roll', (report, done) => {
89
+//   let photo = null
90
+//   CameraRoll.getPhotos({first : 10})
91
+//     .then((resp) => {
92
+//       photo = resp.edges[0].node.image.uri
93
+//       report(<Info key="items">
94
+//         <Text>{photo}</Text>
95
+//       </Info>)
96
+//       return fs.readFile(photo, 'base64')
97
+//     })
98
+//     .then((data) => {
99
+//       report(<Info key="asset image">
100
+//         <Image
101
+//           style={styles.image}
102
+//           source={{uri: `data:image/png;base64, ${data}`}}/>
103
+//       </Info>)
104
+//       done()
105
+//     })
106
+// })
107
+//
108
+// describe('read asset in app bundle',(report, done) => {
109
+//   let target = fs.asset('test-asset2.png')
110
+//   fs.readFile(target, 'base64')
111
+//   .then((data) => {
112
+//     report(<Info key="asset image">
113
+//       <Image
114
+//         style={styles.image}
115
+//         source={{uri: `data:image/png;base64, ${data}`}}/>
116
+//     </Info>)
117
+//     return fs.readFile(fs.asset('test-asset1.json'), 'utf8')
118
+//   })
119
+//   .then((resp) => {
120
+//     report(
121
+//       <Assert key="asset content verify"
122
+//         expect="asset#1"
123
+//         actual={JSON.parse(resp).secret}/>)
124
+//       done()
125
+//   })
126
+// })
127
+//
128
+// describe('stat assets in app', (report, done) => {
129
+//   fs.stat(fs.asset('test-asset2.png'))
130
+//     .then((data) => {
131
+//       report(<Info key="list of assets">
132
+//         <Text>{JSON.stringify(data)}</Text>
133
+//       </Info>)
134
+//       done()
135
+//     })
136
+// })
137
+//
138
+// describe('copy asset', (report, done) => {
139
+//   let dest = `${dirs.DocumentDir}/test-asset-1-${Date.now()}.json`
140
+//   fs.cp(fs.asset('test-asset1.json'), dest)
141
+//     .then(() => fs.readFile(dest, 'utf8'))
142
+//     .then((data) => {
143
+//       report(<Assert key="asset copied correctly"
144
+//         expect={'asset#1'}
145
+//         actual={JSON.parse(data).secret}/>)
146
+//       return fs.stat(fs.asset('test-asset1.json'))
147
+//     })
148
+//     .then((stat) => {
149
+//       report(<Assert key="file size check"
150
+//         expect={27}
151
+//         actual={Math.floor(stat.size)}/>,
152
+//       <Info key="dest file info">
153
+//         <Text>{JSON.stringify(stat)}</Text>
154
+//       </Info>)
155
+//       done()
156
+//     })
157
+// })
158
+//
159
+//
160
+// describe('upload file from assets',(report, done) => {
161
+//   let assetName = fs.asset('test-asset1.json')
162
+//   RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
163
+//     Authorization : `Bearer ${DROPBOX_TOKEN}`,
164
+//     'Dropbox-API-Arg': `{\"path\": \"/rn-upload/file-from-asset-${Platform.OS}.json\",\"mode\": \"add\",\"autorename\": false,\"mute\": false}`,
165
+//     'Content-Type' : 'application/octet-stream',
166
+//   }, RNFetchBlob.wrap(assetName))
167
+//   .then((resp) => {
168
+//     resp = resp.json()
169
+//     report(
170
+//       <Assert key="file name check"
171
+//         expect={`file-from-asset-${Platform.OS}.json`}
172
+//         actual={resp.name}/>)
173
+//     done()
174
+//   })
175
+// })
176
+
177
+describe('Check custom MIME type correctness',(report, done) => {
178
+  RNFetchBlob
179
+  .config({fileCache : true})
180
+  .fetch('GET', `${TEST_SERVER_URL}/public/beethoven.mp3`)
181
+  .then((resp) => {
182
+    return RNFetchBlob.fetch('POST', `${TEST_SERVER_URL}/mime`, null, [
183
+      { name : 'image', filename : 'image', type : 'image/jpeg', data : RNFetchBlob.base64.encode('123456') },
184
+      { name : 'mp3', filename : 'mp3', type : 'application/mp3', data : RNFetchBlob.base64.encode('123456') },
185
+      { name : 'mp3', filename : 'mp3', data : RNFetchBlob.base64.encode('123456') },
186
+      { name : 'asset', filename : 'asset.json', type: 'application/json', data : RNFetchBlob.wrap(fs.asset('test-asset1.json')) },
187
+      { name : 'camera-roll', filename : 'cameraRoll.png', type: 'image/png', data : RNFetchBlob.wrap(photo) },
188
+      { name : 'storage', filename : 'storage.mp3', type: 'application/mp3', data : RNFetchBlob.wrap(resp.path()) },
189
+    ])
190
+  })
191
+  .then((resp) => {
192
+    resp = resp.json()
193
+    report(
194
+      <Assert key="check #1 mime" expect={'image/jpeg'} actual={resp[0]} />,
195
+      <Assert key="check #2 mime" expect={'application/mp3'} actual={resp[1]} />,
196
+      <Assert key="check #3 mime" expect={'application/octet-stream'} actual={resp[2]} />,
197
+      <Assert key="check #4 mime" expect={'application/json'} actual={resp[3]} />,
198
+      <Assert key="check #5 mime" expect={'image/png'} actual={resp[4]} />,
199
+      <Assert key="check #6 mime" expect={'application/mp3'} actual={resp[5]} />,
200
+    )
201
+    done()
202
+  })
203
+})

+ 1
- 1
test/test-android.js Datei anzeigen

17
 const describe = RNTest.config({
17
 const describe = RNTest.config({
18
   group : 'Android only functions',
18
   group : 'Android only functions',
19
   run : Platform.OS === 'android',
19
   run : Platform.OS === 'android',
20
-  expand : true,
20
+  expand : false,
21
 })
21
 })
22
 const { TEST_SERVER_URL, FILENAME, DROPBOX_TOKEN, styles } = prop()
22
 const { TEST_SERVER_URL, FILENAME, DROPBOX_TOKEN, styles } = prop()
23
 
23
 

+ 5
- 3
test/test-init.js Datei anzeigen

18
 // test environment variables
18
 // test environment variables
19
 
19
 
20
 prop('FILENAME', `${Platform.OS}-0.7.0-${Date.now()}.png`)
20
 prop('FILENAME', `${Platform.OS}-0.7.0-${Date.now()}.png`)
21
-prop('TEST_SERVER_URL', 'http://192.168.16.70:8123')
22
-prop('TEST_SERVER_URL_SSL', 'https://192.168.16.70:8124')
21
+prop('TEST_SERVER_URL', 'http://192.168.0.11:8123')
22
+prop('TEST_SERVER_URL_SSL', 'https://192.168.0.11:8124')
23
 prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
23
 prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
24
 prop('styles', {
24
 prop('styles', {
25
   image : {
25
   image : {
50
         done()
50
         done()
51
     })
51
     })
52
 })
52
 })
53
-//
53
+
54
+
54
 require('./test-0.1.x-0.4.x')
55
 require('./test-0.1.x-0.4.x')
55
 require('./test-0.5.1')
56
 require('./test-0.5.1')
56
 require('./test-0.5.2')
57
 require('./test-0.5.2')
57
 require('./test-0.6.0')
58
 require('./test-0.6.0')
59
+require('./test-0.6.2')
58
 require('./test-fs')
60
 require('./test-fs')
59
 require('./test-android')
61
 require('./test-android')