22
33import android .content .res .AssetFileDescriptor ;
44import android .content .res .AssetManager ;
5+ import android .net .Uri ;
56import android .os .Environment ;
67import android .os .StatFs ;
78import android .support .annotation .Nullable ;
1920import com .facebook .react .bridge .WritableMap ;
2021import com .facebook .react .modules .core .RCTNativeAppEventEmitter ;
2122
23+ import java .io .ByteArrayOutputStream ;
2224import java .io .File ;
2325import java .io .FileInputStream ;
2426import java .io .FileNotFoundException ;
25- import java .io .FileOutputStream ;
2627import java .io .IOException ;
2728import java .io .InputStream ;
2829import java .io .OutputStream ;
@@ -46,22 +47,85 @@ public class RNFSManager extends ReactContextBaseJavaModule {
4647 private static final String RNFSFileTypeDirectory = "RNFSFileTypeDirectory" ;
4748
4849 private SparseArray <Downloader > downloaders = new SparseArray <Downloader >();
50+ private ReactApplicationContext reactContext ;
4951
5052 public RNFSManager (ReactApplicationContext reactContext ) {
5153 super (reactContext );
54+ this .reactContext = reactContext ;
5255 }
5356
5457 @ Override
5558 public String getName () {
5659 return "RNFSManager" ;
5760 }
5861
62+ private Uri getFileUri (String filepath ) throws IORejectionException {
63+ Uri uri = Uri .parse (filepath );
64+ if (uri .getScheme () == null ) {
65+ // No prefix, assuming that provided path is absolute path to file
66+ File file = new File (filepath );
67+ if (file .isDirectory ()) {
68+ throw new IORejectionException ("EISDIR" , "EISDIR: illegal operation on a directory, read '" + filepath + "'" );
69+ }
70+ uri = Uri .parse ("file://" + filepath );
71+ }
72+ return uri ;
73+ }
74+
75+ private InputStream getInputStream (String filepath ) throws IORejectionException {
76+ Uri uri = getFileUri (filepath );
77+ InputStream stream ;
78+ try {
79+ stream = reactContext .getContentResolver ().openInputStream (uri );
80+ } catch (FileNotFoundException ex ) {
81+ throw new IORejectionException ("ENOENT" , "ENOENT: no such file or directory, open '" + filepath + "'" );
82+ }
83+ if (stream == null ) {
84+ throw new IORejectionException ("ENOENT" , "ENOENT: could not open an input stream for '" + filepath + "'" );
85+ }
86+ return stream ;
87+ }
88+
89+ private OutputStream getOutputStream (String filepath , boolean append ) throws IORejectionException {
90+ Uri uri = getFileUri (filepath );
91+ OutputStream stream ;
92+ try {
93+ stream = reactContext .getContentResolver ().openOutputStream (uri , append ? "wa" : "w" );
94+ } catch (FileNotFoundException ex ) {
95+ throw new IORejectionException ("ENOENT" , "ENOENT: no such file or directory, open '" + filepath + "'" );
96+ }
97+ if (stream == null ) {
98+ throw new IORejectionException ("ENOENT" , "ENOENT: could not open an output stream for '" + filepath + "'" );
99+ }
100+ return stream ;
101+ }
102+
103+ private static byte [] getInputStreamBytes (InputStream inputStream ) throws IOException {
104+ byte [] bytesResult ;
105+ ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream ();
106+ int bufferSize = 1024 ;
107+ byte [] buffer = new byte [bufferSize ];
108+ try {
109+ int len ;
110+ while ((len = inputStream .read (buffer )) != -1 ) {
111+ byteBuffer .write (buffer , 0 , len );
112+ }
113+ bytesResult = byteBuffer .toByteArray ();
114+ } finally {
115+ try {
116+ byteBuffer .close ();
117+ } catch (IOException ignored ) {
118+ }
119+ }
120+ return bytesResult ;
121+ }
122+
59123 @ ReactMethod
60124 public void writeFile (String filepath , String base64Content , Promise promise ) {
61125 try {
62126 byte [] bytes = Base64 .decode (base64Content , Base64 .DEFAULT );
63127
64- FileOutputStream outputStream = new FileOutputStream (filepath , false );
128+ OutputStream outputStream = getOutputStream (filepath , false );
65129 outputStream .write (bytes );
66130 outputStream .close ();
67131
@@ -77,7 +141,7 @@ public void appendFile(String filepath, String base64Content, Promise promise) {
77141 try {
78142 byte [] bytes = Base64 .decode (base64Content , Base64 .DEFAULT );
79143
80- FileOutputStream outputStream = new FileOutputStream (filepath , true );
144+ OutputStream outputStream = getOutputStream (filepath , true );
81145 outputStream .write (bytes );
82146 outputStream .close ();
83147
@@ -94,7 +158,7 @@ public void write(String filepath, String base64Content, int position, Promise p
94158 byte [] bytes = Base64 .decode (base64Content , Base64 .DEFAULT );
95159
96160 if (position < 0 ) {
97- FileOutputStream outputStream = new FileOutputStream (filepath , true );
161+ OutputStream outputStream = getOutputStream (filepath , true );
98162 outputStream .write (bytes );
99163 outputStream .close ();
100164 } else {
@@ -125,23 +189,9 @@ public void exists(String filepath, Promise promise) {
125189 @ ReactMethod
126190 public void readFile (String filepath , Promise promise ) {
127191 try {
128- File file = new File (filepath );
129-
130- if (file .isDirectory ()) {
131- rejectFileIsDirectory (promise );
132- return ;
133- }
134-
135- if (!file .exists ()) {
136- rejectFileNotFound (promise , filepath );
137- return ;
138- }
139-
140- FileInputStream inputStream = new FileInputStream (filepath );
141- byte [] buffer = new byte [(int )file .length ()];
142- inputStream .read (buffer );
143-
144- String base64Content = Base64 .encodeToString (buffer , Base64 .NO_WRAP );
192+ InputStream inputStream = getInputStream (filepath );
193+ byte [] inputData = getInputStreamBytes (inputStream );
194+ String base64Content = Base64 .encodeToString (inputData , Base64 .NO_WRAP );
145195
146196 promise .resolve (base64Content );
147197 } catch (Exception ex ) {
@@ -151,31 +201,19 @@ public void readFile(String filepath, Promise promise) {
151201 }
152202
153203 @ ReactMethod
154- public void read (String filepath , int length , int position , Promise promise ){
204+ public void read (String filepath , int length , int position , Promise promise ) {
155205 try {
156- File file = new File (filepath );
157-
158- if (file .isDirectory ()) {
159- rejectFileIsDirectory (promise );
160- return ;
161- }
162-
163- if (!file .exists ()) {
164- rejectFileNotFound (promise , filepath );
165- return ;
166- }
167-
168- FileInputStream inputStream = new FileInputStream (filepath );
206+ InputStream inputStream = getInputStream (filepath );
169207 byte [] buffer = new byte [length ];
170208 inputStream .skip (position );
171- inputStream .read (buffer ,0 , length );
209+ inputStream .read (buffer , 0 , length );
172210
173211 String base64Content = Base64 .encodeToString (buffer , Base64 .NO_WRAP );
174212
175213 promise .resolve (base64Content );
176214 } catch (Exception ex ) {
177- ex .printStackTrace ();
178- reject (promise , filepath , ex );
215+ ex .printStackTrace ();
216+ reject (promise , filepath , ex );
179217 }
180218 }
181219
@@ -194,7 +232,7 @@ public void readFileAssets(String filepath, Promise promise) {
194232 byte [] buffer = new byte [stream .available ()];
195233 stream .read (buffer );
196234 String base64Content = Base64 .encodeToString (buffer , Base64 .NO_WRAP );
197- promise .resolve (base64Content );;
235+ promise .resolve (base64Content );
198236 } catch (Exception ex ) {
199237 ex .printStackTrace ();
200238 reject (promise , filepath , ex );
@@ -237,7 +275,7 @@ public void hash(String filepath, String algorithm, Promise promise) {
237275 MessageDigest md = MessageDigest .getInstance (algorithms .get (algorithm ));
238276
239277 FileInputStream inputStream = new FileInputStream (filepath );
240- byte [] buffer = new byte [(int )file .length ()];
278+ byte [] buffer = new byte [(int ) file .length ()];
241279
242280 int read ;
243281 while ((read = inputStream .read (buffer )) != -1 ) {
@@ -285,9 +323,9 @@ public void copyFile(String filepath, String destPath, Promise promise) {
285323 }
286324 }
287325
288- private void copyFile (String filepath , String destPath ) throws IOException {
289- InputStream in = new FileInputStream (filepath );
290- OutputStream out = new FileOutputStream (destPath );
326+ private void copyFile (String filepath , String destPath ) throws IOException , IORejectionException {
327+ InputStream in = getInputStream (filepath );
328+ OutputStream out = getOutputStream (destPath , false );
291329
292330 byte [] buffer = new byte [1024 ];
293331 int length ;
@@ -312,10 +350,10 @@ public void readDir(String directory, Promise promise) {
312350 for (File childFile : files ) {
313351 WritableMap fileMap = Arguments .createMap ();
314352
315- fileMap .putDouble ("mtime" , (double )childFile .lastModified ()/ 1000 );
353+ fileMap .putDouble ("mtime" , (double ) childFile .lastModified () / 1000 );
316354 fileMap .putString ("name" , childFile .getName ());
317355 fileMap .putString ("path" , childFile .getAbsolutePath ());
318- fileMap .putInt ("size" , (int )childFile .length ());
356+ fileMap .putInt ("size" , (int ) childFile .length ());
319357 fileMap .putInt ("type" , childFile .isDirectory () ? 1 : 0 );
320358
321359 fileMaps .pushMap (fileMap );
@@ -416,35 +454,27 @@ public void existsAssets(String filepath, Promise promise) {
416454
417455 /**
418456 * Internal method for copying that works with any InputStream
419- * @param in InputStream from assets or file
420- * @param source source path (only used for logging errors)
457+ *
458+ * @param in InputStream from assets or file
459+ * @param source source path (only used for logging errors)
421460 * @param destination destination path
422- * @param promise React Callback
461+ * @param promise React Callback
423462 */
424463 private void copyInputStream (InputStream in , String source , String destination , Promise promise ) {
425464 OutputStream out = null ;
426465 try {
427- File outFile = new File (destination );
428- try {
429- out = new FileOutputStream (outFile );
430- } catch (FileNotFoundException e ) {
431- reject (promise , source , e );
432- return ;
433- }
466+ out = getOutputStream (destination , false );
434467
435- try {
436- byte [] buffer = new byte [1024 * 10 ]; // 10k buffer
437- int read ;
438- while ((read = in .read (buffer )) != -1 ) {
439- out .write (buffer , 0 , read );
440- }
441- } catch (IOException e ) {
442- reject (promise , source , new Exception (String .format ("Failed to copy '%s' to %s (%s)" , source , destination , e .getLocalizedMessage ())));
443- return ;
468+ byte [] buffer = new byte [1024 * 10 ]; // 10k buffer
469+ int read ;
470+ while ((read = in .read (buffer )) != -1 ) {
471+ out .write (buffer , 0 , read );
444472 }
445473
446474 // Success!
447475 promise .resolve (null );
476+ } catch (Exception ex ) {
477+ reject (promise , source , new Exception (String .format ("Failed to copy '%s' to %s (%s)" , source , destination , ex .getLocalizedMessage ())));
448478 } finally {
449479 if (in != null ) {
450480 try {
@@ -486,9 +516,9 @@ public void stat(String filepath, Promise promise) {
486516
487517 WritableMap statMap = Arguments .createMap ();
488518
489- statMap .putInt ("ctime" , (int )(file .lastModified () / 1000 ));
490- statMap .putInt ("mtime" , (int )(file .lastModified () / 1000 ));
491- statMap .putInt ("size" , (int )file .length ());
519+ statMap .putInt ("ctime" , (int ) (file .lastModified () / 1000 ));
520+ statMap .putInt ("mtime" , (int ) (file .lastModified () / 1000 ));
521+ statMap .putInt ("size" , (int ) file .length ());
492522 statMap .putInt ("type" , file .isDirectory () ? 1 : 0 );
493523
494524 promise .resolve (statMap );
@@ -544,8 +574,8 @@ public void mkdir(String filepath, ReadableMap options, Promise promise) {
544574
545575 private void sendEvent (ReactContext reactContext , String eventName , @ Nullable WritableMap params ) {
546576 reactContext
547- .getJSModule (RCTNativeAppEventEmitter .class )
548- .emit (eventName , params );
577+ .getJSModule (RCTNativeAppEventEmitter .class )
578+ .emit (eventName , params );
549579 }
550580
551581 @ ReactMethod
@@ -660,8 +690,8 @@ public void getFSInfo(Promise promise) {
660690 freeSpace = blockSize * stat .getAvailableBlocks ();
661691 }
662692 WritableMap info = Arguments .createMap ();
663- info .putDouble ("totalSpace" , (double )totalSpace ); // Int32 too small, must use Double
664- info .putDouble ("freeSpace" , (double )freeSpace );
693+ info .putDouble ("totalSpace" , (double ) totalSpace ); // Int32 too small, must use Double
694+ info .putDouble ("freeSpace" , (double ) freeSpace );
665695 promise .resolve (info );
666696 }
667697
@@ -681,6 +711,11 @@ private void reject(Promise promise, String filepath, Exception ex) {
681711 rejectFileNotFound (promise , filepath );
682712 return ;
683713 }
714+ if (ex instanceof IORejectionException ) {
715+ IORejectionException ioRejectionException = (IORejectionException ) ex ;
716+ promise .reject (ioRejectionException .getCode (), ioRejectionException .getMessage ());
717+ return ;
718+ }
684719
685720 promise .reject (null , ex .getMessage ());
686721 }
0 commit comments