react-native-blob-util 0.24.7 → 0.24.8

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.
@@ -149,51 +149,56 @@ public class ReactNativeBlobUtilStream {
149
149
  */
150
150
  void writeStream(String path, String encoding, boolean append, Callback callback) {
151
151
  String resolved = ReactNativeBlobUtilUtils.normalizePath(path);
152
- if (resolved != null)
153
- path = resolved;
154
-
155
- try {
156
- File dest = new File(path);
157
- File dir = dest.getParentFile();
158
-
159
- if (resolved != null && !dest.exists()) {
160
- if (dir != null && !dir.exists()) {
161
- if (!dir.mkdirs()) {
162
- callback.invoke("ENOTDIR", "Failed to create parent directory of '" + path + "'");
163
- return;
164
- }
165
- }
166
- if (!dest.createNewFile()) {
167
- callback.invoke("ENOENT", "File '" + path + "' does not exist and could not be created");
168
- return;
169
- }
170
- } else if (dest.isDirectory()) {
171
- callback.invoke("EISDIR", "Expecting a file but '" + path + "' is a directory");
172
- return;
173
- }
174
-
175
- OutputStream fs;
176
- if (resolved != null && path.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET)) {
177
- fs = ReactNativeBlobUtilImpl.RCTContext.getAssets().openFd(path.replace(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET, "")).createOutputStream ();
178
- }
179
- // fix issue 287
180
- else if (resolved == null) {
181
- fs = ReactNativeBlobUtilImpl.RCTContext.getContentResolver().openOutputStream(Uri.parse(path));
182
- } else {
183
- fs = new FileOutputStream(path, append);
184
- }
185
- this.encoding = encoding;
186
- String streamId = UUID.randomUUID().toString();
187
- ReactNativeBlobUtilStream.fileStreams.put(streamId, this);
188
- this.writeStreamInstance = fs;
152
+ if (resolved != null)
153
+ path = resolved;
154
+
155
+ try {
156
+ OutputStream fs;
157
+ if (resolved != null && path.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET)) {
158
+ fs = ReactNativeBlobUtilImpl.RCTContext.getAssets().openFd(path.replace(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET, "")).createOutputStream ();
159
+ }
160
+ // fix issue 287
161
+ else if (resolved == null) {
162
+ fs = ReactNativeBlobUtilImpl.RCTContext.getContentResolver().openOutputStream(Uri.parse(path));
163
+ } else {
164
+ File dest = prepareOutputFile(path);
165
+ fs = new FileOutputStream(dest, append);
166
+ }
167
+ this.encoding = encoding;
168
+ String streamId = UUID.randomUUID().toString();
169
+ ReactNativeBlobUtilStream.fileStreams.put(streamId, this);
170
+ this.writeStreamInstance = fs;
189
171
  callback.invoke(null, null, streamId);
190
172
  } catch (Exception err) {
191
- callback.invoke("EUNSPECIFIED", "Failed to create write stream at path `" + path + "`; " + err.getLocalizedMessage());
192
- }
193
- }
194
-
195
- /**
196
- * Write a chunk of data into a file stream.
173
+ callback.invoke("EUNSPECIFIED", "Failed to create write stream at path `" + path + "`; " + err.getLocalizedMessage());
174
+ }
175
+ }
176
+
177
+ private static File prepareOutputFile(String path) throws IOException {
178
+ File file = new File(path).getCanonicalFile();
179
+ File parent = file.getParentFile();
180
+
181
+ if (parent == null) {
182
+ throw new IOException("Invalid output path: " + path);
183
+ }
184
+
185
+ if (!parent.exists() && !parent.mkdirs() && !parent.exists()) {
186
+ throw new IOException("Failed to create parent directory of '" + path + "'");
187
+ }
188
+
189
+ if (file.exists() && file.isDirectory()) {
190
+ throw new IOException("Expecting a file but '" + path + "' is a directory");
191
+ }
192
+
193
+ if (!file.exists() && !file.createNewFile() && !file.exists()) {
194
+ throw new IOException("File '" + path + "' does not exist and could not be created");
195
+ }
196
+
197
+ return file;
198
+ }
199
+
200
+ /**
201
+ * Write a chunk of data into a file stream.
197
202
  *
198
203
  * @param streamId File stream ID
199
204
  * @param data Data chunk in string format
@@ -52,17 +52,32 @@ public class ReactNativeBlobUtilFileResp extends ResponseBody {
52
52
  boolean appendToExistingFile = !overwrite;
53
53
  path = path.replace("?append=true", "");
54
54
  mPath = path;
55
- File f = new File(path);
55
+ File f = prepareOutputFile(path);
56
+ ofStream = new FileOutputStream(f, appendToExistingFile);
57
+ }
58
+ }
56
59
 
57
- File parent = f.getParentFile();
58
- if (parent != null && !parent.exists() && !parent.mkdirs()) {
59
- throw new IllegalStateException("Couldn't create dir: " + parent);
60
- }
60
+ private static File prepareOutputFile(String path) throws IOException {
61
+ File file = new File(path).getCanonicalFile();
62
+ File parent = file.getParentFile();
63
+
64
+ if (parent == null) {
65
+ throw new IOException("Invalid output path: " + path);
66
+ }
61
67
 
62
- if (!f.exists())
63
- f.createNewFile();
64
- ofStream = new FileOutputStream(new File(path), appendToExistingFile);
68
+ if (!parent.exists() && !parent.mkdirs() && !parent.exists()) {
69
+ throw new IOException("Couldn't create dir: " + parent);
65
70
  }
71
+
72
+ if (file.exists() && file.isDirectory()) {
73
+ throw new IOException("Output path is a directory: " + file);
74
+ }
75
+
76
+ if (!file.exists() && !file.createNewFile() && !file.exists()) {
77
+ throw new IOException("Couldn't create file: " + file);
78
+ }
79
+
80
+ return file;
66
81
  }
67
82
 
68
83
  @Override
@@ -137,8 +152,10 @@ public class ReactNativeBlobUtilFileResp extends ResponseBody {
137
152
  }
138
153
 
139
154
  return read;
155
+ } catch (IOException ex) {
156
+ throw ex;
140
157
  } catch (Exception ex) {
141
- return -1;
158
+ throw new IOException(ex);
142
159
  }
143
160
  }
144
161
 
@@ -1,84 +1,84 @@
1
- // Copyright 2016 wkh237@github. All rights reserved.
2
- // Use of this source code is governed by a MIT-style license that can be
3
- // found in the LICENSE file.
4
-
5
- import {NativeEventEmitter} from 'react-native';
6
- import UUID from '../utils/uuid';
7
-
8
- import ReactNativeBlobUtil from '../codegenSpecs/NativeBlobUtils';
9
-
10
- const emitter = new NativeEventEmitter(ReactNativeBlobUtil);
11
-
12
- export default class ReactNativeBlobUtilReadStream {
13
-
14
- path: string;
15
- encoding: 'utf8' | 'ascii' | 'base64';
16
- bufferSize: ?number;
17
- closed: boolean;
18
- tick: number = 10;
19
-
20
- constructor(path: string, encoding: string, bufferSize?: ?number, tick: number) {
21
- if (!path)
22
- throw Error('ReactNativeBlobUtil could not open file stream with empty `path`');
23
- this.encoding = encoding || 'utf8';
24
- this.bufferSize = bufferSize;
25
- this.path = path;
26
- this.closed = false;
27
- this.tick = tick;
28
- this._onData = () => {
29
- };
30
- this._onEnd = () => {
31
- };
32
- this._onError = () => {
33
- };
34
- this.streamId = 'RNFBRS' + UUID();
35
-
36
- // register for file stream event
37
- let subscription = emitter.addListener('ReactNativeBlobUtilFilesystem', (e) => {
38
- if (typeof e === 'string') e = JSON.parse(e);
39
- if (e.streamId !== this.streamId) return; // wrong stream
40
- let {event, code, detail} = e;
41
- if (this._onData && event === 'data') {
42
- this._onData(detail);
43
- return;
44
- }
45
- else if (this._onEnd && event === 'end') {
46
- this._onEnd(detail);
47
- }
48
- else {
49
- const err = new Error(detail);
50
- err.code = code || 'EUNSPECIFIED';
51
- if (this._onError)
52
- this._onError(err);
53
- else
54
- throw err;
55
- }
56
- // when stream closed or error, remove event handler
57
- if (event === 'error' || event === 'end') {
58
- subscription.remove();
59
- this.closed = true;
60
- }
61
- });
62
-
63
- }
64
-
65
- open() {
66
- if (!this.closed)
67
- ReactNativeBlobUtil.readStream(this.path, this.encoding, this.bufferSize || 10240, this.tick || -1, this.streamId);
68
- else
69
- throw new Error('Stream closed');
70
- }
71
-
72
- onData(fn: () => void) {
73
- this._onData = fn;
74
- }
75
-
76
- onError(fn) {
77
- this._onError = fn;
78
- }
79
-
80
- onEnd(fn) {
81
- this._onEnd = fn;
82
- }
83
-
84
- }
1
+ // Copyright 2016 wkh237@github. All rights reserved.
2
+ // Use of this source code is governed by a MIT-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import {NativeEventEmitter} from 'react-native';
6
+ import UUID from '../utils/uuid';
7
+
8
+ import ReactNativeBlobUtil from '../codegenSpecs/NativeBlobUtils';
9
+
10
+ const emitter = new NativeEventEmitter(ReactNativeBlobUtil);
11
+
12
+ export default class ReactNativeBlobUtilReadStream {
13
+
14
+ path: string;
15
+ encoding: 'utf8' | 'ascii' | 'base64';
16
+ bufferSize: ?number;
17
+ closed: boolean;
18
+ tick: number = 10;
19
+
20
+ constructor(path: string, encoding: string, bufferSize?: ?number, tick: number) {
21
+ if (!path)
22
+ throw Error('ReactNativeBlobUtil could not open file stream with empty `path`');
23
+ this.encoding = encoding || 'utf8';
24
+ this.bufferSize = bufferSize;
25
+ this.path = path;
26
+ this.closed = false;
27
+ this.tick = tick;
28
+ this._onData = () => {
29
+ };
30
+ this._onEnd = () => {
31
+ };
32
+ this._onError = () => {
33
+ };
34
+ this.streamId = 'RNFBRS' + UUID();
35
+
36
+ // register for file stream event
37
+ let subscription = emitter.addListener('ReactNativeBlobUtilFilesystem', (e) => {
38
+ if (typeof e === 'string') e = JSON.parse(e);
39
+ if (e.streamId !== this.streamId) return; // wrong stream
40
+ let {event, code, detail} = e;
41
+ if (this._onData && event === 'data') {
42
+ this._onData(detail);
43
+ return;
44
+ }
45
+ else if (this._onEnd && event === 'end') {
46
+ this._onEnd(detail);
47
+ }
48
+ else {
49
+ const err = new Error(detail);
50
+ err.code = code || 'EUNSPECIFIED';
51
+ if (this._onError)
52
+ this._onError(err);
53
+ else
54
+ throw err;
55
+ }
56
+ // when stream closed or error, remove event handler
57
+ if (event === 'error' || event === 'end') {
58
+ subscription.remove();
59
+ this.closed = true;
60
+ }
61
+ });
62
+
63
+ }
64
+
65
+ open() {
66
+ if (!this.closed)
67
+ ReactNativeBlobUtil.readStream(this.path, this.encoding, this.bufferSize || 10240, this.tick || -1, this.streamId);
68
+ else
69
+ throw new Error('Stream closed');
70
+ }
71
+
72
+ onData(fn: () => void) {
73
+ this._onData = fn;
74
+ }
75
+
76
+ onError(fn) {
77
+ this._onError = fn;
78
+ }
79
+
80
+ onEnd(fn) {
81
+ this._onEnd = fn;
82
+ }
83
+
84
+ }
package/index.d.ts CHANGED
@@ -3,9 +3,9 @@
3
3
  // Definitions by: MNB <https://github.com/MNBuyskih>
4
4
  // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
5
5
 
6
- export const ReactNativeBlobUtil: ReactNativeBlobUtilStatic;
7
- export type ReactNativeBlobUtil = ReactNativeBlobUtilStatic;
6
+ declare const ReactNativeBlobUtil: ReactNativeBlobUtilStatic;
8
7
  export default ReactNativeBlobUtil;
8
+ export type ReactNativeBlobUtil = ReactNativeBlobUtilStatic;
9
9
  import { filedescriptor } from "./types";
10
10
  import CanceledFetchError from "./class/ReactNativeBlobUtilCanceledFetchError";
11
11
 
@@ -800,7 +800,7 @@ export interface AddAndroidDownloads {
800
800
  /**
801
801
  * If true android download manager will try to save the file to the apps Download direcotry
802
802
  */
803
- storeLocal?: boolean
803
+ storeLocal?: boolean;
804
804
  }
805
805
 
806
806
  export interface ReactNativeBlobUtilResponseInfo {
@@ -878,4 +878,4 @@ export interface MediaCollection {
878
878
  * @param encoding
879
879
  */
880
880
  getBlob(contenturi: string, encoding: string): Promise<string>;
881
- }
881
+ }
@@ -41,6 +41,7 @@
41
41
  - (void) cancelRequest:(NSString * _Nonnull)taskId;
42
42
  - (void) enableProgressReport:(NSString * _Nonnull) taskId config:(ReactNativeBlobUtilProgress * _Nullable)config;
43
43
  - (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(ReactNativeBlobUtilProgress * _Nullable)config;
44
+ - (void) removeRequestForTaskId:(NSString * _Nonnull)taskId;
44
45
 
45
46
 
46
47
  @end
@@ -45,7 +45,7 @@ static void initialize_tables() {
45
45
  - (id)init {
46
46
  self = [super init];
47
47
  if (self) {
48
- self.requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];
48
+ self.requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory];
49
49
 
50
50
  self.taskQueue = [[NSOperationQueue alloc] init];
51
51
  self.taskQueue.qualityOfService = NSQualityOfServiceUtility;
@@ -85,8 +85,10 @@ static void initialize_tables() {
85
85
  callback:callback];
86
86
 
87
87
  @synchronized([ReactNativeBlobUtilNetwork class]) {
88
- [self.requestsTable setObject:request forKey:taskId];
89
- [self checkProgressConfigForTask:taskId];
88
+ if (taskId != nil && request.task != nil) {
89
+ [self.requestsTable setObject:request forKey:taskId];
90
+ [self checkProgressConfigForTask:taskId];
91
+ }
90
92
  }
91
93
  }
92
94
 
@@ -138,6 +140,9 @@ static void initialize_tables() {
138
140
 
139
141
  @synchronized ([ReactNativeBlobUtilNetwork class]) {
140
142
  task = [self.requestsTable objectForKey:taskId].task;
143
+ [self.requestsTable removeObjectForKey:taskId];
144
+ [self.rebindProgressDict removeObjectForKey:taskId];
145
+ [self.rebindUploadProgressDict removeObjectForKey:taskId];
141
146
  }
142
147
 
143
148
  if (task && task.state == NSURLSessionTaskStateRunning) {
@@ -145,6 +150,15 @@ static void initialize_tables() {
145
150
  }
146
151
  }
147
152
 
153
+ - (void) removeRequestForTaskId:(NSString *)taskId
154
+ {
155
+ @synchronized ([ReactNativeBlobUtilNetwork class]) {
156
+ [self.requestsTable removeObjectForKey:taskId];
157
+ [self.rebindProgressDict removeObjectForKey:taskId];
158
+ [self.rebindUploadProgressDict removeObjectForKey:taskId];
159
+ }
160
+ }
161
+
148
162
  // removing case from headers
149
163
  + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers
150
164
  {
@@ -33,7 +33,7 @@
33
33
  @property (nullable, nonatomic) NSError * error;
34
34
  @property (nullable, nonatomic) ReactNativeBlobUtilProgress *progressConfig;
35
35
  @property (nullable, nonatomic) ReactNativeBlobUtilProgress *uploadProgressConfig;
36
- @property (nullable, nonatomic, weak) NSURLSessionTask *task;
36
+ @property (nullable, strong, nonatomic) NSURLSessionTask *task;
37
37
 
38
38
  - (void) sendRequest:(NSDictionary * _Nullable )options
39
39
  contentLength:(long)contentLength
@@ -11,6 +11,7 @@
11
11
  #import "ReactNativeBlobUtilFS.h"
12
12
  #import "ReactNativeBlobUtilConst.h"
13
13
  #import "ReactNativeBlobUtilFileTransformer.h"
14
+ #import "ReactNativeBlobUtilNetwork.h"
14
15
  #import "ReactNativeBlobUtilReqBuilder.h"
15
16
 
16
17
  #import <CommonCrypto/CommonDigest.h>
@@ -489,6 +490,7 @@ typedef NS_ENUM(NSUInteger, ResponseFormat) {
489
490
  }
490
491
  ]);
491
492
 
493
+ [[ReactNativeBlobUtilNetwork sharedInstance] removeRequestForTaskId:self.taskId];
492
494
  respData = nil;
493
495
  receivedBytes = 0;
494
496
  [session finishTasksAndInvalidate];
package/package.json CHANGED
@@ -1,16 +1,24 @@
1
1
  {
2
- "name" : "react-native-blob-util",
3
- "version" : "0.24.7",
4
- "description" : "A module provides upload, download, and files access API. Supports file stream read/write for process large files.",
5
- "main" : "index",
6
- "scripts" : {
7
- "test" : "echo \"Error: no test specified\" && exit 1"
2
+ "name": "react-native-blob-util",
3
+ "version": "0.24.8",
4
+ "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.",
5
+ "main": "index",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "e2e:server": "node tests/e2e/server.js",
9
+ "e2e:android": "node tests/e2e/run-all.js --platforms android",
10
+ "e2e:ios": "node tests/e2e/run-all.js --platforms ios",
11
+ "e2e:windows": "node tests/e2e/run-all.js --platforms windows",
12
+ "e2e:runner": "node tests/e2e/appium/run.js",
13
+ "e2e:all": "node tests/e2e/run-all.js"
8
14
  },
9
- "dependencies" : {
10
- "base-64" : "0.1.0",
11
- "glob" : "13.0.1"
15
+ "dependencies": {
16
+ "appium-uiautomator2-driver": "^7.0.0",
17
+ "base-64": "0.1.0",
18
+ "glob": "13.0.1",
19
+ "uuid" : "^13.0.0"
12
20
  },
13
- "keywords" : [
21
+ "keywords": [
14
22
  "react-native",
15
23
  "fetch",
16
24
  "blob",
@@ -24,54 +32,55 @@
24
32
  "ios",
25
33
  "file system"
26
34
  ],
27
- "repository" : {
28
- "url" : "https://github.com/RonRadtke/react-native-blob-util"
35
+ "repository": {
36
+ "url": "https://github.com/RonRadtke/react-native-blob-util"
29
37
  },
30
- "author" : {
31
- "name" : "RonRadtke",
32
- "username" : "RonRadtke"
38
+ "author": {
39
+ "name": "RonRadtke",
40
+ "username": "RonRadtke"
33
41
  },
34
42
  "funding": {
35
- "type": "github",
36
- "url": "https://github.com/sponsors/ronradtke"
43
+ "type": "github",
44
+ "url": "https://github.com/sponsors/ronradtke"
37
45
  },
38
- "license" : "MIT",
39
- "contributors" : [
46
+ "license": "MIT",
47
+ "contributors": [
40
48
  "Traviskn <>",
41
49
  "Ben <benhsieh@catchplay.com>",
42
50
  "wkh237 <xeiyan@gmail.com>"
43
51
  ],
44
- "devDependencies" : {
52
+ "devDependencies": {
45
53
  "@typescript-eslint/eslint-plugin": "^8.46.4",
46
54
  "@typescript-eslint/parser": "^8.46.4",
47
55
  "eslint": "^8.57.1",
48
- "eslint-plugin-ft-flow" : "^3.0.11",
49
- "eslint-plugin-import" : "^2.32.0",
50
- "eslint-plugin-react" : "^7.37.5",
51
- "eslint-plugin-react-native" : "^5.0.0",
52
- "react" : "19.0.0",
53
- "react-native" : "0.78.2",
54
- "react-native-windows" : "0.78.2"
56
+ "eslint-plugin-ft-flow": "^3.0.11",
57
+ "eslint-plugin-import": "^2.32.0",
58
+ "eslint-plugin-react": "^7.37.5",
59
+ "eslint-plugin-react-native": "^5.0.0",
60
+ "react": "19.0.0",
61
+ "react-native": "0.78.2",
62
+ "react-native-windows": "0.78.2",
63
+ "webdriverio": "^8.41.0"
55
64
  },
56
- "peerDependencies" : {
57
- "react" : "*",
58
- "react-native" : "*"
65
+ "peerDependencies": {
66
+ "react": "*",
67
+ "react-native": "*"
59
68
  },
60
- "codegenConfig" : {
61
- "name" : "ReactNativeBlobUtilSpec",
62
- "type" : "modules",
63
- "jsSrcsDir" : "codegenSpecs",
64
- "windows" : {
65
- "namespace" : "ReactNativeBlobUtilCodegen",
66
- "outputDirectory" : "windows/ReactNativeBlobUtil/codegen",
67
- "separateDataTypes" : true
69
+ "codegenConfig": {
70
+ "name": "ReactNativeBlobUtilSpec",
71
+ "type": "modules",
72
+ "jsSrcsDir": "codegenSpecs",
73
+ "windows": {
74
+ "namespace": "ReactNativeBlobUtilCodegen",
75
+ "outputDirectory": "windows/ReactNativeBlobUtil/codegen",
76
+ "separateDataTypes": true
68
77
  }
69
78
  },
70
- "react-native-windows" : {
71
- "init-windows" : {
72
- "name" : "ReactNativeBlobUtil",
73
- "namespace" : "ReactNativeBlobUtil",
74
- "template" : "cpp-lib"
79
+ "react-native-windows": {
80
+ "init-windows": {
81
+ "name": "ReactNativeBlobUtil",
82
+ "namespace": "ReactNativeBlobUtil",
83
+ "template": "cpp-lib"
75
84
  }
76
85
  }
77
86
  }
package/utils/uuid.js CHANGED
@@ -1,4 +1,5 @@
1
+ import {v4 as uuidv4} from 'uuid';
2
+
1
3
  export default function getUUID() {
2
- return Math.random().toString(36).substring(2, 15) +
3
- Math.random().toString(36).substring(2, 15);
4
+ return uuidv4();
4
5
  }