Skip to content

Commit e46f687

Browse files
committed
Merge branch 'master' of github.com:itinance/react-native-fs
* 'master' of github.com:itinance/react-native-fs: Make inner variables final Add missing AsyncTask import Typo fixes Use AsyncTask for copyFileTask so that it occurs in a background thread rather than blocking the UI thread while the copy takes place Fix more types Fix types Typescript type definitions Use the correct parameters for the native Windows implementation of the 'writeFile' function. PMP-5747 Fix EISDIR on stat directory Add missing arg to moveFile Fix windows dev environment Fix crash with large downloads Add missing `options` arguments Fix compatibility with latest `react-native-windows` Update README.md Update README.md Add scanFile method (Android only)
2 parents 93fd895 + a8d7f78 commit e46f687

File tree

11 files changed

+2432
-1905
lines changed

11 files changed

+2432
-1905
lines changed

FS.common.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,10 @@ var RNFS = {
569569
);
570570
},
571571

572+
scanFile(path: string): Promise<ReadDirItem[]> {
573+
return RNFSManager.scanFile(path);
574+
},
575+
572576
MainBundlePath: RNFSManager.RNFSMainBundlePath,
573577
CachesDirectoryPath: RNFSManager.RNFSCachesDirectoryPath,
574578
ExternalCachesDirectoryPath: RNFSManager.RNFSExternalCachesDirectoryPath,

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,8 @@ The following constants are available on the `RNFS` export:
370370
- `ExternalDirectoryPath` (`String`) The absolute path to the external files, shared directory (android only)
371371
- `ExternalStorageDirectoryPath` (`String`) The absolute path to the external storage, shared directory (android only)
372372

373+
IMPORTANT: when using `ExternalStorageDirectoryPath` it's necessary to request permissions (on Android) to read and write on the external storage, here an example: [React Native Offical Doc] (https://facebook.github.io/react-native/docs/permissionsandroid)
374+
373375
### `readDir(dirpath: string): Promise<ReadDirItem[]>`
374376

375377
Reads the contents of `path`. This must be an absolute path. Use the above path constants to form a usable file path.
@@ -694,6 +696,10 @@ type FSInfoResult = {
694696
};
695697
```
696698

699+
### (Android only) `scanFile(path: string): Promise<string[]>`
700+
701+
Scan the file using [Media Scanner](https://developer.android.com/reference/android/media/MediaScannerConnection).
702+
697703
### (Android only) `getAllExternalFilesDirs(): Promise<string[]>`
698704

699705
Returns an array with the absolute paths to application-specific directories on all shared/external storage devices where the application can place persistent files it owns.

android/src/main/java/com/rnfs/RNFSManager.java

Lines changed: 76 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
import android.content.res.AssetManager;
55
import android.database.Cursor;
66
import android.net.Uri;
7+
import android.os.AsyncTask;
78
import android.os.Environment;
89
import android.os.StatFs;
910
import android.provider.MediaStore;
1011
import android.support.annotation.Nullable;
1112
import android.util.Base64;
1213
import android.util.SparseArray;
14+
import android.media.MediaScannerConnection;
15+
import android.net.Uri;
1316

1417
import com.facebook.react.bridge.Arguments;
1518
import com.facebook.react.bridge.Promise;
@@ -66,21 +69,21 @@ public String getName() {
6669
return "RNFSManager";
6770
}
6871

69-
private Uri getFileUri(String filepath) throws IORejectionException {
72+
private Uri getFileUri(String filepath, boolean isDirectoryAllowed) throws IORejectionException {
7073
Uri uri = Uri.parse(filepath);
7174
if (uri.getScheme() == null) {
7275
// No prefix, assuming that provided path is absolute path to file
7376
File file = new File(filepath);
74-
if (file.isDirectory()) {
77+
if (!isDirectoryAllowed && file.isDirectory()) {
7578
throw new IORejectionException("EISDIR", "EISDIR: illegal operation on a directory, read '" + filepath + "'");
7679
}
7780
uri = Uri.parse("file://" + filepath);
7881
}
7982
return uri;
8083
}
8184

82-
private String getOriginalFilepath(String filepath) throws IORejectionException {
83-
Uri uri = getFileUri(filepath);
85+
private String getOriginalFilepath(String filepath, boolean isDirectoryAllowed) throws IORejectionException {
86+
Uri uri = getFileUri(filepath, isDirectoryAllowed);
8487
String originalFilepath = filepath;
8588
if (uri.getScheme().equals("content")) {
8689
try {
@@ -95,7 +98,7 @@ private String getOriginalFilepath(String filepath) throws IORejectionException
9598
}
9699

97100
private InputStream getInputStream(String filepath) throws IORejectionException {
98-
Uri uri = getFileUri(filepath);
101+
Uri uri = getFileUri(filepath, false);
99102
InputStream stream;
100103
try {
101104
stream = reactContext.getContentResolver().openInputStream(uri);
@@ -109,7 +112,7 @@ private InputStream getInputStream(String filepath) throws IORejectionException
109112
}
110113

111114
private OutputStream getOutputStream(String filepath, boolean append) throws IORejectionException {
112-
Uri uri = getFileUri(filepath);
115+
Uri uri = getFileUri(filepath, false);
113116
OutputStream stream;
114117
try {
115118
stream = reactContext.getContentResolver().openOutputStream(uri, append ? "wa" : "w");
@@ -316,46 +319,69 @@ public void hash(String filepath, String algorithm, Promise promise) {
316319
}
317320

318321
@ReactMethod
319-
public void moveFile(String filepath, String destPath, ReadableMap options, Promise promise) {
322+
public void moveFile(final String filepath, String destPath, ReadableMap options, final Promise promise) {
320323
try {
321-
File inFile = new File(filepath);
324+
final File inFile = new File(filepath);
322325

323326
if (!inFile.renameTo(new File(destPath))) {
324-
copyFile(filepath, destPath);
325-
326-
inFile.delete();
327+
new CopyFileTask() {
328+
@Override
329+
protected void onPostExecute (Exception ex) {
330+
if (ex == null) {
331+
inFile.delete();
332+
promise.resolve(true);
333+
} else {
334+
ex.printStackTrace();
335+
reject(promise, filepath, ex);
336+
}
337+
}
338+
}.execute(filepath, destPath);
339+
} else {
340+
promise.resolve(true);
327341
}
328-
329-
promise.resolve(true);
330342
} catch (Exception ex) {
331343
ex.printStackTrace();
332344
reject(promise, filepath, ex);
333345
}
334346
}
335347

336348
@ReactMethod
337-
public void copyFile(String filepath, String destPath, ReadableMap options, Promise promise) {
338-
try {
339-
copyFile(filepath, destPath);
340-
341-
promise.resolve(null);
342-
} catch (Exception ex) {
343-
ex.printStackTrace();
344-
reject(promise, filepath, ex);
345-
}
349+
public void copyFile(final String filepath, final String destPath, ReadableMap options, final Promise promise) {
350+
new CopyFileTask() {
351+
@Override
352+
protected void onPostExecute (Exception ex) {
353+
if (ex == null) {
354+
promise.resolve(null);
355+
} else {
356+
ex.printStackTrace();
357+
reject(promise, filepath, ex);
358+
}
359+
}
360+
}.execute(filepath, destPath);
346361
}
347362

348-
private void copyFile(String filepath, String destPath) throws IOException, IORejectionException {
349-
InputStream in = getInputStream(filepath);
350-
OutputStream out = getOutputStream(destPath, false);
363+
private class CopyFileTask extends AsyncTask<String, Void, Exception> {
364+
protected Exception doInBackground(String... paths) {
365+
try {
366+
String filepath = paths[0];
367+
String destPath = paths[1];
351368

352-
byte[] buffer = new byte[1024];
353-
int length;
354-
while ((length = in.read(buffer)) > 0) {
355-
out.write(buffer, 0, length);
369+
InputStream in = getInputStream(filepath);
370+
OutputStream out = getOutputStream(destPath, false);
371+
372+
byte[] buffer = new byte[1024];
373+
int length;
374+
while ((length = in.read(buffer)) > 0) {
375+
out.write(buffer, 0, length);
376+
Thread.yield();
377+
}
378+
in.close();
379+
out.close();
380+
return null;
381+
} catch (Exception ex) {
382+
return ex;
383+
}
356384
}
357-
in.close();
358-
out.close();
359385
}
360386

361387
@ReactMethod
@@ -532,7 +558,7 @@ public void setReadable(String filepath, Boolean readable, Boolean ownerOnly, Pr
532558
@ReactMethod
533559
public void stat(String filepath, Promise promise) {
534560
try {
535-
String originalFilepath = getOriginalFilepath(filepath);
561+
String originalFilepath = getOriginalFilepath(filepath, true);
536562
File file = new File(originalFilepath);
537563

538564
if (!file.exists()) throw new Exception("File does not exist");
@@ -597,8 +623,8 @@ public void mkdir(String filepath, ReadableMap options, Promise promise) {
597623

598624
private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
599625
reactContext
600-
.getJSModule(RCTNativeAppEventEmitter.class)
601-
.emit(eventName, params);
626+
.getJSModule(RCTNativeAppEventEmitter.class)
627+
.emit(eventName, params);
602628
}
603629

604630
@ReactMethod
@@ -816,6 +842,22 @@ public void getAllExternalFilesDirs(Promise promise){
816842
promise.resolve(fs);
817843
}
818844

845+
@ReactMethod
846+
public void scanFile(String path, final Promise promise) {
847+
MediaScannerConnection.scanFile(this.getReactApplicationContext(),
848+
new String[]{path},
849+
null,
850+
new MediaScannerConnection.MediaScannerConnectionClient() {
851+
@Override
852+
public void onMediaScannerConnected() {}
853+
@Override
854+
public void onScanCompleted(String path, Uri uri) {
855+
promise.resolve(path);
856+
}
857+
}
858+
);
859+
}
860+
819861
private void reject(Promise promise, String filepath, Exception ex) {
820862
if (ex instanceof FileNotFoundException) {
821863
rejectFileNotFound(promise, filepath);

0 commit comments

Comments
 (0)