react-native-blob-util 0.13.17 → 0.14.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 (25) hide show
  1. package/README.md +472 -407
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java +85 -21
  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 +92 -292
  7. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java +299 -0
  8. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilPackage.java +6 -2
  9. package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +17 -21
  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/fs.js +2 -1
  16. package/index.d.ts +107 -2
  17. package/index.js +2 -0
  18. package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.m +2 -1
  19. package/ios/ReactNativeBlobUtilFS.h +1 -0
  20. package/ios/ReactNativeBlobUtilFS.m +4 -0
  21. package/ios/ReactNativeBlobUtilReqBuilder.m +1 -0
  22. package/mediacollection.js +33 -0
  23. package/package.json +1 -1
  24. package/scripts/prelink.js +1 -1
  25. package/types.js +3 -0
@@ -0,0 +1,287 @@
1
+ package com.ReactNativeBlobUtil;
2
+
3
+ import android.net.Uri;
4
+ import android.os.SystemClock;
5
+ import android.util.Base64;
6
+
7
+ import com.facebook.react.bridge.Arguments;
8
+ import com.facebook.react.bridge.Callback;
9
+ import com.facebook.react.bridge.ReactApplicationContext;
10
+ import com.facebook.react.bridge.ReadableArray;
11
+ import com.facebook.react.bridge.WritableArray;
12
+ import com.facebook.react.bridge.WritableMap;
13
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
14
+
15
+ import java.io.BufferedReader;
16
+ import java.io.File;
17
+ import java.io.FileInputStream;
18
+ import java.io.FileNotFoundException;
19
+ import java.io.FileOutputStream;
20
+ import java.io.IOException;
21
+ import java.io.InputStream;
22
+ import java.io.InputStreamReader;
23
+ import java.io.OutputStream;
24
+ import java.nio.charset.Charset;
25
+ import java.util.HashMap;
26
+ import java.util.UUID;
27
+
28
+ public class ReactNativeBlobUtilStream {
29
+ private final DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
30
+ private String encoding = "base64";
31
+ private OutputStream writeStreamInstance = null;
32
+ private static final HashMap<String, ReactNativeBlobUtilStream> fileStreams = new HashMap<>();
33
+
34
+ ReactNativeBlobUtilStream(ReactApplicationContext ctx) {
35
+ this.emitter = ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
36
+ }
37
+
38
+ /**
39
+ * Create a file stream for read
40
+ *
41
+ * @param path File stream target path
42
+ * @param encoding File stream decoder, should be one of `base64`, `utf8`, `ascii`
43
+ * @param bufferSize Buffer size of read stream, default to 4096 (4095 when encode is `base64`)
44
+ */
45
+ void readStream(String path, String encoding, int bufferSize, int tick, final String streamId) {
46
+ String resolved = ReactNativeBlobUtilUtils.normalizePath(path);
47
+ if (resolved != null)
48
+ path = resolved;
49
+
50
+ try {
51
+ int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096;
52
+ if (bufferSize > 0)
53
+ chunkSize = bufferSize;
54
+
55
+ InputStream fs;
56
+
57
+ if (resolved != null && path.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET)) {
58
+ fs = ReactNativeBlobUtil.RCTContext.getAssets().open(path.replace(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET, ""));
59
+ }
60
+ // fix issue 287
61
+ else if (resolved == null) {
62
+ fs = ReactNativeBlobUtil.RCTContext.getContentResolver().openInputStream(Uri.parse(path));
63
+ } else {
64
+ fs = new FileInputStream(new File(path));
65
+ }
66
+
67
+ int cursor = 0;
68
+ boolean error = false;
69
+
70
+ if (encoding.equalsIgnoreCase("utf8")) {
71
+ InputStreamReader isr = new InputStreamReader(fs, Charset.forName("UTF-8"));
72
+ BufferedReader reader = new BufferedReader(isr, chunkSize);
73
+ char[] buffer = new char[chunkSize];
74
+ // read chunks of the string
75
+ while (reader.read(buffer, 0, chunkSize) != -1) {
76
+ String chunk = new String(buffer);
77
+ emitStreamEvent(streamId, "data", chunk);
78
+ if (tick > 0)
79
+ SystemClock.sleep(tick);
80
+ }
81
+
82
+ reader.close();
83
+ isr.close();
84
+ } else if (encoding.equalsIgnoreCase("ascii")) {
85
+ byte[] buffer = new byte[chunkSize];
86
+ while ((cursor = fs.read(buffer)) != -1) {
87
+ WritableArray chunk = Arguments.createArray();
88
+ for (int i = 0; i < cursor; i++) {
89
+ chunk.pushInt((int) buffer[i]);
90
+ }
91
+ emitStreamEvent(streamId, "data", chunk);
92
+ if (tick > 0)
93
+ SystemClock.sleep(tick);
94
+ }
95
+ } else if (encoding.equalsIgnoreCase("base64")) {
96
+ byte[] buffer = new byte[chunkSize];
97
+ while ((cursor = fs.read(buffer)) != -1) {
98
+ if (cursor < chunkSize) {
99
+ byte[] copy = new byte[cursor];
100
+ System.arraycopy(buffer, 0, copy, 0, cursor);
101
+ emitStreamEvent(streamId, "data", Base64.encodeToString(copy, Base64.NO_WRAP));
102
+ } else
103
+ emitStreamEvent(streamId, "data", Base64.encodeToString(buffer, Base64.NO_WRAP));
104
+ if (tick > 0)
105
+ SystemClock.sleep(tick);
106
+ }
107
+ } else {
108
+ emitStreamEvent(
109
+ streamId,
110
+ "error",
111
+ "EINVAL",
112
+ "Unrecognized encoding `" + encoding + "`, should be one of `base64`, `utf8`, `ascii`"
113
+ );
114
+ error = true;
115
+ }
116
+
117
+ if (!error)
118
+ emitStreamEvent(streamId, "end", "");
119
+ fs.close();
120
+
121
+ } catch (FileNotFoundException err) {
122
+ emitStreamEvent(
123
+ streamId,
124
+ "error",
125
+ "ENOENT",
126
+ "No such file '" + path + "'"
127
+ );
128
+ } catch (Exception err) {
129
+ emitStreamEvent(
130
+ streamId,
131
+ "error",
132
+ "EUNSPECIFIED",
133
+ "Failed to convert data to " + encoding + " encoded string. This might be because this encoding cannot be used for this data."
134
+ );
135
+ err.printStackTrace();
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Create a write stream and store its instance in ReactNativeBlobUtilFS.fileStreams
141
+ *
142
+ * @param path Target file path
143
+ * @param encoding Should be one of `base64`, `utf8`, `ascii`
144
+ * @param append Flag represents if the file stream overwrite existing content
145
+ * @param callback Callback
146
+ */
147
+ void writeStream(String path, String encoding, boolean append, Callback callback) {
148
+ try {
149
+ File dest = new File(path);
150
+ File dir = dest.getParentFile();
151
+
152
+ if (!dest.exists()) {
153
+ if (dir != null && !dir.exists()) {
154
+ if (!dir.mkdirs()) {
155
+ callback.invoke("ENOTDIR", "Failed to create parent directory of '" + path + "'");
156
+ return;
157
+ }
158
+ }
159
+ if (!dest.createNewFile()) {
160
+ callback.invoke("ENOENT", "File '" + path + "' does not exist and could not be created");
161
+ return;
162
+ }
163
+ } else if (dest.isDirectory()) {
164
+ callback.invoke("EISDIR", "Expecting a file but '" + path + "' is a directory");
165
+ return;
166
+ }
167
+
168
+ OutputStream fs = new FileOutputStream(path, append);
169
+ this.encoding = encoding;
170
+ String streamId = UUID.randomUUID().toString();
171
+ ReactNativeBlobUtilStream.fileStreams.put(streamId, this);
172
+ this.writeStreamInstance = fs;
173
+ callback.invoke(null, null, streamId);
174
+ } catch (Exception err) {
175
+ callback.invoke("EUNSPECIFIED", "Failed to create write stream at path `" + path + "`; " + err.getLocalizedMessage());
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Write a chunk of data into a file stream.
181
+ *
182
+ * @param streamId File stream ID
183
+ * @param data Data chunk in string format
184
+ * @param callback JS context callback
185
+ */
186
+ static void writeChunk(String streamId, String data, Callback callback) {
187
+ ReactNativeBlobUtilStream fs = fileStreams.get(streamId);
188
+ assert fs != null;
189
+ OutputStream stream = fs.writeStreamInstance;
190
+ byte[] chunk = ReactNativeBlobUtilUtils.stringToBytes(data, fs.encoding);
191
+ try {
192
+ stream.write(chunk);
193
+ callback.invoke();
194
+ } catch (Exception e) {
195
+ callback.invoke(e.getLocalizedMessage());
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Write data using ascii array
201
+ *
202
+ * @param streamId File stream ID
203
+ * @param data Data chunk in ascii array format
204
+ * @param callback JS context callback
205
+ */
206
+ static void writeArrayChunk(String streamId, ReadableArray data, Callback callback) {
207
+ try {
208
+ ReactNativeBlobUtilStream fs = fileStreams.get(streamId);
209
+ assert fs != null;
210
+ OutputStream stream = fs.writeStreamInstance;
211
+ byte[] chunk = new byte[data.size()];
212
+ for (int i = 0; i < data.size(); i++) {
213
+ chunk[i] = (byte) data.getInt(i);
214
+ }
215
+ stream.write(chunk);
216
+ callback.invoke();
217
+ } catch (Exception e) {
218
+ callback.invoke(e.getLocalizedMessage());
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Close file write stream by ID
224
+ *
225
+ * @param streamId Stream ID
226
+ * @param callback JS context callback
227
+ */
228
+ static void closeStream(String streamId, Callback callback) {
229
+ try {
230
+ ReactNativeBlobUtilStream fs = fileStreams.get(streamId);
231
+ assert fs != null;
232
+ OutputStream stream = fs.writeStreamInstance;
233
+ fileStreams.remove(streamId);
234
+ stream.close();
235
+ callback.invoke();
236
+ } catch (Exception err) {
237
+ callback.invoke(err.getLocalizedMessage());
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Private method for emit read stream event.
243
+ *
244
+ * @param streamName ID of the read stream
245
+ * @param event Event name, `data`, `end`, `error`, etc.
246
+ * @param data Event data
247
+ */
248
+ private void emitStreamEvent(String streamName, String event, String data) {
249
+ WritableMap eventData = Arguments.createMap();
250
+ eventData.putString("event", event);
251
+ eventData.putString("detail", data);
252
+ this.emitter.emit(streamName, eventData);
253
+ }
254
+
255
+ // "event" always is "data"...
256
+ private void emitStreamEvent(String streamName, String event, WritableArray data) {
257
+ WritableMap eventData = Arguments.createMap();
258
+ eventData.putString("event", event);
259
+ eventData.putArray("detail", data);
260
+ this.emitter.emit(streamName, eventData);
261
+ }
262
+
263
+ // "event" always is "error"...
264
+ private void emitStreamEvent(String streamName, String event, String code, String message) {
265
+ WritableMap eventData = Arguments.createMap();
266
+ eventData.putString("event", event);
267
+ eventData.putString("code", code);
268
+ eventData.putString("detail", message);
269
+ this.emitter.emit(streamName, eventData);
270
+ }
271
+
272
+ /**
273
+ * Get input stream of the given path, when the path is a string starts with bundle-assets://
274
+ * the stream is created by Assets Manager, otherwise use FileInputStream.
275
+ *
276
+ * @param path The file to open stream
277
+ * @return InputStream instance
278
+ * @throws IOException If the given file does not exist or is a directory FileInputStream will throw a FileNotFoundException
279
+ */
280
+ public static InputStream inputStreamFromPath(String path) throws IOException {
281
+ if (path.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET)) {
282
+ return ReactNativeBlobUtil.RCTContext.getAssets().open(path.replace(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET, ""));
283
+ }
284
+ return new FileInputStream(new File(path));
285
+ }
286
+
287
+ }
@@ -1,11 +1,17 @@
1
1
  package com.ReactNativeBlobUtil;
2
2
 
3
+ import android.net.Uri;
4
+ import android.util.Base64;
5
+
6
+ import com.ReactNativeBlobUtil.Utils.PathResolver;
3
7
  import com.facebook.react.bridge.Arguments;
4
8
  import com.facebook.react.bridge.WritableMap;
5
9
  import com.facebook.react.modules.core.DeviceEventManagerModule;
6
10
 
11
+ import java.nio.charset.Charset;
7
12
  import java.security.MessageDigest;
8
13
  import java.security.cert.CertificateException;
14
+ import java.util.Locale;
9
15
 
10
16
  import javax.net.ssl.HostnameVerifier;
11
17
  import javax.net.ssl.SSLContext;
@@ -30,7 +36,7 @@ public class ReactNativeBlobUtilUtils {
30
36
  StringBuilder sb = new StringBuilder();
31
37
 
32
38
  for (byte b : digest) {
33
- sb.append(String.format("%02x", b & 0xff));
39
+ sb.append(String.format(Locale.ROOT, "%02x", b & 0xff));
34
40
  }
35
41
 
36
42
  result = sb.toString();
@@ -75,7 +81,7 @@ public class ReactNativeBlobUtilUtils {
75
81
  // Install the all-trusting trust manager
76
82
  final SSLContext sslContext = SSLContext.getInstance("SSL");
77
83
  sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
78
- // Create an ssl socket factory with our all-trusting manager
84
+ // Create an ssl socLket factory with our all-trusting manager
79
85
  final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
80
86
 
81
87
  OkHttpClient.Builder builder = client.newBuilder();
@@ -92,4 +98,53 @@ public class ReactNativeBlobUtilUtils {
92
98
  throw new RuntimeException(e);
93
99
  }
94
100
  }
101
+
102
+ /**
103
+ * String to byte converter method
104
+ *
105
+ * @param data Raw data in string format
106
+ * @param encoding Decoder name
107
+ * @return Converted data byte array
108
+ */
109
+ public static byte[] stringToBytes(String data, String encoding) {
110
+ if (encoding.equalsIgnoreCase("ascii")) {
111
+ return data.getBytes(Charset.forName("US-ASCII"));
112
+ } else if (encoding.toLowerCase(Locale.ROOT).contains("base64")) {
113
+ return Base64.decode(data, Base64.NO_WRAP);
114
+
115
+ } else if (encoding.equalsIgnoreCase("utf8")) {
116
+ return data.getBytes(Charset.forName("UTF-8"));
117
+ }
118
+ return data.getBytes(Charset.forName("US-ASCII"));
119
+ }
120
+
121
+ /**
122
+ * Normalize the path, remove URI scheme (xxx://) so that we can handle it.
123
+ *
124
+ * @param path URI string.
125
+ * @return Normalized string
126
+ */
127
+ public static String normalizePath(String path) {
128
+ if (path == null)
129
+ return null;
130
+ if (!path.matches("\\w+\\:.*"))
131
+ return path;
132
+ if (path.startsWith("file://")) {
133
+ return path.replace("file://", "");
134
+ }
135
+
136
+ Uri uri = Uri.parse(path);
137
+ if (path.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET)) {
138
+ return path;
139
+ } else
140
+ return PathResolver.getRealPathFromURI(ReactNativeBlobUtil.RCTContext, uri);
141
+ }
142
+
143
+ public static boolean isAsset(String path) {
144
+ return path != null && path.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET);
145
+ }
146
+
147
+ public static boolean isContentUri(String path) {
148
+ return path != null && path.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_CONTENT);
149
+ }
95
150
  }
@@ -35,6 +35,11 @@ public class ReactNativeBlobUtilFileResp extends ResponseBody {
35
35
  FileOutputStream ofStream;
36
36
  boolean isEndMarkerReceived;
37
37
 
38
+ public ReactNativeBlobUtilFileResp(ResponseBody body) {
39
+ super();
40
+ this.originalBody = body;
41
+ }
42
+
38
43
  public ReactNativeBlobUtilFileResp(ReactApplicationContext ctx, String taskId, ResponseBody body, String path, boolean overwrite) throws IOException {
39
44
  super();
40
45
  this.rctContext = ctx;
@@ -0,0 +1,19 @@
1
+ package com.ReactNativeBlobUtil.Utils;
2
+
3
+ import android.webkit.MimeTypeMap;
4
+
5
+ public class FileDescription {
6
+ public String name;
7
+ public String partentFolder;
8
+ public String mimeType;
9
+
10
+ public FileDescription(String n, String mT, String pF) {
11
+ name = n;
12
+ partentFolder = pF != null ? pF : "";
13
+ mimeType = mT;
14
+ }
15
+
16
+ public String getFullPath(){
17
+ return partentFolder + "/" + MimeType.getFullFileName(name, mimeType);
18
+ }
19
+ }
@@ -0,0 +1,70 @@
1
+ package com.ReactNativeBlobUtil.Utils;
2
+
3
+ import android.webkit.MimeTypeMap;
4
+
5
+ import org.apache.commons.lang3.StringUtils;
6
+
7
+ public class MimeType {
8
+ static String UNKNOWN = "*/*";
9
+ static String BINARY_FILE = "application/octet-stream";
10
+ static String IMAGE = "image/*";
11
+ static String AUDIO = "audio/*";
12
+ static String VIDEO = "video/*";
13
+ static String TEXT = "text/*";
14
+ static String FONT = "font/*";
15
+ static String APPLICATION = "application/*";
16
+ static String CHEMICAL = "chemical/*";
17
+ static String MODEL = "model/*";
18
+
19
+ /**
20
+ * * Given `name` = `ABC` AND `mimeType` = `video/mp4`, then return `ABC.mp4`
21
+ * * Given `name` = `ABC` AND `mimeType` = `null`, then return `ABC`
22
+ * * Given `name` = `ABC.mp4` AND `mimeType` = `video/mp4`, then return `ABC.mp4`
23
+ *
24
+ * @param name can have file extension or not
25
+ */
26
+
27
+ public static String getFullFileName(String name, String mimeType) {
28
+ // Prior to API 29, MimeType.BINARY_FILE has no file extension
29
+ String ext = MimeType.getExtensionFromMimeType(mimeType);
30
+ if (ext.isEmpty() || name.endsWith("." + "ext")) return name;
31
+ else {
32
+ String fn = name + "." + ext;
33
+ if (fn.endsWith(".")) return StringUtils.stripEnd(fn, ".");
34
+ else return fn;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Some mime types return no file extension on older API levels. This function adds compatibility accross API levels.
40
+ *
41
+ * @see this.getExtensionFromMimeTypeOrFileName
42
+ */
43
+
44
+ public static String getExtensionFromMimeType(String mimeType) {
45
+ if (mimeType != null) {
46
+ if (mimeType.equals(BINARY_FILE)) return "bin";
47
+ else return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
48
+ } else return "";
49
+ }
50
+
51
+ /**
52
+ * @see this.getExtensionFromMimeType
53
+ */
54
+ public static String getExtensionFromMimeTypeOrFileName(String mimeType, String filename) {
55
+ if (mimeType == null || mimeType.equals(UNKNOWN)) return StringUtils.substringAfterLast(filename, ".");
56
+ else return getExtensionFromMimeType(mimeType);
57
+ }
58
+
59
+ /**
60
+ * Some file types return no mime type on older API levels. This function adds compatibility across API levels.
61
+ */
62
+ public static String getMimeTypeFromExtension(String fileExtension) {
63
+ if (fileExtension.equals("bin")) return BINARY_FILE;
64
+ else {
65
+ String mt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
66
+ if (mt != null) return mt;
67
+ else return UNKNOWN;
68
+ }
69
+ }
70
+ }
package/fs.js CHANGED
@@ -23,7 +23,8 @@ const dirs = {
23
23
  SDCardDir: ReactNativeBlobUtil.SDCardDir, // Depracated
24
24
  SDCardApplicationDir: ReactNativeBlobUtil.SDCardApplicationDir, // Deprecated
25
25
  MainBundleDir: ReactNativeBlobUtil.MainBundleDir,
26
- LibraryDir: ReactNativeBlobUtil.LibraryDir
26
+ LibraryDir: ReactNativeBlobUtil.LibraryDir,
27
+ ApplicationSupportDir: ReactNativeBlobUtil.ApplicationSupportDir
27
28
  };
28
29
 
29
30
  function addCode(code: string, error: Error): Error {