Skip to content
This repository was archived by the owner on Mar 16, 2019. It is now read-only.

Commit e267dc5

Browse files
committed
Add #45 Android implementation
1 parent 17f2ab9 commit e267dc5

File tree

6 files changed

+176
-38
lines changed

6 files changed

+176
-38
lines changed

src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
public class RNFetchBlob extends ReactContextBaseJavaModule {
1414

1515
String filePathPrefix = "RNFetchBlob-file://";
16+
static ReactApplicationContext RCTContext;
1617

1718
public RNFetchBlob(ReactApplicationContext reactContext) {
19+
1820
super(reactContext);
21+
RCTContext = reactContext;
1922
}
2023

2124
@Override

src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java

Lines changed: 157 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package com.RNFetchBlob;
22

3+
import android.app.Application;
4+
import android.content.CursorLoader;
35
import android.content.Intent;
6+
import android.content.res.AssetFileDescriptor;
7+
import android.content.res.AssetManager;
8+
import android.database.Cursor;
49
import android.media.MediaScannerConnection;
510
import android.net.Uri;
611
import android.os.AsyncTask;
712
import android.os.Environment;
13+
import android.provider.MediaStore;
814

915
import com.facebook.react.bridge.Arguments;
1016
import com.facebook.react.bridge.Callback;
@@ -43,6 +49,7 @@ public class RNFetchBlobFS {
4349
ReactApplicationContext mCtx;
4450
DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
4551
String encoding = "base64";
52+
static final String assetPrefix = "bundle-assets://";
4653
boolean append = false;
4754
OutputStream writeStreamInstance = null;
4855
static HashMap<String, RNFetchBlobFS> fileStreams = new HashMap<>();
@@ -134,19 +141,33 @@ protected Void doInBackground(Object... args) {
134141
* @param promise
135142
*/
136143
static public void readFile(String path, String encoding, final Promise promise ) {
144+
path = normalizePath(path);
137145
AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
138146
@Override
139147
protected Integer doInBackground(String... strings) {
140148

141149
try {
142150
String path = strings[0];
143151
String encoding = strings[1];
144-
File f = new File(path);
145-
int length = (int) f.length();
146-
byte[] bytes = new byte[length];
147-
FileInputStream in = new FileInputStream(f);
148-
in.read(bytes);
149-
in.close();
152+
byte[] bytes;
153+
154+
if(path.startsWith(assetPrefix)) {
155+
String assetName = path.replace(assetPrefix, "");
156+
long length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
157+
bytes = new byte[(int) length];
158+
InputStream in = RNFetchBlob.RCTContext.getAssets().open(assetName);
159+
in.read(bytes, 0, (int) length);
160+
in.close();
161+
}
162+
else {
163+
File f = new File(path);
164+
int length = (int) f.length();
165+
bytes = new byte[length];
166+
FileInputStream in = new FileInputStream(f);
167+
in.read(bytes);
168+
in.close();
169+
}
170+
150171
switch (encoding.toLowerCase()) {
151172
case "base64" :
152173
promise.resolve(Base64.encodeToString(bytes, 0));
@@ -209,6 +230,7 @@ static public String getTmpPath(ReactApplicationContext ctx, String taskId) {
209230
* @param bufferSize Buffer size of read stream, default to 4096 (4095 when encode is `base64`)
210231
*/
211232
public void readStream( String path, String encoding, int bufferSize) {
233+
path = normalizePath(path);
212234
AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
213235
@Override
214236
protected Integer doInBackground(String ... args) {
@@ -221,7 +243,15 @@ protected Integer doInBackground(String ... args) {
221243
int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096;
222244
if(bufferSize > 0)
223245
chunkSize = bufferSize;
224-
FileInputStream fs = new FileInputStream(new File(path));
246+
247+
InputStream fs;
248+
if(path.startsWith(assetPrefix)) {
249+
fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
250+
}
251+
else {
252+
fs = new FileInputStream(new File(path));
253+
}
254+
225255
byte[] buffer = new byte[chunkSize];
226256
int cursor = 0;
227257
boolean error = false;
@@ -344,7 +374,7 @@ static void writeArrayChunk(String streamId, ReadableArray data, Callback callba
344374
}
345375

346376
/**
347-
* Close file stream by ID
377+
* Close file write stream by ID
348378
* @param streamId Stream ID
349379
* @param callback JS context callback
350380
*/
@@ -395,6 +425,7 @@ static void mkdir(String path, Callback callback) {
395425
* @param callback JS context callback
396426
*/
397427
static void cp(String path, String dest, Callback callback) {
428+
path = normalizePath(path);
398429
InputStream in = null;
399430
OutputStream out = null;
400431

@@ -409,7 +440,7 @@ static void cp(String path, String dest, Callback callback) {
409440
if(!new File(dest).exists())
410441
new File(dest).createNewFile();
411442

412-
in = new FileInputStream(path);
443+
in = inputStreamFromPath(path);
413444
out = new FileOutputStream(dest);
414445

415446
byte[] buf = new byte[1024];
@@ -454,9 +485,23 @@ static void mv(String path, String dest, Callback callback) {
454485
* @param callback JS context callback
455486
*/
456487
static void exists(String path, Callback callback) {
457-
boolean exist = new File(path).exists();
458-
boolean isDir = new File(path).isDirectory();;
459-
callback.invoke(exist, isDir);
488+
path = normalizePath(path);
489+
if(isAsset(path)) {
490+
try {
491+
String filename = path.replace(assetPrefix, "");
492+
AssetFileDescriptor fd = RNFetchBlob.RCTContext.getAssets().openFd(filename);
493+
// TODO : handle asset folder
494+
callback.invoke(true, false);
495+
} catch (IOException e) {
496+
callback.invoke(false, false);
497+
}
498+
}
499+
else {
500+
boolean exist = new File(path).exists();
501+
boolean isDir = new File(path).isDirectory();
502+
callback.invoke(exist, isDir);
503+
}
504+
460505
}
461506

462507
/**
@@ -465,7 +510,9 @@ static void exists(String path, Callback callback) {
465510
* @param callback JS context callback
466511
*/
467512
static void ls(String path, Callback callback) {
513+
path = normalizePath(path);
468514
File src = new File(path);
515+
// TODO : handle asset folder
469516
if(!src.exists() || !src.isDirectory()) {
470517
callback.invoke("ls error: failed to list path `" + path + "` for it is not exist or it is not a folder");
471518
return;
@@ -479,6 +526,7 @@ static void ls(String path, Callback callback) {
479526
}
480527

481528
static void lstat(String path, final Callback callback) {
529+
path = normalizePath(path);
482530
File src = new File(path);
483531
new AsyncTask<String, Integer, Integer>() {
484532
@Override
@@ -511,24 +559,55 @@ protected Integer doInBackground(String ...args) {
511559
*/
512560
static void stat(String path, Callback callback) {
513561
try {
514-
File target = new File(path);
515-
if (!target.exists()) {
516-
callback.invoke("stat error: file " + path + " does not exists");
517-
return;
518-
}
519-
WritableMap stat = Arguments.createMap();
520-
stat.putString("filename", target.getName());
521-
stat.putString("path", target.getPath());
522-
stat.putString("type", target.isDirectory() ? "directory" : "file");
523-
stat.putString("size", String.valueOf(target.length()));
524-
String lastModified = String.valueOf(target.lastModified());
525-
stat.putString("lastModified", lastModified);
526-
callback.invoke(null, stat);
562+
callback.invoke(null, statFile(path));
527563
} catch(Exception err) {
528564
callback.invoke(err.getLocalizedMessage());
529565
}
530566
}
531567

568+
/**
569+
* Basic stat method
570+
* @param path
571+
* @return Stat result of a file or path
572+
*/
573+
static WritableMap statFile(String path) {
574+
try {
575+
path = normalizePath(path);
576+
WritableMap stat = Arguments.createMap();
577+
if(isAsset(path)) {
578+
String name = path.replace(assetPrefix, "");
579+
AssetFileDescriptor fd = RNFetchBlob.RCTContext.getAssets().openFd(name);
580+
stat.putString("filename", name);
581+
stat.putString("path", path);
582+
stat.putString("type", "asset");
583+
stat.putString("size", String.valueOf(fd.getLength()));
584+
stat.putString("lastModified", "0");
585+
}
586+
else {
587+
File target = new File(path);
588+
if (!target.exists()) {
589+
return null;
590+
}
591+
stat.putString("filename", target.getName());
592+
stat.putString("path", target.getPath());
593+
stat.putString("type", target.isDirectory() ? "directory" : "file");
594+
stat.putString("size", String.valueOf(target.length()));
595+
String lastModified = String.valueOf(target.lastModified());
596+
stat.putString("lastModified", lastModified);
597+
598+
}
599+
return stat;
600+
} catch(Exception err) {
601+
return null;
602+
}
603+
}
604+
605+
/**
606+
* Media scanner scan file
607+
* @param path
608+
* @param mimes
609+
* @param callback
610+
*/
532611
void scanFile(String [] path, String[] mimes, final Callback callback) {
533612
try {
534613
MediaScannerConnection.scanFile(mCtx, path, mimes, new MediaScannerConnection.OnScanCompletedListener() {
@@ -669,18 +748,60 @@ void emitFSData(String taskId, String event, String data) {
669748
this.emitter.emit("RNFetchBlobStream" + taskId, eventData);
670749
}
671750

672-
static WritableMap statFile(String path) {
673-
File target = new File(path);
674-
if(!target.exists()) {
675-
return null;
751+
/**
752+
* Get input stream of the given path, when the path is a string starts with bundle-assets://
753+
* the stream is created by Assets Manager, otherwise use FileInputStream.
754+
* @param path
755+
* @return
756+
* @throws IOException
757+
*/
758+
static InputStream inputStreamFromPath(String path) throws IOException {
759+
if (path.startsWith(assetPrefix)) {
760+
return RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
761+
}
762+
return new FileInputStream(new File(path));
763+
}
764+
765+
/**
766+
* Check if the asset or the file exists
767+
* @param path
768+
* @return
769+
*/
770+
static boolean isPathExists(String path) {
771+
if(path.startsWith(assetPrefix)) {
772+
try {
773+
RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
774+
} catch (IOException e) {
775+
return false;
776+
}
777+
return true;
778+
}
779+
else {
780+
return new File(path).exists();
781+
}
782+
783+
}
784+
785+
static boolean isAsset(String path) {
786+
return path.startsWith(assetPrefix);
787+
}
788+
789+
static String normalizePath(String path) {
790+
if(path.startsWith("bundle-assets://")) {
791+
return path;
792+
}
793+
else if (path.startsWith("content://")) {
794+
String[] proj = { MediaStore.Images.Media.DATA };
795+
Uri contentUri = Uri.parse(path);
796+
CursorLoader loader = new CursorLoader(RNFetchBlob.RCTContext, contentUri, proj, null, null, null);
797+
Cursor cursor = loader.loadInBackground();
798+
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
799+
cursor.moveToFirst();
800+
String result = cursor.getString(column_index);
801+
cursor.close();
802+
return result;
676803
}
677-
WritableMap stat = Arguments.createMap();
678-
stat.putString("filename", target.getName());
679-
stat.putString("path", target.getPath());
680-
stat.putString("type", target.isDirectory() ? "directory" : "file");
681-
stat.putInt("size", (int)target.length());
682-
stat.putInt("lastModified", (int)target.lastModified());
683-
return stat;
804+
return path;
684805
}
685806

686807
}

src/fs.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import {
99
NativeModules,
1010
DeviceEventEmitter,
11+
Platform,
1112
NativeAppEventEmitter,
1213
} from 'react-native'
1314
import RNFetchBlobSession from './class/RNFetchBlobSession'
@@ -47,6 +48,15 @@ function session(name:string):RNFetchBlobSession {
4748
}
4849
}
4950

51+
function addAssetPrefix(path:string):string {
52+
if(Platform.OS === 'ios') {
53+
// path from camera roll
54+
if(/^assets-library\:\/\//.test(path))
55+
return path
56+
}
57+
return 'bundle-assets://' + path
58+
}
59+
5060
function createFile(path:string, data:string, encoding: 'base64' | 'ascii' | 'utf8'):Promise {
5161
encoding = encoding || 'utf8'
5262
return new Promise((resolve, reject) => {
@@ -324,5 +334,6 @@ export default {
324334
stat,
325335
lstat,
326336
scanFile,
327-
dirs
337+
dirs,
338+
addAssetPrefix
328339
}

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,5 +272,5 @@ export default {
272272
config,
273273
session,
274274
fs,
275-
wrap,
275+
wrap
276276
}

src/ios/RNFetchBlob/RNFetchBlob.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ - (NSDictionary *)constantsToExport
9797
if(content != nil) {
9898
if([content hasPrefix:self.filePathPrefix]) {
9999
NSString * orgPath = [content substringFromIndex:[self.filePathPrefix length]];
100+
orgPath = [RNFetchBlobFS getPathOfAsset:orgPath];
100101
blobData = [[NSData alloc] initWithContentsOfFile:orgPath];
101102
}
102103
else
@@ -160,6 +161,7 @@ - (NSDictionary *)constantsToExport
160161
// when body is a string contains file path prefix, try load file from the path
161162
if([body hasPrefix:self.filePathPrefix]) {
162163
NSString * orgPath = [body substringFromIndex:[self.filePathPrefix length]];
164+
orgPath = [RNFetchBlobFS getPathOfAsset:orgPath];
163165
[request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
164166
}
165167
// otherwise convert it as BASE64 data string

src/ios/RNFetchBlobFS.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#import "RNFetchBlobFS.h"
1616
#import "RNFetchBlobConst.h"
1717
@import AssetsLibrary;
18+
@import Photos;
1819

1920
NSMutableDictionary *fileStreams = nil;
2021

0 commit comments

Comments
 (0)