react-native-blob-util 0.14.1 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -1
- package/android/src/main/AndroidManifest.xml +0 -3
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java +7 -7
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilBody.java +30 -6
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +2 -0
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java +25 -2
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +10 -0
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java +20 -5
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +100 -1
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilUtils.java +5 -17
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +4 -0
- package/class/ReactNativeBlobUtilBlobResponse.js +1 -1
- package/fs.js +42 -3
- package/index.d.ts +30 -1
- package/index.js +2 -2
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.m +4 -3
- package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +6 -2
- package/ios/ReactNativeBlobUtilConst.h +1 -0
- package/ios/ReactNativeBlobUtilConst.m +1 -0
- package/ios/ReactNativeBlobUtilFS.h +2 -1
- package/ios/ReactNativeBlobUtilFS.m +30 -2
- package/ios/ReactNativeBlobUtilFileTransformer.h +24 -0
- package/ios/ReactNativeBlobUtilFileTransformer.m +21 -0
- package/ios/ReactNativeBlobUtilReqBuilder.m +3 -4
- package/ios/ReactNativeBlobUtilRequest.m +35 -7
- package/mediacollection.js +6 -1
- package/package.json +7 -2
- package/polyfill/Fetch.js +4 -2
- package/polyfill/XMLHttpRequest.js +6 -0
- package/types.js +1 -0
- package/ios/IOS7Polyfill.h +0 -27
- package/react-native.config.js +0 -7
- package/scripts/prelink.js +0 -71
package/README.md
CHANGED
|
@@ -165,7 +165,7 @@ If you are going to use the `wifiOnly` flag, you need to add this to `AndroidMan
|
|
|
165
165
|
|
|
166
166
|
**Grant Access Permission for Android 6.0**
|
|
167
167
|
|
|
168
|
-
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 for Android 6.0+ devices. To grant permissions in runtime, you might use [PermissionAndroid API](https://facebook.github.io/react-native/docs/permissionsandroid
|
|
168
|
+
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 for Android 6.0+ devices. To grant permissions in runtime, you might use [PermissionAndroid API](https://facebook.github.io/react-native/docs/permissionsandroid).
|
|
169
169
|
|
|
170
170
|
## Usage
|
|
171
171
|
|
|
@@ -303,6 +303,26 @@ ReactNativeBlobUtil
|
|
|
303
303
|
|
|
304
304
|
**These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)**
|
|
305
305
|
|
|
306
|
+
**Use File Transformer**
|
|
307
|
+
|
|
308
|
+
If you need to perform any processing on the bytes prior to it being written into storage (e.g. if you want it to be encrypted) then you can use `transform` option. NOTE: you will need to set a transformer on the libray (see [Setting a File Transformer](#Setting-A-File-Transformer))
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
ReactNativeBlobUtil
|
|
312
|
+
.config({
|
|
313
|
+
// response data will be saved to this path if it has access right.
|
|
314
|
+
path: dirs.DocumentDir + '/path-to-file.anything',
|
|
315
|
+
transform: true
|
|
316
|
+
})
|
|
317
|
+
.fetch('GET', 'http://www.example.com/file/example.zip', {
|
|
318
|
+
//some headers ..
|
|
319
|
+
})
|
|
320
|
+
.then((res) => {
|
|
321
|
+
// the path should be dirs.DocumentDir + 'path-to-file.anything'
|
|
322
|
+
console.log('The file saved to ', res.path())
|
|
323
|
+
})
|
|
324
|
+
```
|
|
325
|
+
|
|
306
326
|
#### Upload example : Dropbox [files-upload](https://www.dropbox.com/developers/documentation/http/documentation#files-upload) API
|
|
307
327
|
|
|
308
328
|
`react-native-blob-util` will convert the base64 string in `body` to binary format using native API, this process is done in a separated thread so that it won't block your GUI.
|
|
@@ -676,6 +696,14 @@ await ReactNativeBlobUtil.MediaCollection.writeToMediafile('content://....', //
|
|
|
676
696
|
);
|
|
677
697
|
````
|
|
678
698
|
|
|
699
|
+
Copies and tranforms data from a file in the apps storage to an existing entry of the Media Store. NOTE: you must set a transformer on the file in order for the transformation to happen (see [Setting a File Transformer](#Setting-A-File-Transformer)).
|
|
700
|
+
|
|
701
|
+
````js
|
|
702
|
+
await ReactNativeBlobUtil.MediaCollection.writeToMediafileWithTransform('content://....', // content uri of the entry in the media storage
|
|
703
|
+
localpath // path to the file that should be copied
|
|
704
|
+
);
|
|
705
|
+
````
|
|
706
|
+
|
|
679
707
|
#### copyToInternal
|
|
680
708
|
Copies an entry form the media storage to the apps internal storage.
|
|
681
709
|
````js
|
|
@@ -697,8 +725,10 @@ File Access APIs
|
|
|
697
725
|
- [dirs](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#dirs)
|
|
698
726
|
- [createFile](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#createfilepath-data-encodingpromise)
|
|
699
727
|
- [writeFile (0.6.0)](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise)
|
|
728
|
+
- writeFileWithTransform
|
|
700
729
|
- [appendFile (0.6.0) ](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#appendfilepathstring-contentstring--arraynumber-encodingstring-promisenumber)
|
|
701
730
|
- [readFile (0.6.0)](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#readfilepath-encodingpromise)
|
|
731
|
+
- readFileWithTransform
|
|
702
732
|
- [readStream](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#readstreampath-encoding-buffersize-interval-promisernfbreadstream)
|
|
703
733
|
- [hash (0.10.9)](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#hashpath-algorithm-promise)
|
|
704
734
|
- [writeStream](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#writestreampathstring-encodingstringpromise)
|
|
@@ -878,6 +908,30 @@ ReactNativeBlobUtil.fetch('POST', 'http://example.com/upload', {'Transfer-Encodi
|
|
|
878
908
|
### Self-Signed SSL Server
|
|
879
909
|
|
|
880
910
|
By default, react-native-blob-util does NOT allow connection to unknown certification provider since it's dangerous. To connect a server with self-signed certification, you need to add `trusty` to `config` explicitly. This function is available for version >= `0.5.3`
|
|
911
|
+
In addition since ``0.16.0`` you'll have to define your own trust manager for android.
|
|
912
|
+
````java
|
|
913
|
+
public class MainApplication extends Application implements ReactApplication {
|
|
914
|
+
...
|
|
915
|
+
@Override
|
|
916
|
+
public void onCreate() {
|
|
917
|
+
...
|
|
918
|
+
ReactNativeBlobUtilUtils.sharedTrustManager = final X509TrustManager x509TrustManager = new X509TrustManager() {
|
|
919
|
+
@Override
|
|
920
|
+
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
@Override
|
|
924
|
+
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
@Override
|
|
928
|
+
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
|
929
|
+
return new java.security.cert.X509Certificate[]{};
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
...
|
|
933
|
+
}
|
|
934
|
+
````
|
|
881
935
|
|
|
882
936
|
```js
|
|
883
937
|
ReactNativeBlobUtil.config({
|
|
@@ -903,6 +957,10 @@ ReactNativeBlobUtil.config({
|
|
|
903
957
|
})
|
|
904
958
|
```
|
|
905
959
|
|
|
960
|
+
### Transform Files
|
|
961
|
+
|
|
962
|
+
Sometimes you may need the files to be transformed after reading from storage or before writing into storage (eg encryption/decyrption). In order to perform the transformations, use `readFileWithTransform` and `writeFileWithTransform`. NOTE: you must set a transformer on the file in order for the transformation to happen (see [Setting a File Transformer](#Setting-A-File-Transformer)).
|
|
963
|
+
|
|
906
964
|
## Web API Polyfills
|
|
907
965
|
|
|
908
966
|
After `0.8.0` we've made some [Web API polyfills](https://github.com/RonRadtke/react-native-blob-util/wiki/Web-API-Polyfills-(experimental)) that makes some browser-based library available in RN.
|
|
@@ -910,6 +968,48 @@ After `0.8.0` we've made some [Web API polyfills](https://github.com/RonRadtke/r
|
|
|
910
968
|
- Blob
|
|
911
969
|
- XMLHttpRequest (Use our implementation if you're going to use it with Blob)
|
|
912
970
|
|
|
971
|
+
|
|
972
|
+
## Setting A File Transformer
|
|
973
|
+
|
|
974
|
+
Setting a file transformer will allow you to specify how data should be transformed whenever the library is writing into storage or reading from storage. A use case for this is if you want the files handled by this library to be encrypted.
|
|
975
|
+
|
|
976
|
+
If you want to use a file transformer, you must implement an interface defined in:
|
|
977
|
+
|
|
978
|
+
[ReactNativeBlobUtilFileTransformer.h (iOS)](/ios/ReactNativeBlobUtilFileTransformer.h)
|
|
979
|
+
|
|
980
|
+
[ReactNativeBlobUtilFileTransformer.java (Android)](/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java)
|
|
981
|
+
|
|
982
|
+
Then you set the File Transformer during app startup
|
|
983
|
+
|
|
984
|
+
Android:
|
|
985
|
+
```java
|
|
986
|
+
public class MainApplication extends Application implements ReactApplication {
|
|
987
|
+
...
|
|
988
|
+
@Override
|
|
989
|
+
public void onCreate() {
|
|
990
|
+
...
|
|
991
|
+
ReactNativeBlobUtilFileTransformer.sharedFileTransformer = new MyCustomEncryptor();
|
|
992
|
+
...
|
|
993
|
+
}
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
iOS:
|
|
997
|
+
```m
|
|
998
|
+
@implementation AppDelegate
|
|
999
|
+
...
|
|
1000
|
+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
|
1001
|
+
{
|
|
1002
|
+
...
|
|
1003
|
+
[ReactNativeBlobUtilFileTransformer setFileTransformer: MyCustomEncryptor.new];
|
|
1004
|
+
...
|
|
1005
|
+
}
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
Here are the places where the transformer would apply
|
|
1009
|
+
- Reading a file from the file system
|
|
1010
|
+
- Writing a file into the file system
|
|
1011
|
+
- Http response is downloaded to storage directly
|
|
1012
|
+
|
|
913
1013
|
## Performance Tips
|
|
914
1014
|
|
|
915
1015
|
**Read Stream and Progress Event Overhead**
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
2
|
package="com.ReactNativeBlobUtil">
|
|
3
3
|
|
|
4
|
-
<!-- Required to access Google Play Licensing -->
|
|
5
|
-
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
|
|
6
|
-
|
|
7
4
|
<!-- Required to download files from Google Play -->
|
|
8
5
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
9
6
|
|
|
@@ -233,11 +233,11 @@ public class ReactNativeBlobUtil extends ReactContextBaseJavaModule {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
@ReactMethod
|
|
236
|
-
public void readFile(final String path, final String encoding, final Promise promise) {
|
|
236
|
+
public void readFile(final String path, final String encoding, final boolean transformFile, final Promise promise) {
|
|
237
237
|
threadPool.execute(new Runnable() {
|
|
238
238
|
@Override
|
|
239
239
|
public void run() {
|
|
240
|
-
ReactNativeBlobUtilFS.readFile(path, encoding, promise);
|
|
240
|
+
ReactNativeBlobUtilFS.readFile(path, encoding, transformFile, promise);
|
|
241
241
|
}
|
|
242
242
|
});
|
|
243
243
|
}
|
|
@@ -253,11 +253,11 @@ public class ReactNativeBlobUtil extends ReactContextBaseJavaModule {
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
@ReactMethod
|
|
256
|
-
public void writeFile(final String path, final String encoding, final String data, final boolean append, final Promise promise) {
|
|
256
|
+
public void writeFile(final String path, final String encoding, final String data, final boolean transformFile, final boolean append, final Promise promise) {
|
|
257
257
|
threadPool.execute(new Runnable() {
|
|
258
258
|
@Override
|
|
259
259
|
public void run() {
|
|
260
|
-
ReactNativeBlobUtilFS.writeFile(path, encoding, data, append, promise);
|
|
260
|
+
ReactNativeBlobUtilFS.writeFile(path, encoding, data, transformFile, append, promise);
|
|
261
261
|
}
|
|
262
262
|
});
|
|
263
263
|
}
|
|
@@ -438,8 +438,8 @@ public class ReactNativeBlobUtil extends ReactContextBaseJavaModule {
|
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
@ReactMethod
|
|
441
|
-
public void writeToMediaFile(String fileUri, String path, Promise promise) {
|
|
442
|
-
boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(Uri.parse(fileUri), path, promise);
|
|
441
|
+
public void writeToMediaFile(String fileUri, String path, boolean transformFile, Promise promise) {
|
|
442
|
+
boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(Uri.parse(fileUri), path, transformFile, promise);
|
|
443
443
|
if(res) promise.resolve("Success");
|
|
444
444
|
}
|
|
445
445
|
|
|
@@ -478,7 +478,7 @@ public class ReactNativeBlobUtil extends ReactContextBaseJavaModule {
|
|
|
478
478
|
return;
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(fileuri, path, promise);
|
|
481
|
+
boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(fileuri, path, false, promise);
|
|
482
482
|
if(res) promise.resolve(fileuri.toString());
|
|
483
483
|
}
|
|
484
484
|
|
|
@@ -26,7 +26,6 @@ import okio.BufferedSink;
|
|
|
26
26
|
|
|
27
27
|
class ReactNativeBlobUtilBody extends RequestBody {
|
|
28
28
|
|
|
29
|
-
private InputStream requestStream;
|
|
30
29
|
private long contentLength = 0;
|
|
31
30
|
private ReadableArray form;
|
|
32
31
|
private String mTaskId;
|
|
@@ -71,12 +70,10 @@ class ReactNativeBlobUtilBody extends RequestBody {
|
|
|
71
70
|
try {
|
|
72
71
|
switch (requestType) {
|
|
73
72
|
case SingleFile:
|
|
74
|
-
|
|
75
|
-
contentLength = requestStream.available();
|
|
73
|
+
contentLength = getRequestStream().available();
|
|
76
74
|
break;
|
|
77
75
|
case AsIs:
|
|
78
76
|
contentLength = this.rawBody.getBytes().length;
|
|
79
|
-
requestStream = new ByteArrayInputStream(this.rawBody.getBytes());
|
|
80
77
|
break;
|
|
81
78
|
case Others:
|
|
82
79
|
break;
|
|
@@ -98,7 +95,6 @@ class ReactNativeBlobUtilBody extends RequestBody {
|
|
|
98
95
|
this.form = body;
|
|
99
96
|
try {
|
|
100
97
|
bodyCache = createMultipartBodyCache();
|
|
101
|
-
requestStream = new FileInputStream(bodyCache);
|
|
102
98
|
contentLength = bodyCache.length();
|
|
103
99
|
} catch (Exception ex) {
|
|
104
100
|
ex.printStackTrace();
|
|
@@ -107,6 +103,34 @@ class ReactNativeBlobUtilBody extends RequestBody {
|
|
|
107
103
|
return this;
|
|
108
104
|
}
|
|
109
105
|
|
|
106
|
+
// This organizes the input stream initialization logic into a method. This allows:
|
|
107
|
+
// 1) Initialization to be deferred until it's needed (when we are ready to pipe it into the BufferedSink)
|
|
108
|
+
// 2) The stream to be initialized and used as many times as necessary. When okhttp runs into
|
|
109
|
+
// a connection error, it will retry the request which will require a new stream to write into
|
|
110
|
+
// the sink once again.
|
|
111
|
+
InputStream getInputStreamForRequestBody() {
|
|
112
|
+
try {
|
|
113
|
+
if (this.form != null) {
|
|
114
|
+
return new FileInputStream(bodyCache);
|
|
115
|
+
} else {
|
|
116
|
+
switch (requestType) {
|
|
117
|
+
case SingleFile:
|
|
118
|
+
return getRequestStream();
|
|
119
|
+
case AsIs:
|
|
120
|
+
return new ByteArrayInputStream(this.rawBody.getBytes());
|
|
121
|
+
case Others:
|
|
122
|
+
ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil could not create input stream for request type others");
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch (Exception ex){
|
|
127
|
+
ex.printStackTrace();
|
|
128
|
+
ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil failed to create input stream for request:" + ex.getLocalizedMessage());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
110
134
|
@Override
|
|
111
135
|
public long contentLength() {
|
|
112
136
|
return chunkedEncoding ? -1 : contentLength;
|
|
@@ -120,7 +144,7 @@ class ReactNativeBlobUtilBody extends RequestBody {
|
|
|
120
144
|
@Override
|
|
121
145
|
public void writeTo(@NonNull BufferedSink sink) {
|
|
122
146
|
try {
|
|
123
|
-
pipeStreamToSink(
|
|
147
|
+
pipeStreamToSink(getInputStreamForRequestBody(), sink);
|
|
124
148
|
} catch (Exception ex) {
|
|
125
149
|
ReactNativeBlobUtilUtils.emitWarningEvent(ex.getLocalizedMessage());
|
|
126
150
|
ex.printStackTrace();
|
|
@@ -8,6 +8,7 @@ import java.util.Locale;
|
|
|
8
8
|
class ReactNativeBlobUtilConfig {
|
|
9
9
|
|
|
10
10
|
public Boolean fileCache;
|
|
11
|
+
public Boolean transformFile;
|
|
11
12
|
public String path;
|
|
12
13
|
public String appendExt;
|
|
13
14
|
public ReadableMap addAndroidDownloads;
|
|
@@ -26,6 +27,7 @@ class ReactNativeBlobUtilConfig {
|
|
|
26
27
|
if (options == null)
|
|
27
28
|
return;
|
|
28
29
|
this.fileCache = options.hasKey("fileCache") && options.getBoolean("fileCache");
|
|
30
|
+
this.transformFile = options.hasKey("transformFile") ? options.getBoolean("transformFile") : false;
|
|
29
31
|
this.path = options.hasKey("path") ? options.getString("path") : null;
|
|
30
32
|
this.appendExt = options.hasKey("appendExt") ? options.getString("appendExt") : "";
|
|
31
33
|
this.trusty = options.hasKey("trusty") && options.getBoolean("trusty");
|
|
@@ -54,6 +54,7 @@ class ReactNativeBlobUtilFS {
|
|
|
54
54
|
static boolean writeFile(String path, String encoding, String data, final boolean append) {
|
|
55
55
|
try {
|
|
56
56
|
int written;
|
|
57
|
+
path = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
57
58
|
File f = new File(path);
|
|
58
59
|
File dir = f.getParentFile();
|
|
59
60
|
if (!f.exists()) {
|
|
@@ -121,7 +122,7 @@ class ReactNativeBlobUtilFS {
|
|
|
121
122
|
* @param data Array passed from JS context.
|
|
122
123
|
* @param promise RCT Promise
|
|
123
124
|
*/
|
|
124
|
-
static void writeFile(String path, String encoding, String data, final boolean append, final Promise promise) {
|
|
125
|
+
static void writeFile(String path, String encoding, String data, final boolean transformFile, final boolean append, final Promise promise) {
|
|
125
126
|
try {
|
|
126
127
|
int written;
|
|
127
128
|
File f = new File(path);
|
|
@@ -169,6 +170,12 @@ class ReactNativeBlobUtilFS {
|
|
|
169
170
|
}
|
|
170
171
|
} else {
|
|
171
172
|
byte[] bytes = ReactNativeBlobUtilUtils.stringToBytes(data, encoding);
|
|
173
|
+
if (transformFile) {
|
|
174
|
+
if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
|
|
175
|
+
throw new IllegalStateException("Write file with transform was specified but the shared file transformer is not set");
|
|
176
|
+
}
|
|
177
|
+
bytes = ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onWriteFile(bytes);
|
|
178
|
+
}
|
|
172
179
|
FileOutputStream fout = new FileOutputStream(f, append);
|
|
173
180
|
try {
|
|
174
181
|
fout.write(bytes);
|
|
@@ -237,7 +244,7 @@ class ReactNativeBlobUtilFS {
|
|
|
237
244
|
* @param encoding Encoding of read stream.
|
|
238
245
|
* @param promise JS promise
|
|
239
246
|
*/
|
|
240
|
-
static void readFile(String path, String encoding, final Promise promise) {
|
|
247
|
+
static void readFile(String path, String encoding, final boolean transformFile, final Promise promise) {
|
|
241
248
|
String resolved = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
242
249
|
if (resolved != null)
|
|
243
250
|
path = resolved;
|
|
@@ -280,6 +287,13 @@ class ReactNativeBlobUtilFS {
|
|
|
280
287
|
return;
|
|
281
288
|
}
|
|
282
289
|
|
|
290
|
+
if (transformFile) {
|
|
291
|
+
if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
|
|
292
|
+
throw new IllegalStateException("Read file with transform was specified but the shared file transformer is not set");
|
|
293
|
+
}
|
|
294
|
+
bytes = ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onReadFile(bytes);
|
|
295
|
+
}
|
|
296
|
+
|
|
283
297
|
switch (encoding.toLowerCase(Locale.ROOT)) {
|
|
284
298
|
case "base64":
|
|
285
299
|
promise.resolve(Base64.encodeToString(bytes, Base64.NO_WRAP));
|
|
@@ -441,6 +455,7 @@ class ReactNativeBlobUtilFS {
|
|
|
441
455
|
* @param promise JS promise
|
|
442
456
|
*/
|
|
443
457
|
static void mkdir(String path, Promise promise) {
|
|
458
|
+
path = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
444
459
|
File dest = new File(path);
|
|
445
460
|
if (dest.exists()) {
|
|
446
461
|
promise.reject("EEXIST", (dest.isDirectory() ? "Folder" : "File") + " '" + path + "' already exists");
|
|
@@ -468,6 +483,7 @@ class ReactNativeBlobUtilFS {
|
|
|
468
483
|
*/
|
|
469
484
|
static void cp(String path, String dest, Callback callback) {
|
|
470
485
|
path = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
486
|
+
dest = ReactNativeBlobUtilUtils.normalizePath(dest);
|
|
471
487
|
InputStream in = null;
|
|
472
488
|
OutputStream out = null;
|
|
473
489
|
String message = "";
|
|
@@ -524,6 +540,8 @@ class ReactNativeBlobUtilFS {
|
|
|
524
540
|
* @param callback JS context callback
|
|
525
541
|
*/
|
|
526
542
|
static void mv(String path, String dest, Callback callback) {
|
|
543
|
+
path = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
544
|
+
dest = ReactNativeBlobUtilUtils.normalizePath(dest);
|
|
527
545
|
File src = new File(path);
|
|
528
546
|
if (!src.exists()) {
|
|
529
547
|
callback.invoke("Source file at path `" + path + "` does not exist");
|
|
@@ -627,6 +645,7 @@ class ReactNativeBlobUtilFS {
|
|
|
627
645
|
static void slice(String path, String dest, int start, int end, String encode, Promise promise) {
|
|
628
646
|
try {
|
|
629
647
|
path = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
648
|
+
dest = ReactNativeBlobUtilUtils.normalizePath(dest);
|
|
630
649
|
File source = new File(path);
|
|
631
650
|
if (source.isDirectory()) {
|
|
632
651
|
promise.reject("EISDIR", "Expecting a file but '" + path + "' is a directory");
|
|
@@ -790,6 +809,8 @@ class ReactNativeBlobUtilFS {
|
|
|
790
809
|
promise.reject("EINVAL", "Invalid algorithm '" + algorithm + "', must be one of md5, sha1, sha224, sha256, sha384, sha512");
|
|
791
810
|
return;
|
|
792
811
|
}
|
|
812
|
+
|
|
813
|
+
path = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
793
814
|
|
|
794
815
|
File file = new File(path);
|
|
795
816
|
|
|
@@ -837,6 +858,7 @@ class ReactNativeBlobUtilFS {
|
|
|
837
858
|
*/
|
|
838
859
|
static void createFile(String path, String data, String encoding, Promise promise) {
|
|
839
860
|
try {
|
|
861
|
+
path = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
840
862
|
File dest = new File(path);
|
|
841
863
|
boolean created = dest.createNewFile();
|
|
842
864
|
if (encoding.equals(ReactNativeBlobUtilConst.DATA_ENCODE_URI)) {
|
|
@@ -879,6 +901,7 @@ class ReactNativeBlobUtilFS {
|
|
|
879
901
|
*/
|
|
880
902
|
static void createFileASCII(String path, ReadableArray data, Promise promise) {
|
|
881
903
|
try {
|
|
904
|
+
path = ReactNativeBlobUtilUtils.normalizePath(path);
|
|
882
905
|
File dest = new File(path);
|
|
883
906
|
boolean created = dest.createNewFile();
|
|
884
907
|
if (!created) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
package com.ReactNativeBlobUtil;
|
|
2
|
+
|
|
3
|
+
public class ReactNativeBlobUtilFileTransformer {
|
|
4
|
+
public interface FileTransformer {
|
|
5
|
+
public byte[] onWriteFile(byte[] data);
|
|
6
|
+
public byte[] onReadFile(byte[] data);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public static ReactNativeBlobUtilFileTransformer.FileTransformer sharedFileTransformer;
|
|
10
|
+
}
|
package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java
CHANGED
|
@@ -124,7 +124,7 @@ public class ReactNativeBlobUtilMediaCollection {
|
|
|
124
124
|
return null;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
public static boolean writeToMediaFile(Uri fileUri, String data, Promise promise) {
|
|
127
|
+
public static boolean writeToMediaFile(Uri fileUri, String data, boolean transformFile, Promise promise) {
|
|
128
128
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
129
129
|
try {
|
|
130
130
|
Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
|
|
@@ -151,16 +151,31 @@ public class ReactNativeBlobUtilMediaCollection {
|
|
|
151
151
|
promise.reject("ENOENT", "No such file ('" + normalizedData + "')");
|
|
152
152
|
return false;
|
|
153
153
|
}
|
|
154
|
-
|
|
155
|
-
int read;
|
|
154
|
+
|
|
156
155
|
|
|
157
156
|
FileInputStream fin = new FileInputStream(src);
|
|
158
157
|
FileOutputStream out = new FileOutputStream(descr.getFileDescriptor());
|
|
159
158
|
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
if (transformFile) {
|
|
160
|
+
// in order to transform file, we must load the entire file onto memory
|
|
161
|
+
int length = (int) src.length();
|
|
162
|
+
byte[] bytes = new byte[length];
|
|
163
|
+
fin.read(bytes);
|
|
164
|
+
if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
|
|
165
|
+
throw new IllegalStateException("Write to media file with transform was specified but the shared file transformer is not set");
|
|
166
|
+
}
|
|
167
|
+
byte[] transformedBytes = ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onWriteFile(bytes);
|
|
168
|
+
out.write(transformedBytes);
|
|
169
|
+
} else {
|
|
170
|
+
byte[] buf = new byte[10240];
|
|
171
|
+
int read;
|
|
172
|
+
|
|
173
|
+
while ((read = fin.read(buf)) > 0) {
|
|
174
|
+
out.write(buf, 0, read);
|
|
175
|
+
}
|
|
162
176
|
}
|
|
163
177
|
|
|
178
|
+
|
|
164
179
|
fin.close();
|
|
165
180
|
out.close();
|
|
166
181
|
descr.close();
|
|
@@ -12,6 +12,9 @@ import android.net.NetworkCapabilities;
|
|
|
12
12
|
import android.net.NetworkInfo;
|
|
13
13
|
import android.net.Uri;
|
|
14
14
|
import android.os.Build;
|
|
15
|
+
import android.os.Bundle;
|
|
16
|
+
import android.os.Handler;
|
|
17
|
+
import android.os.Message;
|
|
15
18
|
import android.util.Base64;
|
|
16
19
|
import android.webkit.CookieManager;
|
|
17
20
|
|
|
@@ -48,6 +51,9 @@ import java.util.HashMap;
|
|
|
48
51
|
|
|
49
52
|
import java.util.List;
|
|
50
53
|
import java.util.Locale;
|
|
54
|
+
import java.util.concurrent.Executors;
|
|
55
|
+
import java.util.concurrent.Future;
|
|
56
|
+
import java.util.concurrent.ScheduledExecutorService;
|
|
51
57
|
import java.util.concurrent.TimeUnit;
|
|
52
58
|
|
|
53
59
|
import javax.net.ssl.SSLContext;
|
|
@@ -87,6 +93,12 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
|
|
|
87
93
|
BASE64
|
|
88
94
|
}
|
|
89
95
|
|
|
96
|
+
private boolean shouldTransformFile() {
|
|
97
|
+
return this.options.transformFile &&
|
|
98
|
+
// Can only process if it's written to a file
|
|
99
|
+
(this.options.fileCache || this.options.path != null);
|
|
100
|
+
}
|
|
101
|
+
|
|
90
102
|
public static HashMap<String, Call> taskTable = new HashMap<>();
|
|
91
103
|
public static HashMap<String, Long> androidDownloadManagerTaskTable = new HashMap<>();
|
|
92
104
|
static HashMap<String, ReactNativeBlobUtilProgressConfig> progressReport = new HashMap<>();
|
|
@@ -124,7 +136,9 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
|
|
|
124
136
|
this.rawRequestBodyArray = arrayBody;
|
|
125
137
|
this.client = client;
|
|
126
138
|
|
|
127
|
-
|
|
139
|
+
// If transformFile is specified, we first want to get the response back in memory so we can
|
|
140
|
+
// encrypt it wholesale and at that point, write it into the file storage.
|
|
141
|
+
if((this.options.fileCache || this.options.path != null) && !this.shouldTransformFile())
|
|
128
142
|
responseType = ResponseType.FileStorage;
|
|
129
143
|
else
|
|
130
144
|
responseType = ResponseType.KeepInMemory;
|
|
@@ -153,6 +167,60 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
|
|
|
153
167
|
}
|
|
154
168
|
}
|
|
155
169
|
|
|
170
|
+
private final int QUERY = 1314;
|
|
171
|
+
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
|
|
172
|
+
private Future<?> future;
|
|
173
|
+
private Handler mHandler = new Handler(new Handler.Callback() {
|
|
174
|
+
public boolean handleMessage(Message msg) {
|
|
175
|
+
switch (msg.what) {
|
|
176
|
+
|
|
177
|
+
case QUERY:
|
|
178
|
+
|
|
179
|
+
Bundle data = msg.getData();
|
|
180
|
+
long id = data.getLong("downloadManagerId");
|
|
181
|
+
if (id == downloadManagerId) {
|
|
182
|
+
|
|
183
|
+
Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
|
|
184
|
+
|
|
185
|
+
DownloadManager downloadManager = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
186
|
+
|
|
187
|
+
DownloadManager.Query query = new DownloadManager.Query();
|
|
188
|
+
query.setFilterById(downloadManagerId);
|
|
189
|
+
|
|
190
|
+
Cursor cursor = downloadManager.query(query);
|
|
191
|
+
|
|
192
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
193
|
+
|
|
194
|
+
long written = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
|
|
195
|
+
|
|
196
|
+
long total = cursor.getLong(cursor.getColumnIndex(
|
|
197
|
+
DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
|
|
198
|
+
cursor.close();
|
|
199
|
+
|
|
200
|
+
ReactNativeBlobUtilProgressConfig reportConfig = getReportProgress(taskId);
|
|
201
|
+
float progress = (total > 0) ? written / total : 0;
|
|
202
|
+
|
|
203
|
+
if (reportConfig != null && reportConfig.shouldReport(progress /* progress */)) {
|
|
204
|
+
WritableMap args = Arguments.createMap();
|
|
205
|
+
args.putString("taskId", String.valueOf(taskId));
|
|
206
|
+
args.putString("written", String.valueOf(written));
|
|
207
|
+
args.putString("total", String.valueOf(total));
|
|
208
|
+
args.putString("chunk", "");
|
|
209
|
+
ReactNativeBlobUtil.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
210
|
+
.emit(ReactNativeBlobUtilConst.EVENT_PROGRESS, args);
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (total == written) {
|
|
215
|
+
future.cancel(true);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
156
224
|
@Override
|
|
157
225
|
public void run() {
|
|
158
226
|
|
|
@@ -204,6 +272,17 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
|
|
|
204
272
|
downloadManagerId = dm.enqueue(req);
|
|
205
273
|
androidDownloadManagerTaskTable.put(taskId, Long.valueOf(downloadManagerId));
|
|
206
274
|
appCtx.registerReceiver(this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
|
275
|
+
future = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
|
|
276
|
+
@Override
|
|
277
|
+
public void run() {
|
|
278
|
+
Message msg = mHandler.obtainMessage();
|
|
279
|
+
Bundle data = new Bundle();
|
|
280
|
+
data.putLong("downloadManagerId", downloadManagerId);
|
|
281
|
+
msg.setData(data);
|
|
282
|
+
msg.what = QUERY;
|
|
283
|
+
mHandler.sendMessage(msg);
|
|
284
|
+
}
|
|
285
|
+
}, 0, 100, TimeUnit.MILLISECONDS);
|
|
207
286
|
return;
|
|
208
287
|
}
|
|
209
288
|
|
|
@@ -557,6 +636,26 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
|
|
|
557
636
|
// response data directly pass to JS context as string.
|
|
558
637
|
else {
|
|
559
638
|
byte[] b = resp.body().bytes();
|
|
639
|
+
// If process file option is turned on, we first keep response in memory and then stream that content
|
|
640
|
+
// after processing
|
|
641
|
+
if (this.shouldTransformFile()) {
|
|
642
|
+
if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
|
|
643
|
+
throw new IllegalStateException("Write file with transform was specified but the shared file transformer is not set");
|
|
644
|
+
}
|
|
645
|
+
this.destPath = this.destPath.replace("?append=true", "");
|
|
646
|
+
File file = new File(this.destPath);
|
|
647
|
+
if (!file.exists()) {
|
|
648
|
+
file.createNewFile();
|
|
649
|
+
}
|
|
650
|
+
try (FileOutputStream fos = new FileOutputStream(file)) {
|
|
651
|
+
fos.write(ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onWriteFile(b));
|
|
652
|
+
} catch(Exception e) {
|
|
653
|
+
callback.invoke("Error from file transformer:" + e.getLocalizedMessage(), null);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
callback.invoke(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, this.destPath);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
560
659
|
if (responseFormat == ResponseFormat.BASE64) {
|
|
561
660
|
callback.invoke(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_BASE64, android.util.Base64.encodeToString(b, Base64.NO_WRAP));
|
|
562
661
|
return;
|
|
@@ -10,7 +10,6 @@ import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
|
10
10
|
|
|
11
11
|
import java.nio.charset.Charset;
|
|
12
12
|
import java.security.MessageDigest;
|
|
13
|
-
import java.security.cert.CertificateException;
|
|
14
13
|
import java.util.Locale;
|
|
15
14
|
|
|
16
15
|
import javax.net.ssl.HostnameVerifier;
|
|
@@ -22,9 +21,10 @@ import javax.net.ssl.X509TrustManager;
|
|
|
22
21
|
|
|
23
22
|
import okhttp3.OkHttpClient;
|
|
24
23
|
|
|
25
|
-
|
|
26
24
|
public class ReactNativeBlobUtilUtils {
|
|
27
25
|
|
|
26
|
+
public static X509TrustManager sharedTrustManager;
|
|
27
|
+
|
|
28
28
|
public static String getMD5(String input) {
|
|
29
29
|
String result = null;
|
|
30
30
|
|
|
@@ -61,22 +61,10 @@ public class ReactNativeBlobUtilUtils {
|
|
|
61
61
|
|
|
62
62
|
public static OkHttpClient.Builder getUnsafeOkHttpClient(OkHttpClient client) {
|
|
63
63
|
try {
|
|
64
|
-
// Create a trust manager that does not validate certificate chains
|
|
65
|
-
final X509TrustManager x509TrustManager = new X509TrustManager() {
|
|
66
|
-
@Override
|
|
67
|
-
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
|
68
|
-
}
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
|
72
|
-
}
|
|
65
|
+
if (sharedTrustManager == null) throw new IllegalStateException("Use of own trust manager but none defined");
|
|
73
66
|
|
|
74
|
-
|
|
75
|
-
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
|
76
|
-
return new java.security.cert.X509Certificate[]{};
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
final TrustManager[] trustAllCerts = new TrustManager[]{x509TrustManager};
|
|
67
|
+
final TrustManager[] trustAllCerts = new TrustManager[]{sharedTrustManager};
|
|
80
68
|
|
|
81
69
|
// Install the all-trusting trust manager
|
|
82
70
|
final SSLContext sslContext = SSLContext.getInstance("SSL");
|
|
@@ -85,7 +73,7 @@ public class ReactNativeBlobUtilUtils {
|
|
|
85
73
|
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
|
86
74
|
|
|
87
75
|
OkHttpClient.Builder builder = client.newBuilder();
|
|
88
|
-
builder.sslSocketFactory(sslSocketFactory,
|
|
76
|
+
builder.sslSocketFactory(sslSocketFactory, sharedTrustManager);
|
|
89
77
|
builder.hostnameVerifier(new HostnameVerifier() {
|
|
90
78
|
@Override
|
|
91
79
|
public boolean verify(String hostname, SSLSession session) {
|