react-native-blob-util 0.13.15 → 0.14.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 (26) hide show
  1. package/README.md +472 -407
  2. package/android/build.gradle +49 -39
  3. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java +98 -41
  4. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilBody.java +14 -15
  5. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +8 -6
  6. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java +23 -291
  7. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java +278 -0
  8. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilPackage.java +6 -2
  9. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +29 -23
  10. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilStream.java +287 -0
  11. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilUtils.java +57 -2
  12. package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +5 -0
  13. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileDescription.java +19 -0
  14. package/android/src/main/java/com/ReactNativeBlobUtil/Utils/MimeType.java +70 -0
  15. package/fetch.js +1 -1
  16. package/fs.js +2 -1
  17. package/index.d.ts +108 -3
  18. package/index.js +2 -0
  19. package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.m +2 -1
  20. package/ios/ReactNativeBlobUtilFS.h +1 -0
  21. package/ios/ReactNativeBlobUtilFS.m +4 -0
  22. package/ios/ReactNativeBlobUtilReqBuilder.m +1 -0
  23. package/mediacollection.js +33 -0
  24. package/package.json +1 -1
  25. package/scripts/prelink.js +1 -1
  26. package/types.js +3 -0
@@ -0,0 +1,278 @@
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 androidx.annotation.RequiresApi;
14
+
15
+ import com.ReactNativeBlobUtil.Utils.FileDescription;
16
+ import com.facebook.react.bridge.Arguments;
17
+ import com.facebook.react.bridge.Promise;
18
+ import com.facebook.react.bridge.WritableArray;
19
+
20
+ import java.io.File;
21
+ import java.io.FileInputStream;
22
+ import java.io.FileOutputStream;
23
+ import java.io.IOException;
24
+ import java.io.InputStream;
25
+ import java.io.OutputStream;
26
+
27
+ public class ReactNativeBlobUtilMediaCollection {
28
+
29
+ public enum MediaType {
30
+ Audio,
31
+ Image,
32
+ Video,
33
+ Download
34
+ }
35
+
36
+ private static Uri getMediaUri(MediaType mt) {
37
+ Uri res = null;
38
+ if (mt == MediaType.Audio) {
39
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
40
+ res = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
41
+ } else {
42
+ res = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
43
+ }
44
+ } else if (mt == MediaType.Video) {
45
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
46
+ res = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
47
+ } else {
48
+ res = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
49
+ }
50
+ } else if (mt == MediaType.Image) {
51
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
52
+ res = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
53
+ } else {
54
+ res = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
55
+ }
56
+ } else if (mt == MediaType.Download) {
57
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
58
+ res = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
59
+ }
60
+ }
61
+
62
+ return res;
63
+ }
64
+
65
+ private static String getRelativePath(MediaType mt) {
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
+ }
72
+
73
+ public static Uri createNewMediaFile(FileDescription file, MediaType mt) {
74
+ // Add a specific media item.
75
+ Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
76
+ ContentResolver resolver = appCtx.getContentResolver();
77
+
78
+ ContentValues fileDetails = new ContentValues();
79
+ String relativePath = getRelativePath(mt);
80
+ String mimeType = file.mimeType;
81
+
82
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
83
+ fileDetails.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
84
+ fileDetails.put(MediaStore.MediaColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
85
+ fileDetails.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
86
+ fileDetails.put(MediaStore.MediaColumns.DISPLAY_NAME, file.name);
87
+ fileDetails.put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath + '/' + file.partentFolder);
88
+
89
+ Uri mediauri = getMediaUri(mt);
90
+
91
+ // Keeps a handle to the new file's URI in case we need to modify it later.
92
+ return resolver.insert(mediauri, fileDetails);
93
+ } else {
94
+ File directory = Environment.getExternalStoragePublicDirectory(relativePath);
95
+ if (directory.canWrite()) {
96
+ File f = new File(relativePath + '/' + file.getFullPath());
97
+ if (!f.exists()) {
98
+ boolean result = f.mkdirs();
99
+ if (result) return Uri.fromFile(f);
100
+ }
101
+
102
+ }
103
+ }
104
+
105
+ return null;
106
+ }
107
+
108
+ @RequiresApi(api = Build.VERSION_CODES.Q)
109
+ public static boolean writeToMediaFile(Uri fileUri, String data, Promise promise) {
110
+ try {
111
+ Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
112
+ ContentResolver resolver = appCtx.getContentResolver();
113
+
114
+ // set pending
115
+ ContentValues contentValues = new ContentValues();
116
+ contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
117
+ resolver.update(fileUri, contentValues, null, null);
118
+
119
+ // write data
120
+ OutputStream stream = null;
121
+ Uri uri = null;
122
+
123
+ try {
124
+ ParcelFileDescriptor descr;
125
+ try {
126
+ assert fileUri != null;
127
+ descr = appCtx.getContentResolver().openFileDescriptor(fileUri, "w");
128
+ assert descr != null;
129
+ String normalizedData = ReactNativeBlobUtilUtils.normalizePath(data);
130
+ File src = new File(normalizedData);
131
+ if (!src.exists()) {
132
+ promise.reject("ENOENT", "No such file ('" + normalizedData + "')");
133
+ return false;
134
+ }
135
+ byte[] buf = new byte[10240];
136
+ int read;
137
+ int written = 0;
138
+
139
+ FileInputStream fin = new FileInputStream(src);
140
+ FileOutputStream out = new FileOutputStream(descr.getFileDescriptor());
141
+
142
+ while ((read = fin.read(buf)) > 0) {
143
+ out.write(buf, 0, read);
144
+ }
145
+
146
+ fin.close();
147
+ out.close();
148
+ descr.close();
149
+ } catch (Exception e) {
150
+ e.printStackTrace();
151
+ promise.reject(new IOException("Failed to get output stream."));
152
+ return false;
153
+ }
154
+
155
+ contentValues.clear();
156
+ contentValues.put(MediaStore.Video.Media.IS_PENDING, 0);
157
+ appCtx.getContentResolver().update(fileUri, contentValues, null, null);
158
+ stream = resolver.openOutputStream(fileUri);
159
+ if (stream == null) {
160
+ promise.reject(new IOException("Failed to get output stream."));
161
+ return false;
162
+ }
163
+ } catch (IOException e) {
164
+ // Don't leave an orphan entry in the MediaStore
165
+ resolver.delete(uri, null, null);
166
+ promise.reject(e);
167
+ return false;
168
+ } finally {
169
+ if (stream != null) {
170
+ stream.close();
171
+ return true;
172
+ }
173
+ }
174
+
175
+ // remove pending
176
+ contentValues = new ContentValues();
177
+ contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
178
+ resolver.update(fileUri, contentValues, null, null);
179
+
180
+ } catch (IOException e) {
181
+ promise.reject("ReactNativeBlobUtil.createMediaFile", "Cannot write to file, file might not exist");
182
+ return false;
183
+ }
184
+ return true;
185
+ }
186
+
187
+ public static void copyToInternal(Uri contenturi, String destpath, Promise promise) {
188
+ Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
189
+ ContentResolver resolver = appCtx.getContentResolver();
190
+
191
+ InputStream in = null;
192
+ OutputStream out = null;
193
+
194
+ if (!new File(destpath).exists()) {
195
+ try {
196
+ boolean result = new File(destpath).createNewFile();
197
+ if (!result) {
198
+ promise.reject("ReactNativeBlobUtil.copyToInternal: Destination file at '" + destpath + "' already exists");
199
+ return;
200
+ }
201
+ } catch (IOException ioException) {
202
+ promise.reject("ReactNativeBlobUtil.copyToInternal: Could not create file: " + ioException.getLocalizedMessage());
203
+ }
204
+ }
205
+
206
+ try {
207
+ in = resolver.openInputStream(contenturi);
208
+ out = new FileOutputStream(destpath);
209
+
210
+ byte[] buf = new byte[10240];
211
+ int len;
212
+ while ((len = in.read(buf)) > 0) {
213
+ out.write(buf, 0, len);
214
+ }
215
+
216
+ } catch (IOException e) {
217
+ promise.reject("ReactNativeBlobUtil.copyToInternal: Could not write data: " + e.getLocalizedMessage());
218
+ } finally {
219
+ if (in != null) {
220
+ try {
221
+ in.close();
222
+ } catch (IOException ioException) {
223
+ ioException.printStackTrace();
224
+ }
225
+ }
226
+ if (out != null) {
227
+ try {
228
+ out.close();
229
+ } catch (IOException ioException) {
230
+ ioException.printStackTrace();
231
+ }
232
+ }
233
+ }
234
+
235
+ promise.resolve("");
236
+ }
237
+
238
+ public static void getBlob(Uri contentUri, String encoding, Promise promise) {
239
+ Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext();
240
+ ContentResolver resolver = appCtx.getContentResolver();
241
+ try {
242
+ InputStream in = resolver.openInputStream(contentUri);
243
+ int length = 0;
244
+
245
+ length = in.available();
246
+
247
+ byte[] bytes = new byte[length];
248
+ int bytesRead = in.read(bytes);
249
+ in.close();
250
+
251
+ if (bytesRead < length) {
252
+ promise.reject("EUNSPECIFIED", "Read only " + bytesRead + " bytes of " + length);
253
+ return;
254
+ }
255
+
256
+ switch (encoding.toLowerCase()) {
257
+ case "base64":
258
+ promise.resolve(Base64.encodeToString(bytes, Base64.NO_WRAP));
259
+ break;
260
+ case "ascii":
261
+ WritableArray asciiResult = Arguments.createArray();
262
+ for (byte b : bytes) {
263
+ asciiResult.pushInt((int) b);
264
+ }
265
+ promise.resolve(asciiResult);
266
+ break;
267
+ case "utf8":
268
+ promise.resolve(new String(bytes));
269
+ break;
270
+ default:
271
+ promise.resolve(new String(bytes));
272
+ break;
273
+ }
274
+ } catch (IOException ioException) {
275
+ ioException.printStackTrace();
276
+ }
277
+ }
278
+ }
@@ -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;
@@ -120,7 +114,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
120
114
  OkHttpClient client;
121
115
 
122
116
  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();
117
+ this.method = method.toUpperCase(Locale.ROOT);
124
118
  this.options = new ReactNativeBlobUtilConfig(options);
125
119
  this.taskId = taskId;
126
120
  this.url = url;
@@ -196,7 +190,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
196
190
  req.addRequestHeader(key, headers.getString(key));
197
191
  }
198
192
  // Attempt to add cookie, if it exists
199
- URL urlObj = null;
193
+ URL urlObj;
200
194
  try {
201
195
  urlObj = new URL(url);
202
196
  String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost();
@@ -311,14 +305,14 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
311
305
  else if (value.equalsIgnoreCase("utf8"))
312
306
  responseFormat = ResponseFormat.UTF8;
313
307
  } else {
314
- builder.header(key.toLowerCase(), value);
315
- mheaders.put(key.toLowerCase(), value);
308
+ builder.header(key.toLowerCase(Locale.ROOT), value);
309
+ mheaders.put(key.toLowerCase(Locale.ROOT), value);
316
310
  }
317
311
  }
318
312
  }
319
313
 
320
314
  if (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
321
- String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase();
315
+ String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase(Locale.ROOT);
322
316
 
323
317
  if (rawRequestBodyArray != null) {
324
318
  requestType = RequestType.Form;
@@ -332,7 +326,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
332
326
  if (rawRequestBody.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX)
333
327
  || rawRequestBody.startsWith(ReactNativeBlobUtilConst.CONTENT_PREFIX)) {
334
328
  requestType = RequestType.SingleFile;
335
- } else if (cType.toLowerCase().contains(";base64") || cType.toLowerCase().startsWith("application/octet")) {
329
+ } else if (cType.toLowerCase(Locale.ROOT).contains(";base64") || cType.toLowerCase(Locale.ROOT).startsWith("application/octet")) {
336
330
  cType = cType.replace(";base64", "").replace(";BASE64", "");
337
331
  if (mheaders.containsKey("content-type"))
338
332
  mheaders.put("content-type", cType);
@@ -388,18 +382,21 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
388
382
  // #156 fix cookie issue
389
383
  final Request req = builder.build();
390
384
  clientBuilder.addNetworkInterceptor(new Interceptor() {
385
+ @NonNull
391
386
  @Override
392
- public Response intercept(Chain chain) throws IOException {
387
+ public Response intercept(@NonNull Chain chain) throws IOException {
393
388
  redirects.add(chain.request().url().toString());
394
389
  return chain.proceed(chain.request());
395
390
  }
396
391
  });
397
392
  // Add request interceptor for upload progress event
398
393
  clientBuilder.addInterceptor(new Interceptor() {
394
+ @NonNull
399
395
  @Override
400
396
  public Response intercept(@NonNull Chain chain) throws IOException {
397
+ Response originalResponse = null;
401
398
  try {
402
- Response originalResponse = chain.proceed(req);
399
+ originalResponse = chain.proceed(req);
403
400
  ResponseBody extended;
404
401
  switch (responseType) {
405
402
  case KeepInMemory:
@@ -428,12 +425,21 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
428
425
  return originalResponse.newBuilder().body(extended).build();
429
426
  } catch (SocketException e) {
430
427
  timeout = true;
428
+ if (originalResponse != null) {
429
+ originalResponse.close();
430
+ }
431
431
  } catch (SocketTimeoutException e) {
432
432
  timeout = true;
433
+ if (originalResponse != null) {
434
+ originalResponse.close();
435
+ }
433
436
  //ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil error when sending request : " + e.getLocalizedMessage());
434
437
  } catch (Exception ex) {
435
-
438
+ if (originalResponse != null) {
439
+ originalResponse.close();
440
+ }
436
441
  }
442
+
437
443
  return chain.proceed(chain.request());
438
444
  }
439
445
  });
@@ -457,7 +463,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
457
463
  call.enqueue(new okhttp3.Callback() {
458
464
 
459
465
  @Override
460
- public void onFailure(@NonNull Call call, IOException e) {
466
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
461
467
  cancelTask(taskId);
462
468
  if (respInfo == null) {
463
469
  respInfo = Arguments.createMap();
@@ -708,7 +714,7 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
708
714
  boolean isCustomBinary = false;
709
715
  if (options.binaryContentTypes != null) {
710
716
  for (int i = 0; i < options.binaryContentTypes.size(); i++) {
711
- if (ctype.toLowerCase().contains(options.binaryContentTypes.getString(i).toLowerCase())) {
717
+ if (ctype.toLowerCase(Locale.ROOT).contains(options.binaryContentTypes.getString(i).toLowerCase(Locale.ROOT))) {
712
718
  isCustomBinary = true;
713
719
  break;
714
720
  }
@@ -720,13 +726,13 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
720
726
  private String getHeaderIgnoreCases(Headers headers, String field) {
721
727
  String val = headers.get(field);
722
728
  if (val != null) return val;
723
- return headers.get(field.toLowerCase()) == null ? "" : headers.get(field.toLowerCase());
729
+ return headers.get(field.toLowerCase(Locale.ROOT)) == null ? "" : headers.get(field.toLowerCase(Locale.ROOT));
724
730
  }
725
731
 
726
732
  private String getHeaderIgnoreCases(HashMap<String, String> headers, String field) {
727
733
  String val = headers.get(field);
728
734
  if (val != null) return val;
729
- String lowerCasedValue = headers.get(field.toLowerCase());
735
+ String lowerCasedValue = headers.get(field.toLowerCase(Locale.ROOT));
730
736
  return lowerCasedValue == null ? "" : lowerCasedValue;
731
737
  }
732
738