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.
Files changed (33) hide show
  1. package/README.md +101 -1
  2. package/android/src/main/AndroidManifest.xml +0 -3
  3. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java +7 -7
  4. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilBody.java +30 -6
  5. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +2 -0
  6. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java +25 -2
  7. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +10 -0
  8. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java +20 -5
  9. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +100 -1
  10. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilUtils.java +5 -17
  11. package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +4 -0
  12. package/class/ReactNativeBlobUtilBlobResponse.js +1 -1
  13. package/fs.js +42 -3
  14. package/index.d.ts +30 -1
  15. package/index.js +2 -2
  16. package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.m +4 -3
  17. package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +6 -2
  18. package/ios/ReactNativeBlobUtilConst.h +1 -0
  19. package/ios/ReactNativeBlobUtilConst.m +1 -0
  20. package/ios/ReactNativeBlobUtilFS.h +2 -1
  21. package/ios/ReactNativeBlobUtilFS.m +30 -2
  22. package/ios/ReactNativeBlobUtilFileTransformer.h +24 -0
  23. package/ios/ReactNativeBlobUtilFileTransformer.m +21 -0
  24. package/ios/ReactNativeBlobUtilReqBuilder.m +3 -4
  25. package/ios/ReactNativeBlobUtilRequest.m +35 -7
  26. package/mediacollection.js +6 -1
  27. package/package.json +7 -2
  28. package/polyfill/Fetch.js +4 -2
  29. package/polyfill/XMLHttpRequest.js +6 -0
  30. package/types.js +1 -0
  31. package/ios/IOS7Polyfill.h +0 -27
  32. package/react-native.config.js +0 -7
  33. 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.html).
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
- requestStream = getRequestStream();
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(requestStream, sink);
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
+ }
@@ -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
- byte[] buf = new byte[10240];
155
- int read;
154
+
156
155
 
157
156
  FileInputStream fin = new FileInputStream(src);
158
157
  FileOutputStream out = new FileOutputStream(descr.getFileDescriptor());
159
158
 
160
- while ((read = fin.read(buf)) > 0) {
161
- out.write(buf, 0, read);
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
- if (this.options.fileCache || this.options.path != null)
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
- @Override
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
- @Override
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, x509TrustManager);
76
+ builder.sslSocketFactory(sslSocketFactory, sharedTrustManager);
89
77
  builder.hostnameVerifier(new HostnameVerifier() {
90
78
  @Override
91
79
  public boolean verify(String hostname, SSLSession session) {