Skip to content

Commit 42aeaf8

Browse files
authored
Merge pull request #395 from krzysztof-miemiec/content-uri-support
Content URI support
2 parents 8049b12 + 1a9e0fa commit 42aeaf8

File tree

2 files changed

+120
-71
lines changed

2 files changed

+120
-71
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.rnfs;
2+
3+
class IORejectionException extends Exception {
4+
private String code;
5+
6+
public IORejectionException(String code, String message) {
7+
super(message);
8+
this.code = code;
9+
}
10+
11+
public String getCode() {
12+
return code;
13+
}
14+
}

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

Lines changed: 106 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.content.res.AssetFileDescriptor;
44
import android.content.res.AssetManager;
5+
import android.net.Uri;
56
import android.os.Environment;
67
import android.os.StatFs;
78
import android.support.annotation.Nullable;
@@ -19,10 +20,10 @@
1920
import com.facebook.react.bridge.WritableMap;
2021
import com.facebook.react.modules.core.RCTNativeAppEventEmitter;
2122

23+
import java.io.ByteArrayOutputStream;
2224
import java.io.File;
2325
import java.io.FileInputStream;
2426
import java.io.FileNotFoundException;
25-
import java.io.FileOutputStream;
2627
import java.io.IOException;
2728
import java.io.InputStream;
2829
import 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

Comments
 (0)