react-native-blob-util 0.24.8 → 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.
- package/.claude/settings.local.json +9 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java +26 -27
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +19 -4
- package/ios/ReactNativeBlobUtilRequest.mm +78 -2
- package/package.json +10 -11
- package/utils/uuid.js +9 -3
package/android/build.gradle
CHANGED
|
@@ -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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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
|
-
|
|
607
|
+
if (respFile && ![self ShouldTransformFile]) {
|
|
608
|
+
if (writeStream) {
|
|
609
|
+
[writeStream close];
|
|
610
|
+
writeStream = nil;
|
|
611
|
+
}
|
|
562
612
|
|
|
563
|
-
|
|
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.
|
|
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,10 +13,8 @@
|
|
|
13
13
|
"e2e:all": "node tests/e2e/run-all.js"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"glob": "13.0.1",
|
|
19
|
-
"uuid" : "^13.0.0"
|
|
16
|
+
"base-64": "1.0.0",
|
|
17
|
+
"glob": "13.0.6"
|
|
20
18
|
},
|
|
21
19
|
"keywords": [
|
|
22
20
|
"react-native",
|
|
@@ -50,17 +48,18 @@
|
|
|
50
48
|
"wkh237 <xeiyan@gmail.com>"
|
|
51
49
|
],
|
|
52
50
|
"devDependencies": {
|
|
53
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
54
|
-
"@typescript-eslint/parser": "^8.
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^8.61.1",
|
|
52
|
+
"@typescript-eslint/parser": "^8.61.1",
|
|
53
|
+
"appium-uiautomator2-driver": "^7.6.2",
|
|
55
54
|
"eslint": "^8.57.1",
|
|
56
55
|
"eslint-plugin-ft-flow": "^3.0.11",
|
|
57
56
|
"eslint-plugin-import": "^2.32.0",
|
|
58
57
|
"eslint-plugin-react": "^7.37.5",
|
|
59
58
|
"eslint-plugin-react-native": "^5.0.0",
|
|
60
|
-
"react": "19.
|
|
61
|
-
"react-native": "0.
|
|
62
|
-
"react-native-windows": "0.
|
|
63
|
-
"webdriverio": "^
|
|
59
|
+
"react": "19.2.3",
|
|
60
|
+
"react-native": "0.84.1",
|
|
61
|
+
"react-native-windows": "0.84.0",
|
|
62
|
+
"webdriverio": "^9.29.0"
|
|
64
63
|
},
|
|
65
64
|
"peerDependencies": {
|
|
66
65
|
"react": "*",
|
package/utils/uuid.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
|
|
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;
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
|
|
6
|
+
return v.toString(16);
|
|
7
|
+
});
|
|
5
8
|
}
|
|
9
|
+
|
|
10
|
+
export {getUUID};
|
|
11
|
+
export default getUUID;
|