react-native-blob-util 0.13.18 → 0.15.0

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 (34) hide show
  1. package/README.md +542 -401
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java +89 -25
  4. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilBody.java +14 -15
  5. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +10 -6
  6. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java +115 -289
  7. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +10 -0
  8. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java +314 -0
  9. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilPackage.java +6 -2
  10. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +46 -22
  11. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilStream.java +287 -0
  12. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilUtils.java +57 -2
  13. package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +9 -0
  14. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileDescription.java +19 -0
  15. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/MimeType.java +70 -0
  16. package/class/ReactNativeBlobUtilBlobResponse.js +1 -1
  17. package/fs.js +44 -4
  18. package/index.d.ts +121 -1
  19. package/index.js +2 -0
  20. package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.m +6 -4
  21. package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +6 -0
  22. package/ios/ReactNativeBlobUtilConst.h +1 -0
  23. package/ios/ReactNativeBlobUtilConst.m +1 -0
  24. package/ios/ReactNativeBlobUtilFS.h +3 -1
  25. package/ios/ReactNativeBlobUtilFS.m +33 -0
  26. package/ios/ReactNativeBlobUtilFileTransformer.h +24 -0
  27. package/ios/ReactNativeBlobUtilFileTransformer.m +21 -0
  28. package/ios/ReactNativeBlobUtilReqBuilder.m +2 -2
  29. package/ios/ReactNativeBlobUtilRequest.m +30 -1
  30. package/mediacollection.js +38 -0
  31. package/package.json +1 -1
  32. package/polyfill/Fetch.js +4 -2
  33. package/scripts/prelink.js +1 -1
  34. package/types.js +4 -0
@@ -0,0 +1,314 @@
1
+ package com.ReactNativeBlobUtil;
2
+
3
+ import android.content.ContentResolver;
4
+ import android.content.ContentValues;
5
+ import android.content.Context;
6
+ import android.net.Uri;
7
+ import android.os.Build;
8
+ import android.os.Environment;
9
+ import android.os.ParcelFileDescriptor;
10
+ import android.provider.MediaStore;
11
+ import android.util.Base64;
12
+
13
+ import com.ReactNativeBlobUtil.Utils.FileDescription;
14
+ import com.facebook.react.bridge.Arguments;
15
+ import com.facebook.react.bridge.Promise;
16
+ import com.facebook.react.bridge.ReactApplicationContext;
17
+ import com.facebook.react.bridge.WritableArray;
18
+
19
+ import java.io.File;
20
+ import java.io.FileInputStream;
21
+ import java.io.FileOutputStream;
22
+ import java.io.IOException;
23
+ import java.io.InputStream;
24
+ import java.io.OutputStream;
25
+
26
+ public class ReactNativeBlobUtilMediaCollection {
27
+
28
+ public enum MediaType {
29
+ Audio,
30
+ Image,
31
+ Video,
32
+ Download
33
+ }
34
+
35
+ private static Uri getMediaUri(MediaType mt) {
36
+ Uri res = null;
37
+ if (mt == MediaType.Audio) {
38
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
39
+ res = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
40
+ } else {
41
+ res = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
42
+ }
43
+ } else if (mt == MediaType.Video) {
44
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
45
+ res = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
46
+ } else {
47
+ res = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
48
+ }
49
+ } else if (mt == MediaType.Image) {
50
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
51
+ res = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
52
+ } else {
53
+ res = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
54
+ }
55
+ } else if (mt == MediaType.Download) {
56
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
57
+ res = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
58
+ }
59
+ }
60
+
61
+ return res;
62
+ }
63
+
64
+ private static String getRelativePath(MediaType mt, ReactApplicationContext ctx) {
65
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
66
+ if (mt == MediaType.Audio) return Environment.DIRECTORY_MUSIC;
67
+ if (mt == MediaType.Video) return Environment.DIRECTORY_MOVIES;
68
+ if (mt == MediaType.Image) return Environment.DIRECTORY_PICTURES;
69
+ if (mt == MediaType.Download) return Environment.DIRECTORY_DOWNLOADS;
70
+ return Environment.DIRECTORY_DOWNLOADS;
71
+ } else {
72
+ if (mt == MediaType.Audio) return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_MUSIC);
73
+ if (mt == MediaType.Video) return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_MOVIES);
74
+ if (mt == MediaType.Image) return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_PICTURES);
75
+ if (mt == MediaType.Download) return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_DOWNLOADS);
76
+ return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_DOWNLOADS);
77
+ }
78
+ }
79
+
80
+ public static Uri createNewMediaFile(FileDescription file, MediaType mt, ReactApplicationContext ctx) {
81
+ // Add a specific media item.
82
+ Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
83
+ ContentResolver resolver = appCtx.getContentResolver();
84
+
85
+ ContentValues fileDetails = new ContentValues();
86
+ String relativePath = getRelativePath(mt, ctx);
87
+ String mimeType = file.mimeType;
88
+
89
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
90
+ fileDetails.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
91
+ fileDetails.put(MediaStore.MediaColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
92
+ fileDetails.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
93
+ fileDetails.put(MediaStore.MediaColumns.DISPLAY_NAME, file.name);
94
+ fileDetails.put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath + '/' + file.partentFolder);
95
+
96
+ Uri mediauri = getMediaUri(mt);
97
+
98
+ // Keeps a handle to the new file's URI in case we need to modify it later.
99
+ return resolver.insert(mediauri, fileDetails);
100
+ } else {
101
+ File f = new File(relativePath + file.getFullPath());
102
+ if (true) {
103
+ if (!f.exists()) {
104
+ File parent = f.getParentFile();
105
+ if (parent != null && !parent.exists() && !parent.mkdirs()) {
106
+ return null;
107
+ }
108
+ try {
109
+ if (f.createNewFile()) ;
110
+ {
111
+ return Uri.fromFile(f);
112
+ }
113
+ } catch (IOException ioException) {
114
+ return null;
115
+ }
116
+
117
+ } else {
118
+ return Uri.fromFile(f);
119
+ }
120
+
121
+ }
122
+ }
123
+
124
+ return null;
125
+ }
126
+
127
+ public static boolean writeToMediaFile(Uri fileUri, String data, boolean transformFile, Promise promise) {
128
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
129
+ try {
130
+ Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
131
+ ContentResolver resolver = appCtx.getContentResolver();
132
+
133
+ // set pending doesn't work right now. We would have to requery for the item
134
+ //ContentValues contentValues = new ContentValues();
135
+ //contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
136
+ //resolver.update(fileUri, contentValues, null, null);
137
+
138
+ // write data
139
+ OutputStream stream = null;
140
+ Uri uri = null;
141
+
142
+ try {
143
+ ParcelFileDescriptor descr;
144
+ try {
145
+ assert fileUri != null;
146
+ descr = appCtx.getContentResolver().openFileDescriptor(fileUri, "w");
147
+ assert descr != null;
148
+ String normalizedData = ReactNativeBlobUtilUtils.normalizePath(data);
149
+ File src = new File(normalizedData);
150
+ if (!src.exists()) {
151
+ promise.reject("ENOENT", "No such file ('" + normalizedData + "')");
152
+ return false;
153
+ }
154
+
155
+
156
+ FileInputStream fin = new FileInputStream(src);
157
+ FileOutputStream out = new FileOutputStream(descr.getFileDescriptor());
158
+
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
+ }
176
+ }
177
+
178
+
179
+ fin.close();
180
+ out.close();
181
+ descr.close();
182
+ } catch (Exception e) {
183
+ e.printStackTrace();
184
+ promise.reject(new IOException("Failed to get output stream."));
185
+ return false;
186
+ }
187
+
188
+ //contentValues.clear();
189
+ //contentValues.put(MediaStore.Video.Media.IS_PENDING, 0);
190
+ //appCtx.getContentResolver().update(fileUri, contentValues, null, null);
191
+ stream = resolver.openOutputStream(fileUri);
192
+ if (stream == null) {
193
+ promise.reject(new IOException("Failed to get output stream."));
194
+ return false;
195
+ }
196
+ } catch (IOException e) {
197
+ // Don't leave an orphan entry in the MediaStore
198
+ resolver.delete(uri, null, null);
199
+ promise.reject(e);
200
+ return false;
201
+ } finally {
202
+ if (stream != null) {
203
+ stream.close();
204
+ }
205
+ }
206
+
207
+ // remove pending
208
+ //contentValues = new ContentValues();
209
+ //contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
210
+ //resolver.update(fileUri, contentValues, null, null);
211
+
212
+ } catch (IOException e) {
213
+ promise.reject("ReactNativeBlobUtil.createMediaFile", "Cannot write to file, file might not exist");
214
+ return false;
215
+ }
216
+ return true;
217
+ } else {
218
+ return ReactNativeBlobUtilFS.writeFile(ReactNativeBlobUtilUtils.normalizePath(fileUri.toString()), ReactNativeBlobUtilConst.DATA_ENCODE_URI, data, false);
219
+ }
220
+ }
221
+
222
+ public static void copyToInternal(Uri contenturi, String destpath, Promise promise) {
223
+ Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
224
+ ContentResolver resolver = appCtx.getContentResolver();
225
+
226
+ InputStream in = null;
227
+ OutputStream out = null;
228
+ File f = new File((destpath));
229
+
230
+ if (!f.exists()) {
231
+ try {
232
+ File parent = f.getParentFile();
233
+ if (parent != null && !parent.exists() && !parent.mkdirs()) {
234
+ promise.reject("ReactNativeBlobUtil.copyToInternal: Cannot create parent folders<'" + destpath);
235
+ return;
236
+ }
237
+ boolean result = f.createNewFile();
238
+ if (!result) {
239
+ promise.reject("ReactNativeBlobUtil.copyToInternal: Destination file at '" + destpath + "' already exists");
240
+ return;
241
+ }
242
+ } catch (IOException ioException) {
243
+ promise.reject("ReactNativeBlobUtil.copyToInternal: Could not create file: " + ioException.getLocalizedMessage());
244
+ }
245
+ }
246
+
247
+ try {
248
+ in = resolver.openInputStream(contenturi);
249
+ out = new FileOutputStream(destpath);
250
+
251
+ byte[] buf = new byte[10240];
252
+ int len;
253
+ while ((len = in.read(buf)) > 0) {
254
+ out.write(buf, 0, len);
255
+ }
256
+
257
+ } catch (IOException e) {
258
+ promise.reject("ReactNativeBlobUtil.copyToInternal: Could not write data: " + e.getLocalizedMessage());
259
+ } finally {
260
+ if (in != null) {
261
+ try {
262
+ in.close();
263
+ } catch (IOException ioException) {
264
+ ioException.printStackTrace();
265
+ }
266
+ }
267
+ if (out != null) {
268
+ try {
269
+ out.close();
270
+ } catch (IOException ioException) {
271
+ ioException.printStackTrace();
272
+ }
273
+ }
274
+ }
275
+
276
+ promise.resolve("");
277
+ }
278
+
279
+ public static void getBlob(Uri contentUri, String encoding, Promise promise) {
280
+ Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
281
+ ContentResolver resolver = appCtx.getContentResolver();
282
+ try {
283
+ InputStream in = resolver.openInputStream(contentUri);
284
+ int length = in.available();
285
+
286
+ byte[] bytes = new byte[length];
287
+ int bytesRead = in.read(bytes);
288
+ in.close();
289
+
290
+ if (bytesRead < length) {
291
+ promise.reject("EUNSPECIFIED", "Read only " + bytesRead + " bytes of " + length);
292
+ return;
293
+ }
294
+
295
+ switch (encoding.toLowerCase()) {
296
+ case "base64":
297
+ promise.resolve(Base64.encodeToString(bytes, Base64.NO_WRAP));
298
+ break;
299
+ case "ascii":
300
+ WritableArray asciiResult = Arguments.createArray();
301
+ for (byte b : bytes) {
302
+ asciiResult.pushInt(b);
303
+ }
304
+ promise.resolve(asciiResult);
305
+ break;
306
+ default: // covers utf-8
307
+ promise.resolve(new String(bytes));
308
+ break;
309
+ }
310
+ } catch (IOException ioException) {
311
+ ioException.printStackTrace();
312
+ }
313
+ }
314
+ }
@@ -1,5 +1,7 @@
1
1
  package com.ReactNativeBlobUtil;
2
2
 
3
+ import androidx.annotation.NonNull;
4
+
3
5
  import com.facebook.react.ReactPackage;
4
6
  import com.facebook.react.bridge.JavaScriptModule;
5
7
  import com.facebook.react.bridge.NativeModule;
@@ -13,8 +15,9 @@ import java.util.List;
13
15
 
14
16
  public class ReactNativeBlobUtilPackage implements ReactPackage {
15
17
 
18
+ @NonNull
16
19
  @Override
17
- public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
20
+ public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
18
21
  List<NativeModule> modules = new ArrayList<>();
19
22
  modules.add(new ReactNativeBlobUtil(reactContext));
20
23
  return modules;
@@ -24,8 +27,9 @@ public class ReactNativeBlobUtilPackage implements ReactPackage {
24
27
  return Collections.emptyList();
25
28
  }
26
29
 
30
+ @NonNull
27
31
  @Override
28
- public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
32
+ public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
29
33
  return Collections.emptyList();
30
34
  }
31
35
 
@@ -30,33 +30,27 @@ import com.facebook.react.bridge.WritableArray;
30
30
  import com.facebook.react.bridge.WritableMap;
31
31
  import com.facebook.react.modules.core.DeviceEventManagerModule;
32
32
 
33
- import javax.net.ssl.TrustManagerFactory;
34
- import javax.net.ssl.TrustManager;
35
- import javax.net.ssl.X509TrustManager;
36
- import javax.net.ssl.SSLContext;
37
-
38
33
  import java.io.File;
39
34
  import java.io.FileOutputStream;
40
35
  import java.io.IOException;
41
36
  import java.io.InputStream;
42
37
  import java.net.MalformedURLException;
38
+ import java.net.Proxy;
43
39
  import java.net.SocketException;
44
40
  import java.net.SocketTimeoutException;
45
41
  import java.net.URL;
46
- import java.net.Proxy;
47
42
  import java.nio.ByteBuffer;
48
43
  import java.nio.charset.CharacterCodingException;
49
44
  import java.nio.charset.Charset;
50
45
  import java.nio.charset.CharsetDecoder;
51
- import java.security.KeyStore;
52
46
  import java.util.ArrayList;
53
- import java.util.Arrays;
54
- import java.util.List;
55
47
  import java.util.HashMap;
56
48
 
49
+ import java.util.List;
50
+ import java.util.Locale;
57
51
  import java.util.concurrent.TimeUnit;
58
52
 
59
- import javax.net.ssl.SSLSocketFactory;
53
+ import javax.net.ssl.SSLContext;
60
54
 
61
55
  import okhttp3.Call;
62
56
  import okhttp3.ConnectionPool;
@@ -93,6 +87,12 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
93
87
  BASE64
94
88
  }
95
89
 
90
+ private boolean shouldTransformFile() {
91
+ return this.options.transformFile &&
92
+ // Can only process if it's written to a file
93
+ (this.options.fileCache || this.options.path != null);
94
+ }
95
+
96
96
  public static HashMap<String, Call> taskTable = new HashMap<>();
97
97
  public static HashMap<String, Long> androidDownloadManagerTaskTable = new HashMap<>();
98
98
  static HashMap<String, ReactNativeBlobUtilProgressConfig> progressReport = new HashMap<>();
@@ -120,7 +120,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
120
120
  OkHttpClient client;
121
121
 
122
122
  public ReactNativeBlobUtilReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, OkHttpClient client, final Callback callback) {
123
- this.method = method.toUpperCase();
123
+ this.method = method.toUpperCase(Locale.ROOT);
124
124
  this.options = new ReactNativeBlobUtilConfig(options);
125
125
  this.taskId = taskId;
126
126
  this.url = url;
@@ -130,7 +130,9 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
130
130
  this.rawRequestBodyArray = arrayBody;
131
131
  this.client = client;
132
132
 
133
- if (this.options.fileCache || this.options.path != null)
133
+ // If transformFile is specified, we first want to get the response back in memory so we can
134
+ // encrypt it wholesale and at that point, write it into the file storage.
135
+ if((this.options.fileCache || this.options.path != null) && !this.shouldTransformFile())
134
136
  responseType = ResponseType.FileStorage;
135
137
  else
136
138
  responseType = ResponseType.KeepInMemory;
@@ -196,7 +198,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
196
198
  req.addRequestHeader(key, headers.getString(key));
197
199
  }
198
200
  // Attempt to add cookie, if it exists
199
- URL urlObj = null;
201
+ URL urlObj;
200
202
  try {
201
203
  urlObj = new URL(url);
202
204
  String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost();
@@ -311,14 +313,14 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
311
313
  else if (value.equalsIgnoreCase("utf8"))
312
314
  responseFormat = ResponseFormat.UTF8;
313
315
  } else {
314
- builder.header(key.toLowerCase(), value);
315
- mheaders.put(key.toLowerCase(), value);
316
+ builder.header(key.toLowerCase(Locale.ROOT), value);
317
+ mheaders.put(key.toLowerCase(Locale.ROOT), value);
316
318
  }
317
319
  }
318
320
  }
319
321
 
320
322
  if (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
321
- String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase();
323
+ String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase(Locale.ROOT);
322
324
 
323
325
  if (rawRequestBodyArray != null) {
324
326
  requestType = RequestType.Form;
@@ -332,7 +334,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
332
334
  if (rawRequestBody.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX)
333
335
  || rawRequestBody.startsWith(ReactNativeBlobUtilConst.CONTENT_PREFIX)) {
334
336
  requestType = RequestType.SingleFile;
335
- } else if (cType.toLowerCase().contains(";base64") || cType.toLowerCase().startsWith("application/octet")) {
337
+ } else if (cType.toLowerCase(Locale.ROOT).contains(";base64") || cType.toLowerCase(Locale.ROOT).startsWith("application/octet")) {
336
338
  cType = cType.replace(";base64", "").replace(";BASE64", "");
337
339
  if (mheaders.containsKey("content-type"))
338
340
  mheaders.put("content-type", cType);
@@ -388,14 +390,16 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
388
390
  // #156 fix cookie issue
389
391
  final Request req = builder.build();
390
392
  clientBuilder.addNetworkInterceptor(new Interceptor() {
393
+ @NonNull
391
394
  @Override
392
- public Response intercept(Chain chain) throws IOException {
395
+ public Response intercept(@NonNull Chain chain) throws IOException {
393
396
  redirects.add(chain.request().url().toString());
394
397
  return chain.proceed(chain.request());
395
398
  }
396
399
  });
397
400
  // Add request interceptor for upload progress event
398
401
  clientBuilder.addInterceptor(new Interceptor() {
402
+ @NonNull
399
403
  @Override
400
404
  public Response intercept(@NonNull Chain chain) throws IOException {
401
405
  Response originalResponse = null;
@@ -467,7 +471,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
467
471
  call.enqueue(new okhttp3.Callback() {
468
472
 
469
473
  @Override
470
- public void onFailure(@NonNull Call call, IOException e) {
474
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
471
475
  cancelTask(taskId);
472
476
  if (respInfo == null) {
473
477
  respInfo = Arguments.createMap();
@@ -561,6 +565,26 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
561
565
  // response data directly pass to JS context as string.
562
566
  else {
563
567
  byte[] b = resp.body().bytes();
568
+ // If process file option is turned on, we first keep response in memory and then stream that content
569
+ // after processing
570
+ if (this.shouldTransformFile()) {
571
+ if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
572
+ throw new IllegalStateException("Write file with transform was specified but the shared file transformer is not set");
573
+ }
574
+ this.destPath = this.destPath.replace("?append=true", "");
575
+ File file = new File(this.destPath);
576
+ if (!file.exists()) {
577
+ file.createNewFile();
578
+ }
579
+ try (FileOutputStream fos = new FileOutputStream(file)) {
580
+ fos.write(ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onWriteFile(b));
581
+ } catch(Exception e) {
582
+ callback.invoke("Error from file transformer:" + e.getLocalizedMessage(), null);
583
+ return;
584
+ }
585
+ callback.invoke(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, this.destPath);
586
+ return;
587
+ }
564
588
  if (responseFormat == ResponseFormat.BASE64) {
565
589
  callback.invoke(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_BASE64, android.util.Base64.encodeToString(b, Base64.NO_WRAP));
566
590
  return;
@@ -718,7 +742,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
718
742
  boolean isCustomBinary = false;
719
743
  if (options.binaryContentTypes != null) {
720
744
  for (int i = 0; i < options.binaryContentTypes.size(); i++) {
721
- if (ctype.toLowerCase().contains(options.binaryContentTypes.getString(i).toLowerCase())) {
745
+ if (ctype.toLowerCase(Locale.ROOT).contains(options.binaryContentTypes.getString(i).toLowerCase(Locale.ROOT))) {
722
746
  isCustomBinary = true;
723
747
  break;
724
748
  }
@@ -730,13 +754,13 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
730
754
  private String getHeaderIgnoreCases(Headers headers, String field) {
731
755
  String val = headers.get(field);
732
756
  if (val != null) return val;
733
- return headers.get(field.toLowerCase()) == null ? "" : headers.get(field.toLowerCase());
757
+ return headers.get(field.toLowerCase(Locale.ROOT)) == null ? "" : headers.get(field.toLowerCase(Locale.ROOT));
734
758
  }
735
759
 
736
760
  private String getHeaderIgnoreCases(HashMap<String, String> headers, String field) {
737
761
  String val = headers.get(field);
738
762
  if (val != null) return val;
739
- String lowerCasedValue = headers.get(field.toLowerCase());
763
+ String lowerCasedValue = headers.get(field.toLowerCase(Locale.ROOT));
740
764
  return lowerCasedValue == null ? "" : lowerCasedValue;
741
765
  }
742
766