react-native-blob-util 0.24.9 → 0.24.10

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm view *)",
5
+ "Bash(npm install *)",
6
+ "Bash(git -C tests/e2e/android-app diff --stat package-lock.json)"
7
+ ]
8
+ }
9
+ }
@@ -63,7 +63,7 @@ android {
63
63
  buildTypes {
64
64
  release {
65
65
  minifyEnabled false
66
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
66
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
67
67
  }
68
68
  }
69
69
  compileOptions {
@@ -168,4 +168,4 @@ afterEvaluate { project ->
168
168
  }
169
169
  }
170
170
  }
171
- }
171
+ }
@@ -29,6 +29,7 @@ import java.io.FileOutputStream;
29
29
  import java.io.IOException;
30
30
  import java.io.InputStream;
31
31
  import java.io.OutputStream;
32
+ import java.io.ByteArrayOutputStream;
32
33
  import java.security.MessageDigest;
33
34
  import java.util.ArrayList;
34
35
  import java.util.HashMap;
@@ -251,41 +252,27 @@ class ReactNativeBlobUtilFS {
251
252
  path = resolved;
252
253
  try {
253
254
  byte[] bytes;
254
- int bytesRead;
255
- int length; // max. array length limited to "int", also see https://stackoverflow.com/a/10787175/544779
256
255
 
257
256
  if (resolved != null && resolved.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET)) {
258
257
  String assetName = path.replace(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET, "");
259
- // This fails should an asset file be >2GB
260
- InputStream in = ReactNativeBlobUtilImpl.RCTContext.getAssets().open(assetName);
261
- length = in.available();
262
- bytes = new byte[length];
263
- bytesRead = in.read(bytes, 0, length);
264
- in.close();
258
+ try (InputStream in = ReactNativeBlobUtilImpl.RCTContext.getAssets().open(assetName)) {
259
+ bytes = readBytesWithLimit(in);
260
+ }
265
261
  }
266
262
  // issue 287
267
263
  else if (resolved == null) {
268
- InputStream in = ReactNativeBlobUtilImpl.RCTContext.getContentResolver().openInputStream(Uri.parse(path));
269
- // TODO See https://developer.android.com/reference/java/io/InputStream.html#available()
270
- // Quote: "Note that while some implementations of InputStream will return the total number of bytes
271
- // in the stream, many will not. It is never correct to use the return value of this method to
272
- // allocate a buffer intended to hold all data in this stream."
273
- length = in.available();
274
- bytes = new byte[length];
275
- bytesRead = in.read(bytes);
276
- in.close();
264
+ try (InputStream in = ReactNativeBlobUtilImpl.RCTContext.getContentResolver().openInputStream(Uri.parse(path))) {
265
+ if (in == null) {
266
+ promise.reject("ENOENT", "No such file '" + path + "'");
267
+ return;
268
+ }
269
+ bytes = readBytesWithLimit(in);
270
+ }
277
271
  } else {
278
272
  File f = new File(path);
279
- length = (int) f.length();
280
- bytes = new byte[length];
281
- FileInputStream in = new FileInputStream(f);
282
- bytesRead = in.read(bytes);
283
- in.close();
284
- }
285
-
286
- if (bytesRead < length) {
287
- promise.reject("EUNSPECIFIED", "Read only " + bytesRead + " bytes of " + length);
288
- return;
273
+ try (FileInputStream in = new FileInputStream(f)) {
274
+ bytes = readBytesWithLimit(in);
275
+ }
289
276
  }
290
277
 
291
278
  if (transformFile) {
@@ -326,6 +313,18 @@ class ReactNativeBlobUtilFS {
326
313
 
327
314
  }
328
315
 
316
+ private static byte[] readBytesWithLimit(InputStream in) throws IOException {
317
+ byte[] buffer = new byte[10240];
318
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
319
+ int read;
320
+
321
+ while ((read = in.read(buffer)) != -1) {
322
+ output.write(buffer, 0, read);
323
+ }
324
+
325
+ return output.toByteArray();
326
+ }
327
+
329
328
  /**
330
329
  * Static method that returns system folders to JS context
331
330
  *
@@ -617,6 +617,14 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
617
617
 
618
618
  if (options.timeout >= 0) {
619
619
  clientBuilder.connectTimeout(options.timeout, TimeUnit.MILLISECONDS);
620
+ }
621
+ // For file-to-disk downloads use no read timeout: the 60-second default
622
+ // can fire on slow connections before a large file finishes transferring.
623
+ // Individual socket reads on a healthy connection take milliseconds, so
624
+ // there is no risk of hanging indefinitely; the user can always cancel().
625
+ if (responseType == ResponseType.FileStorage) {
626
+ clientBuilder.readTimeout(0, TimeUnit.MILLISECONDS);
627
+ } else if (options.timeout >= 0) {
620
628
  clientBuilder.readTimeout(options.timeout, TimeUnit.MILLISECONDS);
621
629
  }
622
630
 
@@ -781,11 +789,18 @@ public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnabl
781
789
  case FileStorage:
782
790
  ResponseBody responseBody = resp.body();
783
791
 
792
+ // Drain via byteStream() — avoids OkHttp's bytes() check that rejects
793
+ // content-length > Integer.MAX_VALUE (2 GB). ProgressReportingSource.read()
794
+ // writes each chunk to disk as a side-effect. Closing the stream flushes
795
+ // and closes the FileOutputStream that holds the destination file.
784
796
  try {
785
- // In order to write response data to `destPath` we have to invoke this method.
786
- // It uses customized response body which is able to report download progress
787
- // and write response data to destination path.
788
- responseBody.bytes();
797
+ java.io.InputStream drainStream = responseBody.byteStream();
798
+ try {
799
+ byte[] drainBuf = new byte[65536];
800
+ while (drainStream.read(drainBuf) != -1) { }
801
+ } finally {
802
+ try { drainStream.close(); } catch (Exception ignored2) { }
803
+ }
789
804
  } catch (Exception ignored) {
790
805
  // ignored.printStackTrace();
791
806
  }
@@ -241,6 +241,52 @@ typedef NS_ENUM(NSUInteger, ResponseFormat) {
241
241
  }
242
242
  }
243
243
 
244
+ - (BOOL)copyDownloadedFile:(NSURL *)sourceURL toPath:(NSString *)targetPath append:(BOOL)append
245
+ {
246
+ NSInputStream *inputStream = [NSInputStream inputStreamWithURL:sourceURL];
247
+ NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:targetPath append:append];
248
+
249
+ if (!inputStream || !outputStream) {
250
+ return NO;
251
+ }
252
+
253
+ [inputStream open];
254
+ [outputStream open];
255
+
256
+ uint8_t buffer[65536];
257
+ BOOL success = YES;
258
+
259
+ while ([inputStream hasBytesAvailable]) {
260
+ NSInteger bytesRead = [inputStream read:buffer maxLength:sizeof(buffer)];
261
+ if (bytesRead < 0) {
262
+ success = NO;
263
+ break;
264
+ }
265
+ if (bytesRead == 0) {
266
+ break;
267
+ }
268
+
269
+ NSInteger bytesWritten = 0;
270
+ while (bytesWritten < bytesRead) {
271
+ NSInteger writeResult = [outputStream write:&buffer[bytesWritten] maxLength:(NSUInteger)(bytesRead - bytesWritten)];
272
+ if (writeResult <= 0) {
273
+ success = NO;
274
+ break;
275
+ }
276
+ bytesWritten += writeResult;
277
+ }
278
+
279
+ if (!success) {
280
+ break;
281
+ }
282
+ }
283
+
284
+ [inputStream close];
285
+ [outputStream close];
286
+
287
+ return success;
288
+ }
289
+
244
290
 
245
291
  #pragma mark - Received Response
246
292
  // set expected content length on response received
@@ -558,10 +604,40 @@ typedef NS_ENUM(NSUInteger, ResponseFormat) {
558
604
  - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
559
605
 
560
606
  NSFileManager *fm = [NSFileManager defaultManager];
561
- NSData *data = [fm contentsAtPath:location.path];
607
+ if (respFile && ![self ShouldTransformFile]) {
608
+ if (writeStream) {
609
+ [writeStream close];
610
+ writeStream = nil;
611
+ }
562
612
 
563
- [self configureWriteStream];
613
+ NSString *folder = [destPath stringByDeletingLastPathComponent];
614
+ if (![fm fileExistsAtPath:folder]) {
615
+ [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
616
+ }
617
+
618
+ BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
619
+ BOOL appendToExistingFile = [destPath containsString:@"?append=true"] || !overwrite;
620
+ NSString *normalizedDestPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
621
+ destPath = normalizedDestPath;
622
+
623
+ if (!appendToExistingFile && [fm fileExistsAtPath:destPath]) {
624
+ [fm removeItemAtPath:destPath error:nil];
625
+ }
626
+
627
+ if (!appendToExistingFile) {
628
+ NSError *moveError = nil;
629
+ if ([fm moveItemAtURL:location toURL:[NSURL fileURLWithPath:destPath] error:&moveError]) {
630
+ return;
631
+ }
632
+ }
564
633
 
634
+ if ([self copyDownloadedFile:location toPath:destPath append:appendToExistingFile]) {
635
+ return;
636
+ }
637
+ }
638
+
639
+ NSData *data = [fm contentsAtPath:location.path];
640
+ [self configureWriteStream];
565
641
  [self processData:data];
566
642
 
567
643
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-blob-util",
3
- "version": "0.24.9",
3
+ "version": "0.24.10",
4
4
  "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.",
5
5
  "main": "index",
6
6
  "scripts": {
@@ -13,8 +13,8 @@
13
13
  "e2e:all": "node tests/e2e/run-all.js"
14
14
  },
15
15
  "dependencies": {
16
- "base-64": "0.1.0",
17
- "glob": "13.0.1"
16
+ "base-64": "1.0.0",
17
+ "glob": "13.0.6"
18
18
  },
19
19
  "keywords": [
20
20
  "react-native",
@@ -48,18 +48,18 @@
48
48
  "wkh237 <xeiyan@gmail.com>"
49
49
  ],
50
50
  "devDependencies": {
51
- "@typescript-eslint/eslint-plugin": "^8.46.4",
52
- "@typescript-eslint/parser": "^8.46.4",
53
- "appium-uiautomator2-driver": "^7.0.0",
51
+ "@typescript-eslint/eslint-plugin": "^8.61.1",
52
+ "@typescript-eslint/parser": "^8.61.1",
53
+ "appium-uiautomator2-driver": "^7.6.2",
54
54
  "eslint": "^8.57.1",
55
55
  "eslint-plugin-ft-flow": "^3.0.11",
56
56
  "eslint-plugin-import": "^2.32.0",
57
57
  "eslint-plugin-react": "^7.37.5",
58
58
  "eslint-plugin-react-native": "^5.0.0",
59
- "react": "19.0.0",
60
- "react-native": "0.78.2",
61
- "react-native-windows": "0.78.2",
62
- "webdriverio": "^8.41.0"
59
+ "react": "19.2.3",
60
+ "react-native": "0.84.1",
61
+ "react-native-windows": "0.84.0",
62
+ "webdriverio": "^9.29.0"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "react": "*",
package/utils/uuid.js CHANGED
@@ -1,11 +1,11 @@
1
- function getUUID() {
2
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
3
- const r = Math.floor(Math.random() * 16);
4
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
5
-
6
- return v.toString(16);
7
- });
8
- }
9
-
10
- export {getUUID};
11
- export default getUUID;
1
+ function getUUID() {
2
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
3
+ const r = Math.floor(Math.random() * 16);
4
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
5
+
6
+ return v.toString(16);
7
+ });
8
+ }
9
+
10
+ export {getUUID};
11
+ export default getUUID;