Skip to content

Commit 436017c

Browse files
author
Ron Radtke
committed
Merge remote-tracking branch 'origin/master' into develop
2 parents 66fd211 + a1ba9eb commit 436017c

32 files changed

+676
-336
lines changed

README.md

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ If you are going to use the `wifiOnly` flag, you need to add this to `AndroidMan
165165

166166
**Grant Access Permission for Android 6.0**
167167

168-
Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. So adding permissions in `AndroidManifest.xml` won't work for Android 6.0+ devices. To grant permissions in runtime, you might use [PermissionAndroid API](https://facebook.github.io/react-native/docs/permissionsandroid.html).
168+
Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. So adding permissions in `AndroidManifest.xml` won't work for Android 6.0+ devices. To grant permissions in runtime, you might use [PermissionAndroid API](https://facebook.github.io/react-native/docs/permissionsandroid).
169169

170170
## Usage
171171

@@ -303,6 +303,26 @@ ReactNativeBlobUtil
303303

304304
**These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)**
305305

306+
**Use File Transformer**
307+
308+
If you need to perform any processing on the bytes prior to it being written into storage (e.g. if you want it to be encrypted) then you can use `transform` option. NOTE: you will need to set a transformer on the libray (see [Setting a File Transformer](#Setting-A-File-Transformer))
309+
310+
```js
311+
ReactNativeBlobUtil
312+
.config({
313+
// response data will be saved to this path if it has access right.
314+
path: dirs.DocumentDir + '/path-to-file.anything',
315+
transform: true
316+
})
317+
.fetch('GET', 'http://www.example.com/file/example.zip', {
318+
//some headers ..
319+
})
320+
.then((res) => {
321+
// the path should be dirs.DocumentDir + 'path-to-file.anything'
322+
console.log('The file saved to ', res.path())
323+
})
324+
```
325+
306326
#### Upload example : Dropbox [files-upload](https://www.dropbox.com/developers/documentation/http/documentation#files-upload) API
307327

308328
`react-native-blob-util` will convert the base64 string in `body` to binary format using native API, this process is done in a separated thread so that it won't block your GUI.
@@ -676,6 +696,14 @@ await ReactNativeBlobUtil.MediaCollection.writeToMediafile('content://....', //
676696
);
677697
````
678698

699+
Copies and tranforms data from a file in the apps storage to an existing entry of the Media Store. NOTE: you must set a transformer on the file in order for the transformation to happen (see [Setting a File Transformer](#Setting-A-File-Transformer)).
700+
701+
````js
702+
await ReactNativeBlobUtil.MediaCollection.writeToMediafileWithTransform('content://....', // content uri of the entry in the media storage
703+
localpath // path to the file that should be copied
704+
);
705+
````
706+
679707
#### copyToInternal
680708
Copies an entry form the media storage to the apps internal storage.
681709
````js
@@ -697,8 +725,10 @@ File Access APIs
697725
- [dirs](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#dirs)
698726
- [createFile](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#createfilepath-data-encodingpromise)
699727
- [writeFile (0.6.0)](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise)
728+
- writeFileWithTransform
700729
- [appendFile (0.6.0) ](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#appendfilepathstring-contentstring--arraynumber-encodingstring-promisenumber)
701730
- [readFile (0.6.0)](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#readfilepath-encodingpromise)
731+
- readFileWithTransform
702732
- [readStream](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#readstreampath-encoding-buffersize-interval-promisernfbreadstream)
703733
- [hash (0.10.9)](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#hashpath-algorithm-promise)
704734
- [writeStream](https://github.com/RonRadtke/react-native-blob-util/wiki/File-System-Access-API#writestreampathstring-encodingstringpromise)
@@ -927,13 +957,59 @@ ReactNativeBlobUtil.config({
927957
})
928958
```
929959

960+
### Transform Files
961+
962+
Sometimes you may need the files to be transformed after reading from storage or before writing into storage (eg encryption/decyrption). In order to perform the transformations, use `readFileWithTransform` and `writeFileWithTransform`. NOTE: you must set a transformer on the file in order for the transformation to happen (see [Setting a File Transformer](#Setting-A-File-Transformer)).
963+
930964
## Web API Polyfills
931965

932966
After `0.8.0` we've made some [Web API polyfills](https://github.com/RonRadtke/react-native-blob-util/wiki/Web-API-Polyfills-(experimental)) that makes some browser-based library available in RN.
933967
934968
- Blob
935969
- XMLHttpRequest (Use our implementation if you're going to use it with Blob)
936970

971+
972+
## Setting A File Transformer
973+
974+
Setting a file transformer will allow you to specify how data should be transformed whenever the library is writing into storage or reading from storage. A use case for this is if you want the files handled by this library to be encrypted.
975+
976+
If you want to use a file transformer, you must implement an interface defined in:
977+
978+
[ReactNativeBlobUtilFileTransformer.h (iOS)](/ios/ReactNativeBlobUtilFileTransformer.h)
979+
980+
[ReactNativeBlobUtilFileTransformer.java (Android)](/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java)
981+
982+
Then you set the File Transformer during app startup
983+
984+
Android:
985+
```java
986+
public class MainApplication extends Application implements ReactApplication {
987+
...
988+
@Override
989+
public void onCreate() {
990+
...
991+
ReactNativeBlobUtilFileTransformer.sharedFileTransformer = new MyCustomEncryptor();
992+
...
993+
}
994+
```
995+
996+
iOS:
997+
```m
998+
@implementation AppDelegate
999+
...
1000+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
1001+
{
1002+
...
1003+
[ReactNativeBlobUtilFileTransformer setFileTransformer: MyCustomEncryptor.new];
1004+
...
1005+
}
1006+
```
1007+
1008+
Here are the places where the transformer would apply
1009+
- Reading a file from the file system
1010+
- Writing a file into the file system
1011+
- Http response is downloaded to storage directly
1012+
9371013
## Performance Tips
9381014

9391015
**Read Stream and Progress Event Overhead**

android/src/main/AndroidManifest.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.ReactNativeBlobUtil">
33

4-
<!-- Required to access Google Play Licensing -->
5-
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
6-
74
<!-- Required to download files from Google Play -->
85
<uses-permission android:name="android.permission.INTERNET" />
96

android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,11 @@ public void removeSession(ReadableArray paths, Callback callback) {
233233
}
234234

235235
@ReactMethod
236-
public void readFile(final String path, final String encoding, final Promise promise) {
236+
public void readFile(final String path, final String encoding, final boolean transformFile, final Promise promise) {
237237
threadPool.execute(new Runnable() {
238238
@Override
239239
public void run() {
240-
ReactNativeBlobUtilFS.readFile(path, encoding, promise);
240+
ReactNativeBlobUtilFS.readFile(path, encoding, transformFile, promise);
241241
}
242242
});
243243
}
@@ -253,11 +253,11 @@ public void run() {
253253
}
254254

255255
@ReactMethod
256-
public void writeFile(final String path, final String encoding, final String data, final boolean append, final Promise promise) {
256+
public void writeFile(final String path, final String encoding, final String data, final boolean transformFile, final boolean append, final Promise promise) {
257257
threadPool.execute(new Runnable() {
258258
@Override
259259
public void run() {
260-
ReactNativeBlobUtilFS.writeFile(path, encoding, data, append, promise);
260+
ReactNativeBlobUtilFS.writeFile(path, encoding, data, transformFile, append, promise);
261261
}
262262
});
263263
}
@@ -438,8 +438,8 @@ public void createMediaFile(ReadableMap filedata, String mt, Promise promise) {
438438
}
439439

440440
@ReactMethod
441-
public void writeToMediaFile(String fileUri, String path, Promise promise) {
442-
boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(Uri.parse(fileUri), path, promise);
441+
public void writeToMediaFile(String fileUri, String path, boolean transformFile, Promise promise) {
442+
boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(Uri.parse(fileUri), path, transformFile, promise);
443443
if(res) promise.resolve("Success");
444444
}
445445

@@ -478,7 +478,7 @@ public void copyToMediaStore(ReadableMap filedata, String mt, String path, Promi
478478
return;
479479
}
480480

481-
boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(fileuri, path, promise);
481+
boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(fileuri, path, false, promise);
482482
if(res) promise.resolve(fileuri.toString());
483483
}
484484

android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilBody.java

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
class ReactNativeBlobUtilBody extends RequestBody {
2828

29-
private InputStream requestStream;
3029
private long contentLength = 0;
3130
private ReadableArray form;
3231
private String mTaskId;
@@ -71,12 +70,10 @@ ReactNativeBlobUtilBody setBody(String body) {
7170
try {
7271
switch (requestType) {
7372
case SingleFile:
74-
requestStream = getRequestStream();
75-
contentLength = requestStream.available();
73+
contentLength = getRequestStream().available();
7674
break;
7775
case AsIs:
7876
contentLength = this.rawBody.getBytes().length;
79-
requestStream = new ByteArrayInputStream(this.rawBody.getBytes());
8077
break;
8178
case Others:
8279
break;
@@ -98,7 +95,6 @@ ReactNativeBlobUtilBody setBody(ReadableArray body) {
9895
this.form = body;
9996
try {
10097
bodyCache = createMultipartBodyCache();
101-
requestStream = new FileInputStream(bodyCache);
10298
contentLength = bodyCache.length();
10399
} catch (Exception ex) {
104100
ex.printStackTrace();
@@ -107,6 +103,34 @@ ReactNativeBlobUtilBody setBody(ReadableArray body) {
107103
return this;
108104
}
109105

106+
// This organizes the input stream initialization logic into a method. This allows:
107+
// 1) Initialization to be deferred until it's needed (when we are ready to pipe it into the BufferedSink)
108+
// 2) The stream to be initialized and used as many times as necessary. When okhttp runs into
109+
// a connection error, it will retry the request which will require a new stream to write into
110+
// the sink once again.
111+
InputStream getInputStreamForRequestBody() {
112+
try {
113+
if (this.form != null) {
114+
return new FileInputStream(bodyCache);
115+
} else {
116+
switch (requestType) {
117+
case SingleFile:
118+
return getRequestStream();
119+
case AsIs:
120+
return new ByteArrayInputStream(this.rawBody.getBytes());
121+
case Others:
122+
ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil could not create input stream for request type others");
123+
break;
124+
}
125+
}
126+
} catch (Exception ex){
127+
ex.printStackTrace();
128+
ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil failed to create input stream for request:" + ex.getLocalizedMessage());
129+
}
130+
131+
return null;
132+
}
133+
110134
@Override
111135
public long contentLength() {
112136
return chunkedEncoding ? -1 : contentLength;
@@ -120,7 +144,7 @@ public MediaType contentType() {
120144
@Override
121145
public void writeTo(@NonNull BufferedSink sink) {
122146
try {
123-
pipeStreamToSink(requestStream, sink);
147+
pipeStreamToSink(getInputStreamForRequestBody(), sink);
124148
} catch (Exception ex) {
125149
ReactNativeBlobUtilUtils.emitWarningEvent(ex.getLocalizedMessage());
126150
ex.printStackTrace();

android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
class ReactNativeBlobUtilConfig {
99

1010
public Boolean fileCache;
11+
public Boolean transformFile;
1112
public String path;
1213
public String appendExt;
1314
public ReadableMap addAndroidDownloads;
@@ -26,6 +27,7 @@ class ReactNativeBlobUtilConfig {
2627
if (options == null)
2728
return;
2829
this.fileCache = options.hasKey("fileCache") && options.getBoolean("fileCache");
30+
this.transformFile = options.hasKey("transformFile") ? options.getBoolean("transformFile") : false;
2931
this.path = options.hasKey("path") ? options.getString("path") : null;
3032
this.appendExt = options.hasKey("appendExt") ? options.getString("appendExt") : "";
3133
this.trusty = options.hasKey("trusty") && options.getBoolean("trusty");

android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class ReactNativeBlobUtilFS {
5454
static boolean writeFile(String path, String encoding, String data, final boolean append) {
5555
try {
5656
int written;
57+
path = ReactNativeBlobUtilUtils.normalizePath(path);
5758
File f = new File(path);
5859
File dir = f.getParentFile();
5960
if (!f.exists()) {
@@ -121,7 +122,7 @@ static boolean writeFile(String path, String encoding, String data, final boolea
121122
* @param data Array passed from JS context.
122123
* @param promise RCT Promise
123124
*/
124-
static void writeFile(String path, String encoding, String data, final boolean append, final Promise promise) {
125+
static void writeFile(String path, String encoding, String data, final boolean transformFile, final boolean append, final Promise promise) {
125126
try {
126127
int written;
127128
File f = new File(path);
@@ -169,6 +170,12 @@ static void writeFile(String path, String encoding, String data, final boolean a
169170
}
170171
} else {
171172
byte[] bytes = ReactNativeBlobUtilUtils.stringToBytes(data, encoding);
173+
if (transformFile) {
174+
if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
175+
throw new IllegalStateException("Write file with transform was specified but the shared file transformer is not set");
176+
}
177+
bytes = ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onWriteFile(bytes);
178+
}
172179
FileOutputStream fout = new FileOutputStream(f, append);
173180
try {
174181
fout.write(bytes);
@@ -237,7 +244,7 @@ static void writeFile(String path, ReadableArray data, final boolean append, fin
237244
* @param encoding Encoding of read stream.
238245
* @param promise JS promise
239246
*/
240-
static void readFile(String path, String encoding, final Promise promise) {
247+
static void readFile(String path, String encoding, final boolean transformFile, final Promise promise) {
241248
String resolved = ReactNativeBlobUtilUtils.normalizePath(path);
242249
if (resolved != null)
243250
path = resolved;
@@ -280,6 +287,13 @@ else if (resolved == null) {
280287
return;
281288
}
282289

290+
if (transformFile) {
291+
if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
292+
throw new IllegalStateException("Read file with transform was specified but the shared file transformer is not set");
293+
}
294+
bytes = ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onReadFile(bytes);
295+
}
296+
283297
switch (encoding.toLowerCase(Locale.ROOT)) {
284298
case "base64":
285299
promise.resolve(Base64.encodeToString(bytes, Base64.NO_WRAP));
@@ -441,6 +455,7 @@ private static void deleteRecursive(File fileOrDirectory) throws IOException {
441455
* @param promise JS promise
442456
*/
443457
static void mkdir(String path, Promise promise) {
458+
path = ReactNativeBlobUtilUtils.normalizePath(path);
444459
File dest = new File(path);
445460
if (dest.exists()) {
446461
promise.reject("EEXIST", (dest.isDirectory() ? "Folder" : "File") + " '" + path + "' already exists");
@@ -468,6 +483,7 @@ static void mkdir(String path, Promise promise) {
468483
*/
469484
static void cp(String path, String dest, Callback callback) {
470485
path = ReactNativeBlobUtilUtils.normalizePath(path);
486+
dest = ReactNativeBlobUtilUtils.normalizePath(dest);
471487
InputStream in = null;
472488
OutputStream out = null;
473489
String message = "";
@@ -524,6 +540,8 @@ static void cp(String path, String dest, Callback callback) {
524540
* @param callback JS context callback
525541
*/
526542
static void mv(String path, String dest, Callback callback) {
543+
path = ReactNativeBlobUtilUtils.normalizePath(path);
544+
dest = ReactNativeBlobUtilUtils.normalizePath(dest);
527545
File src = new File(path);
528546
if (!src.exists()) {
529547
callback.invoke("Source file at path `" + path + "` does not exist");
@@ -627,6 +645,7 @@ static void ls(String path, Promise promise) {
627645
static void slice(String path, String dest, int start, int end, String encode, Promise promise) {
628646
try {
629647
path = ReactNativeBlobUtilUtils.normalizePath(path);
648+
dest = ReactNativeBlobUtilUtils.normalizePath(dest);
630649
File source = new File(path);
631650
if (source.isDirectory()) {
632651
promise.reject("EISDIR", "Expecting a file but '" + path + "' is a directory");
@@ -790,6 +809,8 @@ static void hash(String path, String algorithm, Promise promise) {
790809
promise.reject("EINVAL", "Invalid algorithm '" + algorithm + "', must be one of md5, sha1, sha224, sha256, sha384, sha512");
791810
return;
792811
}
812+
813+
path = ReactNativeBlobUtilUtils.normalizePath(path);
793814

794815
File file = new File(path);
795816

@@ -837,6 +858,7 @@ static void hash(String path, String algorithm, Promise promise) {
837858
*/
838859
static void createFile(String path, String data, String encoding, Promise promise) {
839860
try {
861+
path = ReactNativeBlobUtilUtils.normalizePath(path);
840862
File dest = new File(path);
841863
boolean created = dest.createNewFile();
842864
if (encoding.equals(ReactNativeBlobUtilConst.DATA_ENCODE_URI)) {
@@ -879,6 +901,7 @@ static void createFile(String path, String data, String encoding, Promise promis
879901
*/
880902
static void createFileASCII(String path, ReadableArray data, Promise promise) {
881903
try {
904+
path = ReactNativeBlobUtilUtils.normalizePath(path);
882905
File dest = new File(path);
883906
boolean created = dest.createNewFile();
884907
if (!created) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.ReactNativeBlobUtil;
2+
3+
public class ReactNativeBlobUtilFileTransformer {
4+
public interface FileTransformer {
5+
public byte[] onWriteFile(byte[] data);
6+
public byte[] onReadFile(byte[] data);
7+
}
8+
9+
public static ReactNativeBlobUtilFileTransformer.FileTransformer sharedFileTransformer;
10+
}

0 commit comments

Comments
 (0)