From 8121dbf66db16999653a590b1ee41c48add6dba9 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Mon, 27 Mar 2017 22:28:13 +0800 Subject: [PATCH 01/54] Apply possible fix to fs.readFile and fs.readStream for #287 --- .../java/com/RNFetchBlob/RNFetchBlobFS.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java index b7237a461..7457dba19 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java @@ -138,11 +138,13 @@ static public void writeFile(String path, ReadableArray data, final boolean appe * @param promise */ static public void readFile(String path, String encoding, final Promise promise ) { - path = normalizePath(path); + String resolved = normalizePath(path); + if(resolved != null) + path = resolved; try { byte[] bytes; - if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { + if(resolved != null && resolved.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { String assetName = path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, ""); long length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength(); bytes = new byte[(int) length]; @@ -150,6 +152,14 @@ static public void readFile(String path, String encoding, final Promise promise in.read(bytes, 0, (int) length); in.close(); } + // issue 287 + else if(resolved == null) { + InputStream in = RNFetchBlob.RCTContext.getContentResolver().openInputStream(Uri.parse(path)); + int length = (int) in.available(); + bytes = new byte[length]; + in.read(bytes); + in.close(); + } else { File f = new File(path); int length = (int) f.length(); @@ -225,7 +235,9 @@ static public String getTmpPath(ReactApplicationContext ctx, String taskId) { * @param bufferSize Buffer size of read stream, default to 4096 (4095 when encode is `base64`) */ public void readStream(String path, String encoding, int bufferSize, int tick, final String streamId) { - path = normalizePath(path); + String resolved = normalizePath(path); + if(resolved != null) + path = resolved; try { int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096; @@ -233,9 +245,13 @@ public void readStream(String path, String encoding, int bufferSize, int tick, f chunkSize = bufferSize; InputStream fs; - if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { + if(resolved != null && path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "")); } + // fix issue 287 + else if(resolved == null) { + fs = RNFetchBlob.RCTContext.getContentResolver().openInputStream(Uri.parse(path)); + } else { fs = new FileInputStream(new File(path)); } From 40fefd4b2e29bdb13c3cbf1e64e0ced2dac33da7 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Sun, 16 Apr 2017 16:34:11 +0800 Subject: [PATCH 02/54] Add Android fs.readFile app provider URI support #287 --- .../java/com/RNFetchBlob/Utils/PathResolver.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java b/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java index e5742b81e..0327c8e26 100644 --- a/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java +++ b/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java @@ -59,6 +59,14 @@ else if (isMediaDocument(uri)) { return getDataColumn(context, contentUri, selection, selectionArgs); } + else if ("content".equalsIgnoreCase(uri.getScheme())) { + + // Return the remote address + if (isGooglePhotosUri(uri)) + return uri.getLastPathSegment(); + + return getDataColumn(context, uri, null, null); + } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { @@ -103,7 +111,12 @@ public static String getDataColumn(Context context, Uri uri, String selection, final int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } - } finally { + } + catch (Exception ex) { + ex.printStackTrace(); + return null; + } + finally { if (cursor != null) cursor.close(); } From 7752ff70f875de6e3f441e9e189cdfbb04e04eec Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Sun, 14 May 2017 10:54:55 +0800 Subject: [PATCH 03/54] Remove log state from Android file response handler function #353 --- .../main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java b/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java index bc9b3779c..88fc7c69a 100644 --- a/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java +++ b/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java @@ -81,7 +81,6 @@ public long read(Buffer sink, long byteCount) throws IOException { byte[] bytes = new byte[(int) byteCount]; long read = originalBody.byteStream().read(bytes, 0, (int) byteCount); bytesDownloaded += read > 0 ? read : 0; - Log.i("bytes downloaded", String.valueOf(byteCount) + "/" + String.valueOf(read) + "=" + String.valueOf(bytesDownloaded)); if (read > 0) { ofStream.write(bytes, 0, (int) read); } From 1ebbb89a21cc1b9c2b5dd17fdd38e30c0a0f925e Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Sun, 14 May 2017 11:01:52 +0800 Subject: [PATCH 04/54] Fix typos in FileReader polyfill #347 --- polyfill/FileReader.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/polyfill/FileReader.js b/polyfill/FileReader.js index 3a4e29618..b72df17f7 100644 --- a/polyfill/FileReader.js +++ b/polyfill/FileReader.js @@ -47,15 +47,15 @@ export default class FileReader extends EventTarget { } abort() { - log.verbose('abort', b, label) + log.verbose('abort') } readAsArrayBuffer(b:Blob) { - log.verbose('readAsArrayBuffer', b, label) + log.verbose('readAsArrayBuffer', b) } readAsBinaryString(b:Blob) { - log.verbose('readAsBinaryString', b, label) + log.verbose('readAsBinaryString', b) } readAsText(b:Blob, label:?string) { @@ -63,7 +63,7 @@ export default class FileReader extends EventTarget { } readAsDataURL(b:Blob) { - log.verbose('readAsDataURL', b, label) + log.verbose('readAsDataURL', b) } dispatchEvent(event, e) { @@ -78,7 +78,7 @@ export default class FileReader extends EventTarget { // getters and setters - get readState() { + get readyState() { return this._readyState } From 680331d68bee97312f7e3ec1514ee9db4e5da46f Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Mon, 15 May 2017 20:55:29 +0800 Subject: [PATCH 05/54] Optimize JS performance by using different UID generator and remove bind statements --- polyfill/XMLHttpRequest.js | 20 ++++++++++---------- utils/uuid.js | 6 ++---- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/polyfill/XMLHttpRequest.js b/polyfill/XMLHttpRequest.js index 661545906..89171921f 100644 --- a/polyfill/XMLHttpRequest.js +++ b/polyfill/XMLHttpRequest.js @@ -196,11 +196,11 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ }) .fetch(_method, _url, _headers, body) this._task - .stateChange(this._headerReceived.bind(this)) - .uploadProgress(this._uploadProgressEvent.bind(this)) - .progress(this._progressEvent.bind(this)) - .catch(this._onError.bind(this)) - .then(this._onDone.bind(this)) + .stateChange(this._headerReceived) + .uploadProgress(this._uploadProgressEvent) + .progress(this._progressEvent) + .catch(this._onError) + .then(this._onDone) }) } @@ -274,7 +274,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ return result.substr(0, result.length-2) } - _headerReceived(e) { + _headerReceived = (e) => { log.debug('header received ', this._task.taskId, e) this.responseURL = this._url if(e.state === "2") { @@ -285,7 +285,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ } } - _uploadProgressEvent(send:number, total:number) { + _uploadProgressEvent = (send:number, total:number) => { if(!this._uploadStarted) { this.upload.dispatchEvent('loadstart') this._uploadStarted = true @@ -295,7 +295,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ this.upload.dispatchEvent('progress', new ProgressEvent(true, send, total)) } - _progressEvent(send:number, total:number, chunk:string) { + _progressEvent = (send:number, total:number, chunk:string) => { log.verbose(this.readyState) if(this._readyState === XMLHttpRequest.HEADERS_RECEIVED) this._dispatchReadStateChange(XMLHttpRequest.LOADING) @@ -310,7 +310,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ this.dispatchEvent('progress', e) } - _onError(err) { + _onError = (err) => { let statusCode = Math.floor(this.status) if(statusCode >= 100 && statusCode !== 408) { return @@ -331,7 +331,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ this.clearEventListeners() } - _onDone(resp) { + _onDone = (resp) => { log.debug('XMLHttpRequest done', this._url, resp, this) this._statusText = this._status let responseDataReady = () => { diff --git a/utils/uuid.js b/utils/uuid.js index e7d0e300f..f147e1f3e 100644 --- a/utils/uuid.js +++ b/utils/uuid.js @@ -1,6 +1,4 @@ export default function getUUID() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); - return v.toString(16); - }); + return Math.random().toString(36).substring(2, 15) + + Math.random().toString(36).substring(2, 15); } From fd9f20d4f275e2a469e4282951f550f324a8d454 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Thu, 18 May 2017 09:19:43 +0800 Subject: [PATCH 06/54] Update ISSUE_TEMPLATE --- .github/ISSUE_TEMPLATE | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 65d12bb4c..25e78c4cb 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -4,3 +4,4 @@ You may want to take a look on that page or find issues tagged "trouble shooting * please provide the version of installed library and RN project. * a sample code snippet/repository is very helpful to spotting the problem. * issues which have been tagged as 'needs feedback', will be closed after 2 weeks if receive no feedbacks. +* issues lack of detailed information will be closed without any feedback From f17783b8e75e509da955589261919391d3b2dad3 Mon Sep 17 00:00:00 2001 From: Yonsh Lin Date: Fri, 26 May 2017 18:10:40 +0800 Subject: [PATCH 07/54] Fix some Promise.reject not being returned in FileSystem (#366) --- fs.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs.js b/fs.js index ee211dd41..eed79ab27 100644 --- a/fs.js +++ b/fs.js @@ -171,12 +171,12 @@ function writeFile(path:string, data:string | Array, encoding:?string):P return Promise.reject('Invalid argument "path" ') if(encoding.toLocaleLowerCase() === 'ascii') { if(!Array.isArray(data)) - Promise.reject(new Error(`Expected "data" is an Array when encoding is "ascii", however got ${typeof data}`)) + return Promise.reject(new Error(`Expected "data" is an Array when encoding is "ascii", however got ${typeof data}`)) else return RNFetchBlob.writeFileArray(path, data, false); } else { if(typeof data !== 'string') - Promise.reject(new Error(`Expected "data" is a String when encoding is "utf8" or "base64", however got ${typeof data}`)) + return Promise.reject(new Error(`Expected "data" is a String when encoding is "utf8" or "base64", however got ${typeof data}`)) else return RNFetchBlob.writeFile(path, encoding, data, false); } @@ -188,12 +188,12 @@ function appendFile(path:string, data:string | Array, encoding:?string): return Promise.reject('Invalid argument "path" ') if(encoding.toLocaleLowerCase() === 'ascii') { if(!Array.isArray(data)) - Promise.reject(new Error(`Expected "data" is an Array when encoding is "ascii", however got ${typeof data}`)) + return Promise.reject(new Error(`Expected "data" is an Array when encoding is "ascii", however got ${typeof data}`)) else return RNFetchBlob.writeFileArray(path, data, true); } else { if(typeof data !== 'string') - Promise.reject(new Error(`Expected "data" is a String when encoding is "utf8" or "base64", however got ${typeof data}`)) + return Promise.reject(new Error(`Expected "data" is a String when encoding is "utf8" or "base64", however got ${typeof data}`)) else return RNFetchBlob.writeFile(path, encoding, data, true); } From acf4aa5578b57556fcc9c5212537ceb23ec987b6 Mon Sep 17 00:00:00 2001 From: Guy Blank Date: Fri, 26 May 2017 13:11:07 +0300 Subject: [PATCH 08/54] Add support for SDCards applications's directory (Android only) (#372) --- android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java | 1 + fs.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java index 8517283a8..def758e05 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java @@ -203,6 +203,7 @@ static public Map getSystemfolders(ReactApplicationContext ctx) state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { res.put("SDCardDir", Environment.getExternalStorageDirectory().getAbsolutePath()); + res.put("SDCardApplicationDir", ctx.getExternalFilesDir(null).getParentFile().getAbsolutePath()); } res.put("MainBundleDir", ctx.getApplicationInfo().dataDir); return res; diff --git a/fs.js b/fs.js index eed79ab27..83e6bdceb 100644 --- a/fs.js +++ b/fs.js @@ -29,7 +29,9 @@ const dirs = { DownloadDir : RNFetchBlob.DownloadDir, DCIMDir : RNFetchBlob.DCIMDir, SDCardDir : RNFetchBlob.SDCardDir, - MainBundleDir : RNFetchBlob.MainBundleDir + SDCardApplicationDir : RNFetchBlob.SDCardApplicationDir, + MainBundleDir : RNFetchBlob.MainBundleDir, + LibraryDir : RNFetchBlob.LibraryDir } /** From f345afa27e59ac0924212577ff13c690efa42e1b Mon Sep 17 00:00:00 2001 From: Jon San Miguel Date: Tue, 30 May 2017 08:58:25 -0400 Subject: [PATCH 09/54] Android Feature: Handle Arbitrary Providers (#374) * Update ISSUE_TEMPLATE * Handle Android content providers --- .github/ISSUE_TEMPLATE | 1 + .../com/RNFetchBlob/Utils/PathResolver.java | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 65d12bb4c..25e78c4cb 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -4,3 +4,4 @@ You may want to take a look on that page or find issues tagged "trouble shooting * please provide the version of installed library and RN project. * a sample code snippet/repository is very helpful to spotting the problem. * issues which have been tagged as 'needs feedback', will be closed after 2 weeks if receive no feedbacks. +* issues lack of detailed information will be closed without any feedback diff --git a/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java b/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java index e5742b81e..381a3f9f3 100644 --- a/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java +++ b/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java @@ -8,6 +8,11 @@ import android.provider.MediaStore; import android.content.ContentUris; import android.os.Environment; +import android.content.ContentResolver; +import com.RNFetchBlob.RNFetchBlobUtils; +import java.io.File; +import java.io.InputStream; +import java.io.FileOutputStream; public class PathResolver { public static String getRealPathFromURI(final Context context, final Uri uri) { @@ -59,6 +64,29 @@ else if (isMediaDocument(uri)) { return getDataColumn(context, contentUri, selection, selectionArgs); } + // Other Providers + else { + try { + InputStream attachment = context.getContentResolver().openInputStream(uri); + if (attachment != null) { + String filename = getContentName(context.getContentResolver(), uri); + if (filename != null) { + File file = new File(context.getCacheDir(), filename); + FileOutputStream tmp = new FileOutputStream(file); + byte[] buffer = new byte[1024]; + while (attachment.read(buffer) > 0) { + tmp.write(buffer); + } + tmp.close(); + attachment.close(); + return file.getAbsolutePath(); + } + } + } catch (Exception e) { + RNFetchBlobUtils.emitWarningEvent(e.toString()); + return null; + } + } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { @@ -77,6 +105,18 @@ else if ("file".equalsIgnoreCase(uri.getScheme())) { return null; } + private static String getContentName(ContentResolver resolver, Uri uri) { + Cursor cursor = resolver.query(uri, null, null, null, null); + cursor.moveToFirst(); + int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME); + if (nameIndex >= 0) { + String name = cursor.getString(nameIndex); + cursor.close(); + return name; + } + return null; + } + /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. From 869bb47d161ae17f4d7611c9ff57ee36446f1bf2 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Tue, 30 May 2017 21:03:47 +0800 Subject: [PATCH 10/54] Update contributor list --- CONTRIBUTORS.md | 12 ++++++++++++ package.json | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 63c11724f..33ce358a2 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,7 +1,9 @@ 960px Andreas Amsenius +Andrew Jack Arthur Ouaki Binur Konarbai +Bronco Chris Sloey Corentin Smith Dmitry Petukhov @@ -9,16 +11,26 @@ Dombi Soma Kristóf Erik Smartt Evgeniy Baraniuk Frank van der Hoek +Guy Blank +Jon San Miguel Juan B. Rodriguez Kaishley Martin Giachetti +Max Gurela Mike Monteith Naoki AINOYA Nguyen Cao Nhat Linh +Nick Pomfret +Oliver Petter Hesselberg +Reza Ghorbani +Steve Liles Tim Suchanek +Yonsh Lin +atlanteh follower francisco-sanchez-molina +gferreyra91 hhravn kejinliang pedramsaleh diff --git a/package.json b/package.json index c842ce5c2..9059164d8 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "author": "wkh237 ", "license": "MIT", "contributors": [ + "Ben ", "" ] -} +} \ No newline at end of file From 93509a7a880b044661bdd32bdc2f430f0ffb137e Mon Sep 17 00:00:00 2001 From: atlanteh Date: Sun, 4 Jun 2017 17:22:33 +0300 Subject: [PATCH 11/54] remove redundant console.log every AppState change (#385) --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 0f68fa2e1..5c7f5f9c0 100644 --- a/index.js +++ b/index.js @@ -51,7 +51,6 @@ const RNFetchBlob = NativeModules.RNFetchBlob // their .expire event if(Platform.OS === 'ios') { AppState.addEventListener('change', (e) => { - console.log('app state changed', e) if(e === 'active') RNFetchBlob.emitExpiredEvent(()=>{}) }) From 4ea868fe7908023f331840e5a903ac78048fd7dd Mon Sep 17 00:00:00 2001 From: Jacob Lauritzen Date: Tue, 6 Jun 2017 05:19:27 +0200 Subject: [PATCH 12/54] Android share cookies w/ RN, remove cookie utils (#388) --- .../java/com/RNFetchBlob/RNFetchBlob.java | 57 +++++-------- .../java/com/RNFetchBlob/RNFetchBlobReq.java | 15 ++-- .../com/RNFetchBlob/RNFetchBlobUtils.java | 4 +- .../com/RNFetchBlob/Utils/RNFBCookieJar.java | 66 --------------- index.js | 2 - ios/RNFetchBlob/RNFetchBlob.m | 19 ----- ios/RNFetchBlobNetwork.h | 2 - ios/RNFetchBlobNetwork.m | 83 ------------------- net.js | 36 -------- 9 files changed, 27 insertions(+), 257 deletions(-) delete mode 100644 android/src/main/java/com/RNFetchBlob/Utils/RNFBCookieJar.java delete mode 100644 net.js diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java index 88f42ca74..d046f1804 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java @@ -3,9 +3,7 @@ import android.app.Activity; import android.content.Intent; import android.net.Uri; -import android.util.Log; -import com.RNFetchBlob.Utils.RNFBCookieJar; import com.facebook.react.bridge.ActivityEventListener; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.LifecycleEventListener; @@ -15,12 +13,16 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; + +// Cookies +import com.facebook.react.modules.network.ForwardingCookieHandler; +import com.facebook.react.modules.network.CookieJarContainer; +import com.facebook.react.modules.network.OkHttpClientProvider; +import okhttp3.OkHttpClient; +import okhttp3.JavaNetCookieJar; import java.util.HashMap; import java.util.Map; -import java.util.UUID; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -30,6 +32,11 @@ public class RNFetchBlob extends ReactContextBaseJavaModule { + // Cookies + private final ForwardingCookieHandler mCookieHandler; + private final CookieJarContainer mCookieJarContainer; + private final OkHttpClient mClient; + static ReactApplicationContext RCTContext; static LinkedBlockingQueue taskQueue = new LinkedBlockingQueue<>(); static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue); @@ -42,6 +49,11 @@ public RNFetchBlob(ReactApplicationContext reactContext) { super(reactContext); + mClient = OkHttpClientProvider.getOkHttpClient(); + mCookieHandler = new ForwardingCookieHandler(reactContext); + mCookieJarContainer = (CookieJarContainer) mClient.cookieJar(); + mCookieJarContainer.setCookieJar(new JavaNetCookieJar(mCookieHandler)); + RCTContext = reactContext; reactContext.addActivityEventListener(new ActivityEventListener() { @Override @@ -252,35 +264,6 @@ public void run() { } - @ReactMethod - /** - * Get cookies belongs specific host. - * @param host String domain name. - */ - public void getCookies(String domain, Promise promise) { - try { - WritableMap cookies = RNFBCookieJar.getCookies(domain); - promise.resolve(cookies); - } catch(Exception err) { - promise.reject("RNFetchBlob.getCookies", err.getMessage()); - } - } - - @ReactMethod - /** - * Remove cookies for specific domain - * @param domain String of the domain - * @param promise JSC promise injected by RN - */ - public void removeCookies(String domain, Promise promise) { - try { - RNFBCookieJar.removeCookies(domain); - promise.resolve(null); - } catch(Exception err) { - promise.reject("RNFetchBlob.removeCookies", err.getMessage()); - } - } - @ReactMethod /** * @param path Stream file path @@ -338,12 +321,12 @@ public void enableUploadProgressReport(String taskId, int interval, int count) { @ReactMethod public void fetchBlob(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, final Callback callback) { - new RNFetchBlobReq(options, taskId, method, url, headers, body, null, callback).run(); - } + new RNFetchBlobReq(options, taskId, method, url, headers, body, null, mClient, callback).run(); +} @ReactMethod public void fetchBlobForm(ReadableMap options, String taskId, String method, String url, ReadableMap headers, ReadableArray body, final Callback callback) { - new RNFetchBlobReq(options, taskId, method, url, headers, null, body, callback).run(); + new RNFetchBlobReq(options, taskId, method, url, headers, null, body, mClient, callback).run(); } @ReactMethod diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index 8af663ecf..f01e82efb 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -11,7 +11,6 @@ import com.RNFetchBlob.Response.RNFetchBlobDefaultResp; import com.RNFetchBlob.Response.RNFetchBlobFileResp; -import com.RNFetchBlob.Utils.RNFBCookieJar; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; @@ -21,14 +20,12 @@ import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.modules.network.OkHttpClientProvider; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.CookieHandler; -import java.net.CookieManager; -import java.net.CookiePolicy; import java.net.MalformedURLException; import java.net.SocketException; import java.net.SocketTimeoutException; @@ -43,7 +40,6 @@ import okhttp3.Call; import okhttp3.ConnectionPool; -import okhttp3.CookieJar; import okhttp3.Headers; import okhttp3.Interceptor; import okhttp3.MediaType; @@ -98,8 +94,9 @@ enum ResponseFormat { WritableMap respInfo; boolean timeout = false; ArrayList redirects = new ArrayList<>(); + OkHttpClient client; - public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, final Callback callback) { + public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, OkHttpClient client, final Callback callback) { this.method = method.toUpperCase(); this.options = new RNFetchBlobConfig(options); this.taskId = taskId; @@ -108,6 +105,7 @@ public RNFetchBlobReq(ReadableMap options, String taskId, String method, String this.callback = callback; this.rawRequestBody = body; this.rawRequestBodyArray = arrayBody; + this.client = client; if(this.options.fileCache || this.options.path != null) responseType = ResponseType.FileStorage; @@ -196,7 +194,7 @@ else if(this.options.fileCache) if (this.options.trusty) { clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient(); } else { - clientBuilder = new OkHttpClient.Builder(); + clientBuilder = client.newBuilder(); } final Request.Builder builder = new Request.Builder(); @@ -297,10 +295,7 @@ else if(cType.isEmpty()) { } // #156 fix cookie issue - - final Request req = builder.build(); - clientBuilder.cookieJar(new RNFBCookieJar()); clientBuilder.addNetworkInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java index 479bd4a50..6e976d775 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java @@ -52,7 +52,7 @@ public static void emitWarningEvent(String data) { .emit(RNFetchBlobConst.EVENT_MESSAGE, args); } - public static OkHttpClient.Builder getUnsafeOkHttpClient() { + public static OkHttpClient.Builder getUnsafeOkHttpClient(OkHttpClient client) { try { // Create a trust manager that does not validate certificate chains final TrustManager[] trustAllCerts = new TrustManager[]{ @@ -78,7 +78,7 @@ public java.security.cert.X509Certificate[] getAcceptedIssuers() { // Create an ssl socket factory with our all-trusting manager final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - OkHttpClient.Builder builder = new OkHttpClient.Builder(); + OkHttpClient.Builder builder = client.newBuilder(); builder.sslSocketFactory(sslSocketFactory); builder.hostnameVerifier(new HostnameVerifier() { @Override diff --git a/android/src/main/java/com/RNFetchBlob/Utils/RNFBCookieJar.java b/android/src/main/java/com/RNFetchBlob/Utils/RNFBCookieJar.java deleted file mode 100644 index 0075cfa90..000000000 --- a/android/src/main/java/com/RNFetchBlob/Utils/RNFBCookieJar.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.RNFetchBlob.Utils; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Set; - -import okhttp3.Cookie; -import okhttp3.CookieJar; -import okhttp3.HttpUrl; - -/** - * Created by wkh237on 2016/10/14. - */ - - - -public class RNFBCookieJar implements CookieJar { - - static final HashMap> cookieStore = new HashMap<>(); - private List cookies; - - @Override - public void saveFromResponse(HttpUrl url, List cookies) { - cookieStore.put(url.host(), cookies); - } - - @Override - public List loadForRequest(HttpUrl url) { - List cookies = cookieStore.get(url.host()); - return cookies != null ? cookies : new ArrayList(); - } - - public static void removeCookies(String domain) { - if(domain != null && domain.length() > 0) { - if(cookieStore.containsKey(domain)) - cookieStore.remove(domain); - } - else - cookieStore.clear(); - } - - public static WritableMap getCookies(String host) { - Set domains = cookieStore.keySet(); - WritableMap cookieMap = Arguments.createMap(); - if(host.length() > 0 && cookieStore.containsKey(host)) { - domains.clear(); - domains.add(host); - } - // no domain specified, return all cookies - for(String key : domains) { - WritableArray cookiesInDomain = Arguments.createArray(); - for(Cookie c: cookieStore.get(key)){ - cookiesInDomain.pushString(c.toString()); - } - cookieMap.putArray(key, cookiesInDomain); - } - - return cookieMap; - } -} diff --git a/index.js b/index.js index 0f68fa2e1..cb16b45eb 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,6 @@ import polyfill from './polyfill' import _ from 'lodash' import android from './android' import ios from './ios' -import net from './net' import JSONStream from './json-stream' const { RNFetchBlobSession, @@ -564,7 +563,6 @@ export default { session, fs, wrap, - net, polyfill, JSONStream } diff --git a/ios/RNFetchBlob/RNFetchBlob.m b/ios/RNFetchBlob/RNFetchBlob.m index 4ba969d7b..246d6707c 100644 --- a/ios/RNFetchBlob/RNFetchBlob.m +++ b/ios/RNFetchBlob/RNFetchBlob.m @@ -580,25 +580,6 @@ - (UIViewController *) documentInteractionControllerViewControllerForPreview: (U return window.rootViewController; } -# pragma mark - getCookies - -RCT_EXPORT_METHOD(getCookies:(NSString *)url resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) -{ - resolve([RNFetchBlobNetwork getCookies:url]); -} - -# pragma mark - removeCookie - -RCT_EXPORT_METHOD(removeCookies:(NSString *)domain resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) -{ - NSError * err = nil; - [RNFetchBlobNetwork removeCookies:domain error:&err]; - if(err) - reject(@"RNFetchBlob failed to remove cookie", @"RNFetchBlob failed to remove cookie", nil); - else - resolve(@[[NSNull null]]); -} - # pragma mark - check expired network events RCT_EXPORT_METHOD(emitExpiredEvent:(RCTResponseSenderBlock)callback) diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index f07f63f1d..d3b4654a5 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -49,10 +49,8 @@ typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse - (nullable id) init; - (void) sendRequest; - (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; -+ (void) removeCookies:(NSString *) domain error:(NSError **)error; + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config; + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config; -+ (NSDictionary *) getCookies:(NSString *) url; diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 6adbfccfa..85030b743 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -578,89 +578,6 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen } } -# pragma mark - cookies handling API - -+ (NSDictionary *) getCookies:(NSString *) domain -{ - NSMutableDictionary * result = [NSMutableDictionary new]; - NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - for(NSHTTPCookie * cookie in [cookieStore cookies]) - { - NSString * cDomain = [cookie domain]; - if([result objectForKey:cDomain] == nil) - { - [result setObject:[NSMutableArray new] forKey:cDomain]; - } - if([cDomain isEqualToString:domain] || [domain length] == 0) - { - NSMutableString * cookieStr = [[NSMutableString alloc] init]; - cookieStr = [[self class] getCookieString:cookie]; - NSMutableArray * ary = [result objectForKey:cDomain]; - [ary addObject:cookieStr]; - [result setObject:ary forKey:cDomain]; - } - } - return result; -} - -// remove cookies for given domain, if domain is empty remove all cookies in shared cookie storage. -+ (void) removeCookies:(NSString *) domain error:(NSError **)error -{ - @try - { - NSHTTPCookieStorage * cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - for(NSHTTPCookie * cookie in [cookies cookies]) - { - BOOL shouldRemove = domain == nil || [domain length] < 1 || [[cookie domain] isEqualToString:domain]; - if(shouldRemove) - { - [cookies deleteCookie:cookie]; - } - } - } - @catch(NSError * err) - { - *error = err; - } -} - -// convert NSHTTPCookie to string -+ (NSString *) getCookieString:(NSHTTPCookie *) cookie -{ - NSMutableString * cookieStr = [[NSMutableString alloc] init]; - [cookieStr appendString:cookie.name]; - [cookieStr appendString:@"="]; - [cookieStr appendString:cookie.value]; - - if(cookie.expiresDate == nil) { - [cookieStr appendString:@"; max-age=0"]; - } - else { - [cookieStr appendString:@"; expires="]; - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - [dateFormatter setDateFormat:@"EEE, dd MM yyyy HH:mm:ss ZZZ"]; - NSString *strDate = [dateFormatter stringFromDate:cookie.expiresDate]; - [cookieStr appendString:strDate]; - } - - - [cookieStr appendString:@"; domain="]; - [cookieStr appendString: [cookie domain]]; - [cookieStr appendString:@"; path="]; - [cookieStr appendString:cookie.path]; - - - if (cookie.isSecure) { - [cookieStr appendString:@"; secure"]; - } - - if (cookie.isHTTPOnly) { - [cookieStr appendString:@"; httponly"]; - } - return cookieStr; - -} - + (void) cancelRequest:(NSString *)taskId { NSURLSessionDataTask * task = [taskTable objectForKey:taskId]; diff --git a/net.js b/net.js deleted file mode 100644 index d48388de5..000000000 --- a/net.js +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 wkh237@github. All rights reserved. -// Use of this source code is governed by a MIT-style license that can be -// found in the LICENSE file. - -import { - NativeModules, - DeviceEventEmitter, - Platform, - NativeAppEventEmitter, -} from 'react-native' - -const RNFetchBlob = NativeModules.RNFetchBlob - -/** - * Get cookie according to the given url. - * @param {string} domain Domain of the cookies to be removed, remove all - * @return {Promise>} Cookies of a specific domain. - */ -function getCookies(domain:string):Promise> { - return RNFetchBlob.getCookies(domain || '') -} - -/** - * Remove cookies for a specific domain - * @param {?string} domain Domain of the cookies to be removed, remove all - * cookies when this is null. - * @return {Promise} - */ -function removeCookies(domain:?string):Promise { - return RNFetchBlob.removeCookies(domain || '') -} - -export default { - getCookies, - removeCookies -} From 5e554ac807a5f167528a8f302b5f53fedc2c5bc3 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Tue, 6 Jun 2017 16:08:39 +0800 Subject: [PATCH 13/54] Add missing argument to RNFetchBlobReq.java #388 --- android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index f01e82efb..db213c1e8 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -192,7 +192,7 @@ else if(this.options.fileCache) try { // use trusty SSL socket if (this.options.trusty) { - clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient(); + clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient(client); } else { clientBuilder = client.newBuilder(); } From d807f0f4affcdeb0b9d62e66a6a110474a69f105 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Sun, 11 Jun 2017 16:26:38 +0800 Subject: [PATCH 14/54] Add missing Android implementation --- android.js | 14 +++++++++++--- .../main/java/com/RNFetchBlob/RNFetchBlobFS.java | 12 ++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/android.js b/android.js index e39d52d32..0b5dbd84b 100644 --- a/android.js +++ b/android.js @@ -21,18 +21,26 @@ function actionViewIntent(path:string, mime:string = 'text/plain') { if(Platform.OS === 'android') return RNFetchBlob.actionViewIntent(path, mime) else - return Promise.reject('RNFetchBlob.actionViewIntent only supports Android.') + return Promise.reject('RNFetchBlob.android.actionViewIntent only supports Android.') } function getContentIntent(mime:string) { if(Platform.OS === 'android') return RNFetchBlob.getContentIntent(mime) else - return Promise.reject('RNFetchBlob.getContentIntent only supports Android.') + return Promise.reject('RNFetchBlob.android.getContentIntent only supports Android.') +} + +function addCompleteDownload(config) { + if(Platform.OS === 'android') + return RNFetchBlob.addCompleteDownload(config) + else + return Promise.reject('RNFetchBlob.android.addCompleteDownload only supports Android.') } export default { actionViewIntent, - getContentIntent + getContentIntent, + addCompleteDownload } diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java index def758e05..7b5e3bd11 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java @@ -882,13 +882,21 @@ static boolean isAsset(String path) { return false; } + /** + * Normalize the path, remove URI scheme (xxx://) so that we can handle it. + * @param path URI string. + * @return Normalized string + */ static String normalizePath(String path) { if(path == null) return null; - Uri uri = Uri.parse(path); - if(uri.getScheme() == null) { + if(!path.matches("\\w+\\:.*")) return path; + if(path.startsWith("file://")) { + return path.replace("file://", ""); } + + Uri uri = Uri.parse(path); if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { return path; } From 5b43136bebaba47cb329b7c65e2d4b4472dccedb Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Sun, 11 Jun 2017 16:27:08 +0800 Subject: [PATCH 15/54] Fix Fetch Replacement progress and cancel task issue #370 --- .../java/com/RNFetchBlob/RNFetchBlob.java | 28 +++++++++++++ polyfill/Fetch.js | 42 ++++++++++++------- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java index d046f1804..38a814ebc 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java @@ -1,6 +1,7 @@ package com.RNFetchBlob; import android.app.Activity; +import android.app.DownloadManager; import android.content.Intent; import android.net.Uri; @@ -341,4 +342,31 @@ public void getContentIntent(String mime, Promise promise) { } + @ReactMethod + public void addCompleteDownload (ReadableMap config, Promise promise) { + DownloadManager dm = (DownloadManager) RNFetchBlob.RCTContext.getSystemService(RNFetchBlob.RCTContext.DOWNLOAD_SERVICE); + String path = RNFetchBlobFS.normalizePath(config.getString("path")); + if(path == null) { + promise.reject("RNFetchblob.addCompleteDownload can not resolve URI:" + config.getString("path"), "RNFetchblob.addCompleteDownload can not resolve URI:" + path); + return; + } + try { + WritableMap stat = RNFetchBlobFS.statFile(path); + dm.addCompletedDownload( + config.hasKey("title") ? config.getString("title") : "", + config.hasKey("description") ? config.getString("description") : "", + true, + config.hasKey("mime") ? config.getString("mime") : null, + path, + Long.valueOf(stat.getString("size")), + config.hasKey("showNotification") && config.getBoolean("showNotification") + ); + promise.resolve(null); + } + catch(Exception ex) { + promise.reject("RNFetchblob.addCompleteDownload failed", ex.getStackTrace().toString()); + } + + } + } diff --git a/polyfill/Fetch.js b/polyfill/Fetch.js index 7907f8f17..7be52e084 100644 --- a/polyfill/Fetch.js +++ b/polyfill/Fetch.js @@ -57,24 +57,38 @@ class RNFetchBlobFetchPolyfill { // task is a progress reportable and cancellable Promise, however, // task.then is not, so we have to extend task.then with progress and // cancel function - let task = promise + let progressHandler, uploadHandler, cancelHandler + let statefulPromise = promise .then((body) => { - return RNFetchBlob.config(config) - .fetch(options.method, url, options.headers, body) + let task = RNFetchBlob.config(config) + .fetch(options.method, url, options.headers, body) + if(progressHandler) + task.progress(progressHandler) + if(uploadHandler) + task.uploadProgress(uploadHandler) + if(cancelHandler) + task.cancel() + return task.then((resp) => { + log.verbose('response', resp) + // release blob cache created when sending request + if(blobCache !== null && blobCache instanceof Blob) + blobCache.close() + return Promise.resolve(new RNFetchBlobFetchRepsonse(resp)) + }) }) - let statefulPromise = task.then((resp) => { - log.verbose('response', resp) - // release blob cache created when sending request - if(blobCache !== null && blobCache instanceof Blob) - blobCache.close() - return Promise.resolve(new RNFetchBlobFetchRepsonse(resp)) - }) - // extend task.then progress with report and cancelling functions - statefulPromise.cancel = task.cancel - statefulPromise.progress = task.progress - statefulPromise.uploadProgress = task.uploadProgress + statefulPromise.progress = (fn) => { + progressHandler = fn + } + statefulPromise.uploadProgress = (fn) => { + uploadHandler = fn + } + statefulPromise.cancel = () => { + cancelHandler = true + if(task.cancel) + task.cancel() + } return statefulPromise From d00bd9138a483d00c1fe45c3b0f02dda19ff4a62 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Wed, 14 Jun 2017 22:39:34 +0800 Subject: [PATCH 16/54] Fix missing Android import --- android/src/main/java/com/RNFetchBlob/RNFetchBlob.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java index 38a814ebc..19e1be435 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java @@ -16,6 +16,7 @@ import com.facebook.react.bridge.ReadableMap; // Cookies +import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.network.ForwardingCookieHandler; import com.facebook.react.modules.network.CookieJarContainer; import com.facebook.react.modules.network.OkHttpClientProvider; From 4073093f487ded69b197b88b107d6a95b141780c Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Wed, 14 Jun 2017 22:39:34 +0800 Subject: [PATCH 17/54] Fix missing Android import fix --- android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index f01e82efb..db213c1e8 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -192,7 +192,7 @@ else if(this.options.fileCache) try { // use trusty SSL socket if (this.options.trusty) { - clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient(); + clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient(client); } else { clientBuilder = client.newBuilder(); } From d61461bf2b3824a164201151484f304f607b5346 Mon Sep 17 00:00:00 2001 From: Jeremi Stadler Date: Sat, 17 Jun 2017 04:20:14 +0200 Subject: [PATCH 18/54] [Android] Update android gradle build version (#396) Change to jCenter because mavenCentral does not have the latest version of the package --- android/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 6a477b8b2..5fd627f22 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,10 +6,10 @@ repositories { buildscript { repositories { - mavenCentral() + jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.3.3' } } From ae083dd7f5c9020bded576fa0fcd59563ae924ca Mon Sep 17 00:00:00 2001 From: Jeremi Stadler Date: Mon, 19 Jun 2017 09:03:18 +0200 Subject: [PATCH 19/54] Downgrade android gradle build tools (#400) To support the default react-native gradle version https://github.com/wkh237/react-native-fetch-blob/pull/396 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 5fd627f22..919e64ac8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -9,7 +9,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:2.2.3' } } From 1d4e69cf20f0d86166b8f1346981874873b9847a Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Tue, 20 Jun 2017 23:12:58 +0800 Subject: [PATCH 20/54] Fix incorrect UTF-8 data conversion issue when the data contains special characters. --- android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java index 7b5e3bd11..1537bca71 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java @@ -250,10 +250,7 @@ public void readStream(String path, String encoding, int bufferSize, int tick, f CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); while ((cursor = fs.read(buffer)) != -1) { encoder.encode(ByteBuffer.wrap(buffer).asCharBuffer()); - String chunk = new String(buffer); - if(cursor != bufferSize) { - chunk = chunk.substring(0, cursor); - } + String chunk = new String(buffer, 0, cursor); emitStreamEvent(streamId, "data", chunk); if(tick > 0) SystemClock.sleep(tick); From 5e41837d72e67bff0744de15bb3a17865df5b886 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Fri, 23 Jun 2017 13:09:31 +0800 Subject: [PATCH 21/54] Create CONTRIBUTING.md --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..fb71468eb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +For developers who interested in making contribution to this project, please see [https://github.com/wkh237/react-native-fetch-blob-dev](https://github.com/wkh237/react-native-fetch-blob-dev) for more information. + +Please read the following rules before opening a PR : + +1. If the PR is offering a feature please make the PR to our "Feature Branch" 0.11.0 +2. Bug fix request to "Bug Fix Branch" 0.10.6 +3. Correct README.md can directly to master From 2c16a9488d4b883c4086ad68f61aada32c8a2095 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Mon, 26 Jun 2017 23:32:48 +0800 Subject: [PATCH 22/54] Fix IOS request timeout issue #368 by change default request setting to defaultSessionConfiguration, and make backgroundSessionConfigurationWithIdentifier optional --- ios/RNFetchBlobNetwork.m | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 85030b743..7be57fc59 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -82,6 +82,7 @@ @interface RNFetchBlobNetwork () NSMutableArray * redirects; ResponseFormat responseFormat; BOOL * followRedirect; + BOOL backgroundTask; } @end @@ -168,6 +169,8 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options self.expectedBytes = 0; self.receivedBytes = 0; self.options = options; + + backgroundTask = [options valueForKey:@"IOSBackgroundTask"] == nil ? NO : [[options valueForKey:@"IOSBackgroundTask"] boolValue]; followRedirect = [options valueForKey:@"followRedirect"] == nil ? YES : [[options valueForKey:@"followRedirect"] boolValue]; isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue]; redirects = [[NSMutableArray alloc] init]; @@ -192,13 +195,12 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options // the session trust any SSL certification NSURLSessionConfiguration *defaultConfigObject; - if(!followRedirect) - { - defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration]; - } - else + + defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration]; + + if(backgroundTask) { - NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId]; + defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId]; } // set request timeout @@ -247,14 +249,6 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; __block UIApplication * app = [UIApplication sharedApplication]; - // #115 handling task expired when application entering backgound for a long time - UIBackgroundTaskIdentifier tid = [app beginBackgroundTaskWithName:taskId expirationHandler:^{ - NSLog([NSString stringWithFormat:@"session %@ expired", taskId ]); - [expirationTable setObject:task forKey:taskId]; - // comment out this one as it might cause app crash #271 -// [app endBackgroundTask:tid]; - }]; - } // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled From 45b86cfde232c88f857b470f52b1cddc1ef9f9f5 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Mon, 26 Jun 2017 23:52:27 +0800 Subject: [PATCH 23/54] bump to 0.10.6 and update contributors --- CONTRIBUTORS.md | 6 ++++++ package.json | 2 +- react-native-fetch-blob.podspec | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 33ce358a2..5fa3a88c4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,7 +1,10 @@ 960px +Amerrnath Andreas Amsenius Andrew Jack Arthur Ouaki +Ben +Ben Hsieh Binur Konarbai Bronco Chris Sloey @@ -12,6 +15,8 @@ Erik Smartt Evgeniy Baraniuk Frank van der Hoek Guy Blank +Jacob Lauritzen +Jeremi Stadler Jon San Miguel Juan B. Rodriguez Kaishley @@ -24,6 +29,7 @@ Nick Pomfret Oliver Petter Hesselberg Reza Ghorbani +Simón Gómez Steve Liles Tim Suchanek Yonsh Lin diff --git a/package.json b/package.json index 9059164d8..a4524df60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-fetch-blob", - "version": "0.10.5", + "version": "0.10.6", "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.", "main": "index.js", "scripts": { diff --git a/react-native-fetch-blob.podspec b/react-native-fetch-blob.podspec index 2518b32d5..f702d287b 100644 --- a/react-native-fetch-blob.podspec +++ b/react-native-fetch-blob.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |s| s.name = "react-native-fetch-blob" - s.version = "0.10.3-beta.1" + s.version = "0.10.6" s.summary = "A project committed to make file acess and data transfer easier, effiecient for React Native developers." s.requires_arc = true s.license = 'MIT' s.homepage = 'n/a' s.authors = { "wkh237" => "xeiyan@gmail.com" } - s.source = { :git => "https://github.com/wkh237/react-native-fetch-blob", :tag => 'v0.10.3-beta.1'} + s.source = { :git => "https://github.com/wkh237/react-native-fetch-blob", :tag => 'v0.10.6'} s.source_files = 'ios/**/*.{h,m}' s.platform = :ios, "7.0" s.dependency 'React/Core' From 40efd14a5937e84cc573b2360643c24f46045183 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Mon, 26 Jun 2017 23:58:10 +0800 Subject: [PATCH 24/54] Update PULL_REQUEST_TEMPLATE --- .github/PULL_REQUEST_TEMPLATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index 6f184b4a1..edadaa9b6 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,5 +1,5 @@ Thank you for making a pull request ! Just a gentle reminder :) 1. If the PR is offering a feature please make the request to our "Feature Branch" 0.11.0 -2. Bug fix request to "Bug Fix Branch" 0.10.6 +2. Bug fix request to "Bug Fix Branch" 0.10.7 3. Correct README.md can directly to master From 08f8403dc43ad34320ee3038fa877a92b2b735c3 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Mon, 3 Jul 2017 08:48:44 +0800 Subject: [PATCH 25/54] Fixed a bug which causes XMLHttpRequest getting incorrect header when sending multiple requests in the same time --- polyfill/XMLHttpRequest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polyfill/XMLHttpRequest.js b/polyfill/XMLHttpRequest.js index 89171921f..42c987704 100644 --- a/polyfill/XMLHttpRequest.js +++ b/polyfill/XMLHttpRequest.js @@ -277,7 +277,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ _headerReceived = (e) => { log.debug('header received ', this._task.taskId, e) this.responseURL = this._url - if(e.state === "2") { + if(e.state === "2" && e.taskId === this._task.taskId) { this._responseHeaders = e.headers this._statusText = e.status this._status = Math.floor(e.status) From 8a75a9be2a613f6ce78dd6cd0e849437a9a8c41c Mon Sep 17 00:00:00 2001 From: Jon San Miguel Date: Mon, 3 Jul 2017 19:40:08 -0400 Subject: [PATCH 26/54] Set mime if set in addAndroidDownloads (#421) --- android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index db213c1e8..56fec29b5 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -148,6 +148,9 @@ public void run() { if(options.addAndroidDownloads.hasKey("path")) { req.setDestinationUri(Uri.parse("file://" + options.addAndroidDownloads.getString("path"))); } + if(options.addAndroidDownloads.hasKey("mime")) { + req.setMimeType(options.addAndroidDownloads.getString("mime")); + } // set headers ReadableMapKeySetIterator it = headers.keySetIterator(); while (it.hasNextKey()) { From ed2732a47ef5b4e7748b4c7632771d6f30a12c88 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Wed, 5 Jul 2017 14:14:16 +0800 Subject: [PATCH 27/54] Fix Download Manager bug when the file is not a multimedia #391 --- .../java/com/RNFetchBlob/RNFetchBlobReq.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index db213c1e8..31377650a 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -150,6 +150,14 @@ public void run() { } // set headers ReadableMapKeySetIterator it = headers.keySetIterator(); + // #391 Add MIME type to the request + if(options.addAndroidDownloads.hasKey("mime")) { + req.setMimeType(options.addAndroidDownloads.getString("mime")); + } + + if(options.addAndroidDownloads.hasKey("mediaScannable") && options.addAndroidDownloads.hasKey("mediaScannable") == true ) { + req.allowScanningByMediaScanner(); + } while (it.hasNextKey()) { String key = it.nextKey(); req.addRequestHeader(key, headers.getString(key)); @@ -636,16 +644,20 @@ public void onReceive(Context context, Intent intent) { return; } String contentUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); - if (contentUri != null) { + if ( contentUri != null && + options.addAndroidDownloads.hasKey("mime") && + options.addAndroidDownloads.getString("mime").contains("image")) { Uri uri = Uri.parse(contentUri); Cursor cursor = appCtx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null); - // use default destination of DownloadManager + + // use default destination of DownloadManager if (cursor != null) { cursor.moveToFirst(); filePath = cursor.getString(0); } } } + // When the file is not found in media content database, check if custom path exists if (options.addAndroidDownloads.hasKey("path")) { try { From 6bb7c65161c17e4961e28dee4b8ff0d359da03ff Mon Sep 17 00:00:00 2001 From: Jeff Hellman Date: Fri, 14 Jul 2017 01:15:53 -0600 Subject: [PATCH 28/54] Add support for TLS 1.2 when running Android 4 (#430) --- .../java/com/RNFetchBlob/RNFetchBlobReq.java | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index 34dc2887c..8a81a832e 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -7,10 +7,12 @@ import android.content.IntentFilter; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.util.Base64; import com.RNFetchBlob.Response.RNFetchBlobDefaultResp; import com.RNFetchBlob.Response.RNFetchBlobFileResp; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; @@ -21,6 +23,7 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.network.OkHttpClientProvider; +import com.facebook.react.modules.network.TLSSocketFactory; import java.io.File; import java.io.FileOutputStream; @@ -35,11 +38,14 @@ import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.util.ArrayList; +import java.util.List; import java.util.HashMap; + import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.ConnectionPool; +import okhttp3.ConnectionSpec; import okhttp3.Headers; import okhttp3.Interceptor; import okhttp3.MediaType; @@ -48,6 +54,8 @@ import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; +import okhttp3.TlsVersion; + public class RNFetchBlobReq extends BroadcastReceiver implements Runnable { @@ -366,9 +374,10 @@ public Response intercept(Chain chain) throws IOException { clientBuilder.retryOnConnectionFailure(false); clientBuilder.followRedirects(options.followRedirect); clientBuilder.followSslRedirects(options.followRedirect); + clientBuilder.retryOnConnectionFailure(true); + OkHttpClient client = enableTls12OnPreLollipop(clientBuilder).build(); - OkHttpClient client = clientBuilder.retryOnConnectionFailure(true).build(); Call call = client.newCall(req); taskTable.put(taskId, call); call.enqueue(new okhttp3.Callback() { @@ -683,5 +692,28 @@ public void onReceive(Context context, Intent intent) { } } + public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + try { + client.sslSocketFactory(new TLSSocketFactory()); + + ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .build(); + + List< ConnectionSpec > specs = new ArrayList < > (); + specs.add(cs); + specs.add(ConnectionSpec.COMPATIBLE_TLS); + specs.add(ConnectionSpec.CLEARTEXT); + + client.connectionSpecs(specs); + } catch (Exception exc) { + FLog.e("OkHttpClientProvider", "Error while enabling TLS 1.2", exc); + } + } + + return client; + } + } From b70a12455dd353517dc392a65d77f43e2f1938aa Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Fri, 14 Jul 2017 15:34:11 +0800 Subject: [PATCH 29/54] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b315848b9..7680fab86 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ A project committed to making file access and data transfer easier and more effi * [Android Media Scanner, and Download Manager Support](#user-content-android-media-scanner-and-download-manager-support) * [Self-Signed SSL Server](#user-content-self-signed-ssl-server) * [Transfer Encoding](#user-content-transfer-encoding) - * [RNFetchBlob as Fetch](#user-content-rnfetchblob-as-fetch) + * [Drop-in Fetch Replacement](#user-content-drop-in-fetch-replacement) * [File System](#user-content-file-system) * [File access](#user-content-file-access) * [File stream](#user-content-file-stream) * [Manage cached files](#user-content-cache-file-management) * [Web API Polyfills](#user-content-web-api-polyfills) -* [Performance Tips](#user-content-performance-tipsd) +* [Performance Tips](#user-content-performance-tips) * [API References](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API) * [Caveats](#user-content-caveats) * [Development](#user-content-development) @@ -452,11 +452,11 @@ task.cancel((err) => { ... }) ``` -### RNFetchBlob as Fetch +### Drop-in Fetch Replacement 0.9.0 -If you have existing code that uses `whatwg-fetch`(the official **fetch**), you don't have to change them after 0.9.0, just use fetch replacement. The difference between Official fetch and fetch replacement is, official fetch uses [whatwg-fetch](https://github.com/github/fetch) js library which wraps XMLHttpRequest polyfill under the hood it's a great library for web developers, however that does not play very well with RN. Our implementation is simply a wrapper of RNFetchBlob.fetch and fs APIs, so you can access all the features we provide. +If you have existing code that uses `whatwg-fetch`(the official **fetch**), it's not necessary to replace them with `RNFetchblob.fetch`, you can simply use our **Fetch Replacement**. The difference between Official them is official fetch uses [whatwg-fetch](https://github.com/github/fetch) which wraps XMLHttpRequest polyfill under the hood. It's a great library for web developers, but does not play very well with RN. Our implementation is simply a wrapper of our `fetch` and `fs` APIs, so you can access all the features we provided. [See document and examples](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetch-replacement) @@ -613,6 +613,8 @@ In `v0.5.0` we've added `writeStream` and `readStream`, which allows your app r When calling `readStream` method, you have to `open` the stream, and start to read data. When the file is large, consider using an appropriate `bufferSize` and `interval` to reduce the native event dispatching overhead (see [Performance Tips](#user-content-performance-tips)) +> The file stream event has a default throttle(10ms) and buffer size which preventing it cause too much overhead to main thread, yo can also [tweak these values](#user-content-performance-tips). + ```js let data = '' RNFetchBlob.fs.readStream( From 712c8a3a07044fb2ed9679eb5cfa010f553ad215 Mon Sep 17 00:00:00 2001 From: JoshB Date: Mon, 17 Jul 2017 05:22:33 -0700 Subject: [PATCH 30/54] [iOS] Fix for RNFetchBlob.writeChunk failing to write base64 encoded strings (#433) --- ios/RNFetchBlobFS.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ios/RNFetchBlobFS.m b/ios/RNFetchBlobFS.m index 9d4e00b0d..5e102d184 100644 --- a/ios/RNFetchBlobFS.m +++ b/ios/RNFetchBlobFS.m @@ -568,11 +568,11 @@ - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)enco // Write file chunk into an opened stream - (void)writeEncodeChunk:(NSString *) chunk { - NSMutableData * decodedData = [NSData alloc]; + NSData * decodedData = nil; if([[self.encoding lowercaseString] isEqualToString:@"base64"]) { - decodedData = [[NSData alloc] initWithBase64EncodedData:chunk options:0]; - } - if([[self.encoding lowercaseString] isEqualToString:@"utf8"]) { + decodedData = [[NSData alloc] initWithBase64EncodedString:chunk options: NSDataBase64DecodingIgnoreUnknownCharacters]; + } + else if([[self.encoding lowercaseString] isEqualToString:@"utf8"]) { decodedData = [chunk dataUsingEncoding:NSUTF8StringEncoding]; } else if([[self.encoding lowercaseString] isEqualToString:@"ascii"]) { @@ -793,4 +793,4 @@ + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest return; } -@end +@end \ No newline at end of file From d83d8007a8e8cdddda368d78bb622a53a99b8d3b Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Tue, 25 Jul 2017 09:57:53 +0800 Subject: [PATCH 31/54] Add missing API Blob.safeClose() --- polyfill/Blob.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/polyfill/Blob.js b/polyfill/Blob.js index 384ae8fd9..a91a897b6 100644 --- a/polyfill/Blob.js +++ b/polyfill/Blob.js @@ -130,6 +130,8 @@ export default class Blob extends EventTarget { // Blob data from file path else if(typeof data === 'string' && data.startsWith('RNFetchBlob-file://')) { log.verbose('create Blob cache file from file path', data) + // set this flag so that we know this blob is a wrapper of an existing file + this._isReference = true this._ref = String(data).replace('RNFetchBlob-file://', '') let orgPath = this._ref if(defer) @@ -282,6 +284,20 @@ export default class Blob extends EventTarget { }) } + safeClose() { + if(this._closed) + return Promise.reject('Blob has been released.) + this._closed = true + if(!this._isReference) { + return fs.unlink(this._ref).catch((err) => { + console.warn(err) + }) + } + else { + return Promise.resolve() + } + } + _invokeOnCreateEvent() { log.verbose('invoke create event', this._onCreated) this._blobCreated = true From d1d07d0922468ed812de5bc11d4ed4743589074b Mon Sep 17 00:00:00 2001 From: Hizoul Date: Wed, 2 Aug 2017 02:43:47 +0200 Subject: [PATCH 32/54] Fix Compilation Error in React Native 0.47.0 (#452) createJSModules was removedin React Native 0.47.0 and results in the attached Build Error. removing @Override fixes build (didn't remove function because I don't know if it should be kept for downard compatability?) ``` node_modules/react-native-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java:23: error: method does not override or implement a method from a supertype @Override ^ 1 error Incremental compilation of 1 classes completed in 0.219 secs. :react-native-fetch-blob:compileReleaseJavaWithJavac FAILED FAILURE: Build failed with an exception. ``` --- android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java index 74e0224a7..48aac7ac3 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java @@ -20,7 +20,6 @@ public List createNativeModules(ReactApplicationContext reactConte return modules; } - @Override public List> createJSModules() { return Collections.emptyList(); } From 6bde5167b435e23e334377aa3bb62e8cf7c7e188 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Wed, 2 Aug 2017 23:03:16 +0800 Subject: [PATCH 33/54] Bump to 0.10.7 --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a4524df60..0d88ec178 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-fetch-blob", - "version": "0.10.6", + "version": "0.10.7", "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.", "main": "index.js", "scripts": { @@ -8,7 +8,7 @@ }, "dependencies": { "base-64": "0.1.0", - "glob": "^7.0.6" + "glob": "7.0.6" }, "keywords": [ "react-native", @@ -35,4 +35,4 @@ "Ben ", "" ] -} \ No newline at end of file +} From 50c157345f13725855e6405f91a1f544575cfd4e Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Wed, 2 Aug 2017 23:13:10 +0800 Subject: [PATCH 34/54] Update PULL_REQUEST_TEMPLATE --- .github/PULL_REQUEST_TEMPLATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index edadaa9b6..cef8ed2af 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,5 +1,5 @@ Thank you for making a pull request ! Just a gentle reminder :) 1. If the PR is offering a feature please make the request to our "Feature Branch" 0.11.0 -2. Bug fix request to "Bug Fix Branch" 0.10.7 +2. Bug fix request to "Bug Fix Branch" 0.10.8 3. Correct README.md can directly to master From 55009f148288f6471e7e084db3881bbdd1d7d98a Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Thu, 3 Aug 2017 09:34:37 +0800 Subject: [PATCH 35/54] Correct unterminated string #455 --- polyfill/Blob.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polyfill/Blob.js b/polyfill/Blob.js index a91a897b6..53662a798 100644 --- a/polyfill/Blob.js +++ b/polyfill/Blob.js @@ -286,7 +286,7 @@ export default class Blob extends EventTarget { safeClose() { if(this._closed) - return Promise.reject('Blob has been released.) + return Promise.reject('Blob has been released.') this._closed = true if(!this._isReference) { return fs.unlink(this._ref).catch((err) => { From 9ab4ebb72e648156b2af60578de112022f856fb4 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Thu, 3 Aug 2017 09:37:08 +0800 Subject: [PATCH 36/54] bump to 0.10.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d88ec178..a93dba81d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-fetch-blob", - "version": "0.10.7", + "version": "0.10.8", "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.", "main": "index.js", "scripts": { From 1336555d79014a181ab79350434e016ea1bc8324 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Thu, 3 Aug 2017 09:42:16 +0800 Subject: [PATCH 37/54] Update PULL_REQUEST_TEMPLATE --- .github/PULL_REQUEST_TEMPLATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index cef8ed2af..b03e3e8e3 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,5 +1,5 @@ Thank you for making a pull request ! Just a gentle reminder :) 1. If the PR is offering a feature please make the request to our "Feature Branch" 0.11.0 -2. Bug fix request to "Bug Fix Branch" 0.10.8 +2. Bug fix request to "Bug Fix Branch" 0.10.9 3. Correct README.md can directly to master From 2aea0b58e99581000d7336697e70b3bba7670a50 Mon Sep 17 00:00:00 2001 From: grylance Date: Wed, 9 Aug 2017 16:12:33 +0100 Subject: [PATCH 38/54] Fix path argument in iOS excludeFromBackupKey (#473) --- ios.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios.js b/ios.js index 566b424e2..340ef04cf 100644 --- a/ios.js +++ b/ios.js @@ -43,7 +43,7 @@ function openDocument(path:string, scheme:string) { * @param {string} url URL of the resource, only file URL is supported * @return {Promise} */ -function excludeFromBackupKey(url:string) { +function excludeFromBackupKey(path:string) { return RNFetchBlob.excludeFromBackupKey('file://' + path); } From 821eeb0f9db1736009ee791c14298ef72144a825 Mon Sep 17 00:00:00 2001 From: Kota Furusawa Date: Thu, 24 Aug 2017 10:02:20 +0800 Subject: [PATCH 39/54] Fix README (#501) remove extra space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7680fab86..3c7d24a59 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ RNFetchBlob console.log('The file saved to ', res.path()) // Beware that when using a file path as Image source on Android, // you must prepend "file://"" before the file path - imageView = + imageView = }) ``` From 5db2a74b077685666ea70c94c4f92c237cc66aa8 Mon Sep 17 00:00:00 2001 From: Catalin Miron Date: Thu, 12 Oct 2017 22:55:43 +0300 Subject: [PATCH 40/54] Fix README.md Fix example links for Download and Multipart/form-data --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c7d24a59..7723d1ce7 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ A project committed to making file access and data transfer easier and more effi * [Installation](#user-content-installation) * [HTTP Data Transfer](#user-content-http-data-transfer) * [Regular Request](#user-content-regular-request) - * [Download file](#user-content-download-example--fetch-files-that-needs-authorization-token) + * [Download file](#download-example-fetch-files-that-need-authorization-token) * [Upload file](#user-content-upload-example--dropbox-files-upload-api) - * [Multipart/form upload](#user-content-multipartform-data-example--post-form-data-with-file-and-data) + * [Multipart/form upload](#multipartform-data-example-post-form-data-with-file-and-data) * [Upload/Download progress](#user-content-uploaddownload-progress) * [Cancel HTTP request](#user-content-cancel-request) * [Android Media Scanner, and Download Manager Support](#user-content-android-media-scanner-and-download-manager-support) From e39a41b8ebd9182692d5cd876f8111aba5060ce5 Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Wed, 6 Dec 2017 07:51:15 +0100 Subject: [PATCH 41/54] add "maintainer missing" --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3c7d24a59..dc5b3c5a6 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,11 @@ A project committed to making file access and data transfer easier and more efficient for React Native developers. > For Firebase Storage solution, please upgrade to the latest version for the best compatibility. +## RNFB MAINTAINER GONE MISSING (Help Wanted) + +wkh237's last Github activity was in September 2017. Since there is a lot of work on issues and PRs that is left, is there anybody who else who could provide a maintained fork in the meantime? Create an issue and address me (@lll000111) and I'll add a link to your maintained repo fork right here. + + ## Features - Transfer data directly from/to storage without BASE64 bridging - File API supports regular files, Asset files, and CameraRoll files From 52c477315d473289f1bede66a51088c366dad643 Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Wed, 6 Dec 2017 07:52:09 +0100 Subject: [PATCH 42/54] add link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dc5b3c5a6..c0a3124e3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A project committed to making file access and data transfer easier and more effi ## RNFB MAINTAINER GONE MISSING (Help Wanted) -wkh237's last Github activity was in September 2017. Since there is a lot of work on issues and PRs that is left, is there anybody who else who could provide a maintained fork in the meantime? Create an issue and address me (@lll000111) and I'll add a link to your maintained repo fork right here. +wkh237's last Github activity was in September 2017. Since there is a lot of work on issues and PRs that is left, is there anybody who else who could provide a maintained fork in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)) and I'll add a link to your maintained repo fork right here. ## Features From 455097db73ccdb9d56426a47ccd1db949809a5b3 Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Wed, 6 Dec 2017 07:54:44 +0100 Subject: [PATCH 43/54] more vertical space around the message --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c0a3124e3..20b08470b 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,13 @@ A project committed to making file access and data transfer easier and more efficient for React Native developers. > For Firebase Storage solution, please upgrade to the latest version for the best compatibility. +
+ ## RNFB MAINTAINER GONE MISSING (Help Wanted) wkh237's last Github activity was in September 2017. Since there is a lot of work on issues and PRs that is left, is there anybody who else who could provide a maintained fork in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)) and I'll add a link to your maintained repo fork right here. +
## Features - Transfer data directly from/to storage without BASE64 bridging From 51f8a19e0960987717ee521aa960a8737b99444b Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Wed, 6 Dec 2017 07:58:07 +0100 Subject: [PATCH 44/54] update the maintainer-missing message --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20b08470b..883f0fd13 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A project committed to making file access and data transfer easier and more effi ## RNFB MAINTAINER GONE MISSING (Help Wanted) -wkh237's last Github activity was in September 2017. Since there is a lot of work on issues and PRs that is left, is there anybody who else who could provide a maintained fork in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)) and I'll add a link to your maintained repo fork right here. +wkh237's last Github activity was in September 2017. Since there is a lot of work on issues and PRs that needs to be done, is there anybody who could provide a maintained fork of this package in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)), or send me an email, and I'll add a link to your maintained repo fork right here.
From 684f6df2838b41536a0e535966ffa5586866e79b Mon Sep 17 00:00:00 2001 From: nd-02110114 Date: Tue, 23 Jan 2018 15:05:05 +0900 Subject: [PATCH 45/54] add close tag --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 883f0fd13..edb5e170e 100644 --- a/README.md +++ b/README.md @@ -644,7 +644,7 @@ RNFetchBlob.fs.readStream( console.log('oops', err) }) ifstream.onEnd(() => { - }) }) ``` From 58df91fe8dd446dc3c3cb06474bd76d63bef13ff Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Tue, 20 Feb 2018 09:26:24 +0100 Subject: [PATCH 46/54] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 883f0fd13..e64e8f34c 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,14 @@ A project committed to making file access and data transfer easier and more efficient for React Native developers. > For Firebase Storage solution, please upgrade to the latest version for the best compatibility. +

## RNFB MAINTAINER GONE MISSING (Help Wanted) -wkh237's last Github activity was in September 2017. Since there is a lot of work on issues and PRs that needs to be done, is there anybody who could provide a maintained fork of this package in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)), or send me an email, and I'll add a link to your maintained repo fork right here. +wkh237's last Github activity was in September 2017, and he has not reacted to emails either. Since there is a lot of work on issues and PRs that needs to be done, is there anybody who could provide a maintained fork of this package in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)), or send me an email, and I'll add a link to your maintained repo fork right here. I don't have admin rights, I cannot accept PRs, I cannot given anyone else rights on this repo, somebody needs to fork it and become a full maintainer. +

## Features From e6480db3192b0c44721899bfc8d8966780e80026 Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Tue, 20 Feb 2018 09:26:57 +0100 Subject: [PATCH 47/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e64e8f34c..10ca0df76 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A project committed to making file access and data transfer easier and more effi ## RNFB MAINTAINER GONE MISSING (Help Wanted) -wkh237's last Github activity was in September 2017, and he has not reacted to emails either. Since there is a lot of work on issues and PRs that needs to be done, is there anybody who could provide a maintained fork of this package in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)), or send me an email, and I'll add a link to your maintained repo fork right here. I don't have admin rights, I cannot accept PRs, I cannot given anyone else rights on this repo, somebody needs to fork it and become a full maintainer. +wkh237's last Github activity was in September 2017, and he has not reacted to emails either. Since there is a lot of work on issues and PRs that needs to be done, is there anybody who could provide a maintained fork of this package in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)), or send me an email, and I'll add a link to your maintained repo fork right here. I don't have admin rights, I cannot accept PRs, I cannot give anyone else rights on this repo, somebody needs to fork it and become a full maintainer.

From 05768a26afb9f6c4a813c706324e7798dffe068b Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Tue, 20 Mar 2018 17:00:49 +0100 Subject: [PATCH 48/54] Update README.md --- README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 486897675..5a655c565 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,23 @@ -# react-native-fetch-blob -[![release](https://img.shields.io/github/release/wkh237/react-native-fetch-blob.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/releases) [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![](https://img.shields.io/badge/Wiki-Public-brightgreen.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/wiki) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]() - +## RNFB MAINTAINER GONE MISSING (Help Wanted) -A project committed to making file access and data transfer easier and more efficient for React Native developers. -> For Firebase Storage solution, please upgrade to the latest version for the best compatibility. +wkh237's last Github activity was in September 2017, and he has not reacted to emails either. Since there is a lot of work on issues and PRs that needs to be done, is there anybody who could provide a maintained fork of this package in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)), or send me an email, and I'll add a link to your maintained repo fork right here. I don't have admin rights, I cannot accept PRs, I cannot give anyone else rights on this repo, somebody needs to fork it and become a full maintainer. -

-## RNFB MAINTAINER GONE MISSING (Help Wanted) +## About Pull Requests -wkh237's last Github activity was in September 2017, and he has not reacted to emails either. Since there is a lot of work on issues and PRs that needs to be done, is there anybody who could provide a maintained fork of this package in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)), or send me an email, and I'll add a link to your maintained repo fork right here. I don't have admin rights, I cannot accept PRs, I cannot give anyone else rights on this repo, somebody needs to fork it and become a full maintainer. +I cannot (@lll000111) handle them, I'm not the owner. My own PRs are still in the queue waiting for review from the owner. I coulöd merge the PRs of others but not my own... but since I am in no position to do anything with this repo, I don't do mobile development, I don't even have a smart phone, I won't merge anything. **In any case though,** you should not send PRs for `master` but against the `0.10.9` branch. It already is quite different from `master`. So try that branch first if you want to patch, not `master`, even for your own fork.

+
+ +# react-native-fetch-blob +[![release](https://img.shields.io/github/release/wkh237/react-native-fetch-blob.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/releases) [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![](https://img.shields.io/badge/Wiki-Public-brightgreen.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/wiki) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]() + + +A project committed to making file access and data transfer easier and more efficient for React Native developers. +> For Firebase Storage solution, please upgrade to the latest version for the best compatibility. ## Features - Transfer data directly from/to storage without BASE64 bridging From c4c3d43636519106c94f9d1db6afca90f658243f Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Tue, 20 Mar 2018 17:03:43 +0100 Subject: [PATCH 49/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a655c565..177e968f9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ wkh237's last Github activity was in September 2017, and he has not reacted to e ## About Pull Requests -I cannot (@lll000111) handle them, I'm not the owner. My own PRs are still in the queue waiting for review from the owner. I coulöd merge the PRs of others but not my own... but since I am in no position to do anything with this repo, I don't do mobile development, I don't even have a smart phone, I won't merge anything. **In any case though,** you should not send PRs for `master` but against the `0.10.9` branch. It already is quite different from `master`. So try that branch first if you want to patch, not `master`, even for your own fork. +I (@lll000111) cannot handle them, I'm not the owner. My own PRs are still in the queue waiting for review from the owner. I could merge the PRs of others but not my own(!)... but since I am in no position to manage this repo — I don't do mobile development, I don't even have a smart phone — I won't merge anything. **In any case though,** you should not send PRs for `master` but against the `0.10.9` branch. It already is quite different from `master`. So try that branch first if you want to patch, not `master`, even for your own fork and your private patches.

From c582d0f1b684eb6265d86d0a7847522fba33a56e Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Thu, 22 Mar 2018 23:56:31 +0100 Subject: [PATCH 50/54] Update README.md --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 177e968f9..7ed5633e6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,17 @@ -## RNFB MAINTAINER GONE MISSING (Help Wanted) +## RNFB MAINTAINER GONE MISSING (New Repository Location) -wkh237's last Github activity was in September 2017, and he has not reacted to emails either. Since there is a lot of work on issues and PRs that needs to be done, is there anybody who could provide a maintained fork of this package in the meantime? Create an issue and address (ping) me ([@lll000111](https://github.com/lll000111)), or send me an email, and I'll add a link to your maintained repo fork right here. I don't have admin rights, I cannot accept PRs, I cannot give anyone else rights on this repo, somebody needs to fork it and become a full maintainer. +wkh237's last Github activity was in September 2017, and he has not reacted to emails either. This repository no longer is the main location of "react-native-fetch-blob". +**UPDATE 22 March, 2018** + +The owners of this fork have agreed to maintain this package: + +https://github.com/joltup/react-native-fetch-blob + +That means issues and PRs should be posted there. + + +

## About Pull Requests From be49c45933536a5dc0e405d0aaf46d1a9c873a7e Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Thu, 22 Mar 2018 23:59:54 +0100 Subject: [PATCH 51/54] Update README.md --- README.md | 807 +----------------------------------------------------- 1 file changed, 3 insertions(+), 804 deletions(-) diff --git a/README.md b/README.md index 7ed5633e6..31bb6c652 100644 --- a/README.md +++ b/README.md @@ -1,814 +1,13 @@ -## RNFB MAINTAINER GONE MISSING (New Repository Location) +# New Maintainers and Repository Location wkh237's last Github activity was in September 2017, and he has not reacted to emails either. This repository no longer is the main location of "react-native-fetch-blob". -**UPDATE 22 March, 2018** - The owners of this fork have agreed to maintain this package: https://github.com/joltup/react-native-fetch-blob That means issues and PRs should be posted there. +--- -
-
- -## About Pull Requests - -I (@lll000111) cannot handle them, I'm not the owner. My own PRs are still in the queue waiting for review from the owner. I could merge the PRs of others but not my own(!)... but since I am in no position to manage this repo — I don't do mobile development, I don't even have a smart phone — I won't merge anything. **In any case though,** you should not send PRs for `master` but against the `0.10.9` branch. It already is quite different from `master`. So try that branch first if you want to patch, not `master`, even for your own fork and your private patches. - -
-
-
- -# react-native-fetch-blob -[![release](https://img.shields.io/github/release/wkh237/react-native-fetch-blob.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/releases) [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![](https://img.shields.io/badge/Wiki-Public-brightgreen.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/wiki) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]() - - -A project committed to making file access and data transfer easier and more efficient for React Native developers. -> For Firebase Storage solution, please upgrade to the latest version for the best compatibility. - -## Features -- Transfer data directly from/to storage without BASE64 bridging -- File API supports regular files, Asset files, and CameraRoll files -- Native-to-native file manipulation API, reduce JS bridging performance loss -- File stream support for dealing with large file -- Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental) -- JSON stream supported base on [Oboe.js](https://github.com/jimhigson/oboe.js/) @jimhigson - -## TOC (visit [Wiki](https://github.com/wkh237/react-native-fetch-blob/wiki) to get the complete documentation) -* [About](#user-content-about) -* [Installation](#user-content-installation) -* [HTTP Data Transfer](#user-content-http-data-transfer) - * [Regular Request](#user-content-regular-request) - * [Download file](#download-example-fetch-files-that-need-authorization-token) - * [Upload file](#user-content-upload-example--dropbox-files-upload-api) - * [Multipart/form upload](#multipartform-data-example-post-form-data-with-file-and-data) - * [Upload/Download progress](#user-content-uploaddownload-progress) - * [Cancel HTTP request](#user-content-cancel-request) - * [Android Media Scanner, and Download Manager Support](#user-content-android-media-scanner-and-download-manager-support) - * [Self-Signed SSL Server](#user-content-self-signed-ssl-server) - * [Transfer Encoding](#user-content-transfer-encoding) - * [Drop-in Fetch Replacement](#user-content-drop-in-fetch-replacement) -* [File System](#user-content-file-system) - * [File access](#user-content-file-access) - * [File stream](#user-content-file-stream) - * [Manage cached files](#user-content-cache-file-management) -* [Web API Polyfills](#user-content-web-api-polyfills) -* [Performance Tips](#user-content-performance-tips) -* [API References](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API) -* [Caveats](#user-content-caveats) -* [Development](#user-content-development) - -## About - -This project was started in the cause of solving issue [facebook/react-native#854](https://github.com/facebook/react-native/issues/854), React Native's lacks of `Blob` implementation which results into problems when transferring binary data. - -It is committed to making file access and transfer easier and more efficient for React Native developers. We've implemented highly customizable filesystem and network module which plays well together. For example, developers can upload and download data directly from/to storage, which is more efficient, especially for large files. The file system supports file stream, so you don't have to worry about OOM problem when accessing large files. - -In `0.8.0` we introduced experimental Web API polyfills that make it possible to use browser-based libraries in React Native, such as, [FireBase JS SDK](https://github.com/wkh237/rn-firebase-storage-upload-sample) - - -## Installation - -Install package from npm - -```sh -npm install --save react-native-fetch-blob -``` - -Or if using CocoaPods, add the pod to your `Podfile` - -``` -pod 'react-native-fetch-blob', - :path => '../node_modules/react-native-fetch-blob' -``` - -After `0.10.3` you can install this package directly from Github - -```sh -# replace with any one of the branches -npm install --save github:wkh237/react-native-fetch-blob-package# -``` -**Automatically Link Native Modules** - -For 0.29.2+ projects, simply link native packages via the following command (note: rnpm has been merged into react-native) - -``` -react-native link -``` - -As for projects < 0.29 you need `rnpm` to link native packages - -```sh -rnpm link -``` - -Optionally, use the following command to add Android permissions to `AndroidManifest.xml` automatically - -```sh -RNFB_ANDROID_PERMISSIONS=true react-native link -``` - -pre 0.29 projects - -```sh -RNFB_ANDROID_PERMISSIONS=true rnpm link -``` - -The link script might not take effect if you have non-default project structure, please visit [the wiki](https://github.com/wkh237/react-native-fetch-blob/wiki/Manually-Link-Package) to link the package manually. - -**Grant Permission to External storage for Android 5.0 or lower** - -The mechanism for granting Android permissions has slightly different since Android 6.0 released, please refer to [Official Document](https://developer.android.com/training/permissions/requesting.html). - -If you're going to access external storage (say, SD card storage) for `Android 5.0` (or lower) devices, you might have to add the following line to `AndroidManifest.xml`. - -```diff - - - - -+ -+ - - ... - -``` - -Also, if you're going to use `Android Download Manager` you have to add this to `AndroidManifest.xml` - -```diff - - - -+ - -``` - -**Grant Access Permission for Android 6.0** - -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). - -## Usage - -ES6 - -The module uses ES6 style export statement, simply use `import` to load the module. - -```js -import RNFetchBlob from 'react-native-fetch-blob' -``` - -ES5 - -If you're using ES5 require statement to load the module, please add `default`. See [here](https://github.com/wkh237/react-native-fetch-blob/wiki/Trouble-Shooting#rnfetchblobfetch-is-not-a-function) for more detail. - -``` -var RNFetchBlob = require('react-native-fetch-blob').default -``` - -## HTTP Data Transfer - - -### Regular Request - -After `0.8.0` react-native-fetch-blob automatically decides how to send the body by checking its type and `Content-Type` in the header. The rule is described in the following diagram - - - -To sum up: - -- To send a form data, the `Content-Type` header does not matter. When the body is an `Array` we will set proper content type for you. -- To send binary data, you have two choices, use BASE64 encoded string or path points to a file contains the body. - - If the `Content-Type` containing substring`;BASE64` or `application/octet` the given body will be considered as a BASE64 encoded data which will be decoded to binary data as the request body. - - Otherwise, if a string starts with `RNFetchBlob-file://` (which can simply be done by `RNFetchBlob.wrap(PATH_TO_THE_FILE)`), it will try to find the data from the URI string after `RNFetchBlob-file://` and use it as the request body. -- To send the body as-is, simply use a `Content-Type` header not containing `;BASE64` or `application/octet`. - -> It is Worth to mentioning that the HTTP request uses cache by default, if you're going to disable it simply add a Cache-Control header `'Cache-Control' : 'no-store'` - -> After 0.9.4, we disabled `Chunked` transfer encoding by default, if you're going to use it, you should explicitly set header `Transfer-Encoding` to `Chunked`. - -### Download example: Fetch files that need authorization token - -Most simple way is download to memory and stored as BASE64 encoded string, this is handy when the response data is small. - -```js - -// send http request in a new thread (using native code) -RNFetchBlob.fetch('GET', 'http://www.example.com/images/img1.png', { - Authorization : 'Bearer access-token...', - // more headers .. - }) - // when response status code is 200 - .then((res) => { - // the conversion is done in native code - let base64Str = res.base64() - // the following conversions are done in js, it's SYNC - let text = res.text() - let json = res.json() - - }) - // Status code is not 200 - .catch((errorMessage, statusCode) => { - // error handling - }) -``` - -### Download to storage directly - -If the response data is large, that would be a bad idea to convert it into BASE64 string. A better solution is streaming the response directly into a file, simply add a `fileCache` option to config, and set it to `true`. This will make incoming response data stored in a temporary path **without** any file extension. - -**These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)** - -```js -RNFetchBlob - .config({ - // add this option that makes response data to be stored as a file, - // this is much more performant. - fileCache : true, - }) - .fetch('GET', 'http://www.example.com/file/example.zip', { - //some headers .. - }) - .then((res) => { - // the temp file path - console.log('The file saved to ', res.path()) - }) -``` - -**Set Temp File Extension** - -Sometimes you might need a file extension for some reason. For example, when using file path as the source of `Image` component, the path should end with something like .png or .jpg, you can do this by add `appendExt` option to `config`. - -```js -RNFetchBlob - .config({ - fileCache : true, - // by adding this option, the temp files will have a file extension - appendExt : 'png' - }) - .fetch('GET', 'http://www.example.com/file/example.zip', { - //some headers .. - }) - .then((res) => { - // the temp file path with file extension `png` - console.log('The file saved to ', res.path()) - // Beware that when using a file path as Image source on Android, - // you must prepend "file://"" before the file path - imageView = - }) -``` - -**Use Specific File Path** - -If you prefer a particular file path rather than randomly generated one, you can use `path` option. We've added [several constants](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs) in v0.5.0 which represents commonly used directories. - -```js -let dirs = RNFetchBlob.fs.dirs -RNFetchBlob -.config({ - // response data will be saved to this path if it has access right. - path : dirs.DocumentDir + '/path-to-file.anything' -}) -.fetch('GET', 'http://www.example.com/file/example.zip', { - //some headers .. -}) -.then((res) => { - // the path should be dirs.DocumentDir + 'path-to-file.anything' - console.log('The file saved to ', res.path()) -}) -``` - -**These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)** - -#### Upload example : Dropbox [files-upload](https://www.dropbox.com/developers/documentation/http/documentation#files-upload) API - -`react-native-fetch-blob` 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. - -```js - -RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', { - Authorization : "Bearer access-token...", - 'Dropbox-API-Arg': JSON.stringify({ - path : '/img-from-react-native.png', - mode : 'add', - autorename : true, - mute : false - }), - 'Content-Type' : 'application/octet-stream', - // here's the body you're going to send, should be a BASE64 encoded string - // (you can use "base64"(refer to the library 'mathiasbynens/base64') APIs to make one). - // The data will be converted to "byte array"(say, blob) before request sent. - }, base64ImageString) - .then((res) => { - console.log(res.text()) - }) - .catch((err) => { - // error handling .. - }) -``` - -### Upload a file from storage - -If you're going to use a `file` as request body, just wrap the path with `wrap` API. - -```js -RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', { - // dropbox upload headers - Authorization : "Bearer access-token...", - 'Dropbox-API-Arg': JSON.stringify({ - path : '/img-from-react-native.png', - mode : 'add', - autorename : true, - mute : false - }), - 'Content-Type' : 'application/octet-stream', - // Change BASE64 encoded data to a file path with prefix `RNFetchBlob-file://`. - // Or simply wrap the file path with RNFetchBlob.wrap(). - }, RNFetchBlob.wrap(PATH_TO_THE_FILE)) - .then((res) => { - console.log(res.text()) - }) - .catch((err) => { - // error handling .. - }) -``` - -### Multipart/form-data example: Post form data with file and data - -In `version >= 0.3.0` you can also post files with form data, just put an array in `body`, with elements have property `name`, `data`, and `filename`(optional). - -Elements have property `filename` will be transformed into binary format, otherwise, it turns into utf8 string. - -```js - - RNFetchBlob.fetch('POST', 'http://www.example.com/upload-form', { - Authorization : "Bearer access-token", - otherHeader : "foo", - 'Content-Type' : 'multipart/form-data', - }, [ - // element with property `filename` will be transformed into `file` in form data - { name : 'avatar', filename : 'avatar.png', data: binaryDataInBase64}, - // custom content type - { name : 'avatar-png', filename : 'avatar-png.png', type:'image/png', data: binaryDataInBase64}, - // part file from storage - { name : 'avatar-foo', filename : 'avatar-foo.png', type:'image/foo', data: RNFetchBlob.wrap(path_to_a_file)}, - // elements without property `filename` will be sent as plain text - { name : 'name', data : 'user'}, - { name : 'info', data : JSON.stringify({ - mail : 'example@example.com', - tel : '12345678' - })}, - ]).then((resp) => { - // ... - }).catch((err) => { - // ... - }) -``` - -What if you want to append a file to form data? Just like [upload a file from storage](#user-content-upload-a-file-from-storage) example, wrap `data` by `wrap` API (this feature is only available for `version >= v0.5.0`). On version >= `0.6.2`, it is possible to set custom MIME type when appending a file to form data. But keep in mind when the file is large it's likely to crash your app. Please consider use other strategy (see [#94](https://github.com/wkh237/react-native-fetch-blob/issues/94)). - -```js - - RNFetchBlob.fetch('POST', 'http://www.example.com/upload-form', { - Authorization : "Bearer access-token", - otherHeader : "foo", - // this is required, otherwise it won't be process as a multipart/form-data request - 'Content-Type' : 'multipart/form-data', - }, [ - // append field data from file path - { - name : 'avatar', - filename : 'avatar.png', - // Change BASE64 encoded data to a file path with prefix `RNFetchBlob-file://`. - // Or simply wrap the file path with RNFetchBlob.wrap(). - data: RNFetchBlob.wrap(PATH_TO_THE_FILE) - }, - { - name : 'ringtone', - filename : 'ring.mp3', - // use custom MIME type - type : 'application/mp3', - // upload a file from asset is also possible in version >= 0.6.2 - data : RNFetchBlob.wrap(RNFetchBlob.fs.asset('default-ringtone.mp3')) - } - // elements without property `filename` will be sent as plain text - { name : 'name', data : 'user'}, - { name : 'info', data : JSON.stringify({ - mail : 'example@example.com', - tel : '12345678' - })}, - ]).then((resp) => { - // ... - }).catch((err) => { - // ... - }) -``` - -### Upload/Download progress - -In `version >= 0.4.2` it is possible to know the upload/download progress. After `0.7.0` IOS and Android upload progress are also supported. - -```js - RNFetchBlob.fetch('POST', 'http://www.example.com/upload', { - //... some headers, - 'Content-Type' : 'octet-stream' - }, base64DataString) - // listen to upload progress event - .uploadProgress((written, total) => { - console.log('uploaded', written / total) - }) - // listen to download progress event - .progress((received, total) => { - console.log('progress', received / total) - }) - .then((resp) => { - // ... - }) - .catch((err) => { - // ... - }) -``` - -In `0.9.6`, you can specify an object as the first argument which contains `count` and `interval`, to the frequency of progress event (this will be done in the native context a reduce RCT bridge overhead). Notice that `count` argument will not work if the server does not provide response content length. - - -```js - RNFetchBlob.fetch('POST', 'http://www.example.com/upload', { - //... some headers, - 'Content-Type' : 'octet-stream' - }, base64DataString) - // listen to upload progress event, emit every 250ms - .uploadProgress({ interval : 250 },(written, total) => { - console.log('uploaded', written / total) - }) - // listen to download progress event, every 10% - .progress({ count : 10 }, (received, total) => { - console.log('progress', received / total) - }) - .then((resp) => { - // ... - }) - .catch((err) => { - // ... - }) -``` - -### Cancel Request - -After `0.7.0` it is possible to cancel an HTTP request. Upon cancellation, it throws a promise rejection, be sure to catch it. - -```js -let task = RNFetchBlob.fetch('GET', 'http://example.com/file/1') - -task.then(() => { ... }) - // handle request cancelled rejection - .catch((err) => { - console.log(err) - }) -// cancel the request, the callback function is optional -task.cancel((err) => { ... }) - -``` - -### Drop-in Fetch Replacement - -0.9.0 - -If you have existing code that uses `whatwg-fetch`(the official **fetch**), it's not necessary to replace them with `RNFetchblob.fetch`, you can simply use our **Fetch Replacement**. The difference between Official them is official fetch uses [whatwg-fetch](https://github.com/github/fetch) which wraps XMLHttpRequest polyfill under the hood. It's a great library for web developers, but does not play very well with RN. Our implementation is simply a wrapper of our `fetch` and `fs` APIs, so you can access all the features we provided. - -[See document and examples](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetch-replacement) - -### Android Media Scanner, and Download Manager Support - -If you want to make a file in `External Storage` becomes visible in Picture, Downloads, or other built-in apps, you will have to use `Media Scanner` or `Download Manager`. - -**Media Scanner** - -Media scanner scans the file and categorizes by given MIME type, if MIME type not specified, it will try to resolve the file using its file extension. - -```js - -RNFetchBlob - .config({ - // DCIMDir is in external storage - path : dirs.DCIMDir + '/music.mp3' - }) - .fetch('GET', 'http://example.com/music.mp3') - .then((res) => RNFetchBlob.fs.scanFile([ { path : res.path(), mime : 'audio/mpeg' } ])) - .then(() => { - // scan file success - }) - .catch((err) => { - // scan file error - }) -``` - -**Download Manager** - -When downloading large files on Android it is recommended to use `Download Manager`, it supports a lot of native features like the progress bar, and notification, also the download task will be handled by OS, and more efficient. - - - -When using DownloadManager, `fileCache` and `path` properties in `config` will not take effect, because Android DownloadManager can only store files to external storage, also notice that Download Manager can only support `GET` method, which means the request body will be ignored. - -When download complete, DownloadManager will generate a file path so that you can deal with it. - -```js -RNFetchBlob - .config({ - addAndroidDownloads : { - useDownloadManager : true, // <-- this is the only thing required - // Optional, override notification setting (default to true) - notification : false, - // Optional, but recommended since android DownloadManager will fail when - // the url does not contains a file extension, by default the mime type will be text/plain - mime : 'text/plain', - description : 'File downloaded by download manager.' - } - }) - .fetch('GET', 'http://example.com/file/somefile') - .then((resp) => { - // the path of downloaded file - resp.path() - }) -``` - -Your app might not have right to remove/change the file created by Download Manager, therefore you might need to [set custom location to the download task](https://github.com/wkh237/react-native-fetch-blob/issues/236). - -**Download Notification and Visibility in Download App (Android Only)** - - - - - -If you need to display a notification upon the file is downloaded to storage (as the above) or make the downloaded file visible in "Downloads" app. You have to add some options to `config`. - -```js -RNFetchBlob.config({ - fileCache : true, - // android only options, these options be a no-op on IOS - addAndroidDownloads : { - // Show notification when response data transmitted - notification : true, - // Title of download notification - title : 'Great ! Download Success ! :O ', - // File description (not notification description) - description : 'An image file.', - mime : 'image/png', - // Make the file scannable by media scanner - mediaScannable : true, - } -}) -.fetch('GET', 'http://example.com/image1.png') -.then(...) -``` - -**Open Downloaded File with Intent** - -This is a new feature added in `0.9.0` if you're going to open a file path using official [Linking](https://facebook.github.io/react-native/docs/linking.html) API that might not work as expected, also, if you're going to install an APK in `Downloads` app, that will not function too. As an alternative, you can try `actionViewIntent` API, which will send an ACTION_VIEW intent for you which uses the given `MIME` type. - -Download and install an APK programmatically - -```js - -const android = RNFetchBlob.android - -RNFetchBlob.config({ - addAndroidDownloads : { - useDownloadManager : true, - title : 'awesome.apk', - description : 'An APK that will be installed', - mime : 'application/vnd.android.package-archive', - mediaScannable : true, - notification : true, - } - }) - .fetch('GET', `http://www.example.com/awesome.apk`) - .then((res) => { - android.actionViewIntent(res.path(), 'application/vnd.android.package-archive') - }) -``` - -Or show an image in image viewer - -```js - android.actionViewIntent(PATH_OF_IMG, 'image/png') -``` - -## File System - -### File Access - -File access APIs were made when developing `v0.5.0`, which helping us write tests, and was not planned to be a part of this module. However, we realized that it's hard to find a great solution to manage cached files, everyone who uses this module may need these APIs for their cases. - -Before start using file APIs, we recommend read [Differences between File Source](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#differences-between-file-source) first. - -File Access APIs -- [asset (0.6.2)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#assetfilenamestringstring) -- [dirs](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs) -- [createFile](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#createfilepath-data-encodingpromise) -- [writeFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise) -- [appendFile (0.6.0) ](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#appendfilepathstring-contentstring--array-encodingstringpromise) -- [readFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readfilepath-encodingpromise) -- [readStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readstreampath-encoding-buffersizepromise) -- [writeStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writestreampathstring-encodingstring-appendbooleanpromise) -- [unlink](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#unlinkpathstringpromise) -- [mkdir](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#mkdirpathstringpromise) -- [ls](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#lspathstringpromise) -- [mv](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#mvfromstring-tostringpromise) -- [cp](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#cpsrcstring-deststringpromise) -- [exists](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#existspathstringpromise) -- [isDir](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#isdirpathstringpromise) -- [stat](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#statpathstringpromise) -- [lstat](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#lstatpathstringpromise) -- [scanFile (Android only)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#scanfilepathstringpromise-androi-only) - -See [File API](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API) for more information - -### File Stream - -In `v0.5.0` we've added `writeStream` and `readStream`, which allows your app read/write data from the file path. This API creates a file stream, rather than convert entire data into BASE64 encoded string. It's handy when processing **large files**. - -When calling `readStream` method, you have to `open` the stream, and start to read data. When the file is large, consider using an appropriate `bufferSize` and `interval` to reduce the native event dispatching overhead (see [Performance Tips](#user-content-performance-tips)) - -> The file stream event has a default throttle(10ms) and buffer size which preventing it cause too much overhead to main thread, yo can also [tweak these values](#user-content-performance-tips). - -```js -let data = '' -RNFetchBlob.fs.readStream( - // file path - PATH_TO_THE_FILE, - // encoding, should be one of `base64`, `utf8`, `ascii` - 'base64', - // (optional) buffer size, default to 4096 (4095 for BASE64 encoded data) - // when reading file in BASE64 encoding, buffer size must be multiples of 3. - 4095) -.then((ifstream) => { - ifstream.open() - ifstream.onData((chunk) => { - // when encoding is `ascii`, chunk will be an array contains numbers - // otherwise it will be a string - data += chunk - }) - ifstream.onError((err) => { - console.log('oops', err) - }) - ifstream.onEnd(() => { - - }) -}) -``` - -When using `writeStream`, the stream object becomes writable, and you can then perform operations like `write` and `close`. - -```js -RNFetchBlob.fs.writeStream( - PATH_TO_FILE, - // encoding, should be one of `base64`, `utf8`, `ascii` - 'utf8', - // should data append to existing content ? - true) -.then((ofstream) => { - ofstream.write('foo') - ofstream.write('bar') - ofstream.close() -}) - -``` - -### Cache File Management - -When using `fileCache` or `path` options along with `fetch` API, response data will automatically store into the file system. The files will **NOT** removed unless you `unlink` it. There're several ways to remove the files - -```js - - // remove file using RNFetchblobResponse.flush() object method - RNFetchblob.config({ - fileCache : true - }) - .fetch('GET', 'http://example.com/download/file') - .then((res) => { - // remove cached file from storage - res.flush() - }) - - // remove file by specifying a path - RNFetchBlob.fs.unlink('some-file-path').then(() => { - // ... - }) - -``` - -You can also group requests by using `session` API and use `dispose` to remove them all when needed. - -```js - - RNFetchblob.config({ - fileCache : true - }) - .fetch('GET', 'http://example.com/download/file') - .then((res) => { - // set session of a response - res.session('foo') - }) - - RNFetchblob.config({ - // you can also set session beforehand - session : 'foo' - fileCache : true - }) - .fetch('GET', 'http://example.com/download/file') - .then((res) => { - // ... - }) - - // or put an existing file path to the session - RNFetchBlob.session('foo').add('some-file-path') - // remove a file path from the session - RNFetchBlob.session('foo').remove('some-file-path') - // list paths of a session - RNFetchBlob.session('foo').list() - // remove all files in a session - RNFetchBlob.session('foo').dispose().then(() => { ... }) - -``` - -### Transfer Encoding - -After `0.9.4`, the `Chunked` transfer encoding is disabled by default due to some service provider may not support chunked transfer. To enable it, set `Transfer-Encoding` header to `Chunked`. - -```js -RNFetchBlob.fetch('POST', 'http://example.com/upload', { 'Transfer-Encoding' : 'Chunked' }, bodyData) -``` - -### Self-Signed SSL Server - -By default, react-native-fetch-blob does NOT allow connection to unknown certification provider since it's dangerous. To connect a server with self-signed certification, you need to add `trusty` to `config` explicitly. This function is available for version >= `0.5.3` - -```js -RNFetchBlob.config({ - trusty : true -}) -.then('GET', 'https://mysite.com') -.then((resp) => { - // ... -}) -``` - -## Web API Polyfills - -After `0.8.0` we've made some [Web API polyfills](https://github.com/wkh237/react-native-fetch-blob/wiki/Web-API-Polyfills-(experimental)) that makes some browser-based library available in RN. - -- Blob -- XMLHttpRequest (Use our implementation if you're going to use it with Blob) - -Here's a [sample app](https://github.com/wkh237/rn-firebase-storage-upload-sample) that uses polyfills to upload files to FireBase. - -## Performance Tips - -**Read Stream and Progress Event Overhead** - -If the process seems to block JS thread when file is large when reading data via `fs.readStream`. It might because the default buffer size is quite small (4kb) which result in a lot of events triggered from JS thread. Try to increase the buffer size (for example 100kb = 102400) and set a larger interval (available for 0.9.4+, the default value is 10ms) to limit the frequency. - -**Reduce RCT Bridge and BASE64 Overhead** - -React Native connects JS and Native context by passing JSON around React Native bridge, and there will be an overhead to convert data before they sent to each side. When data is large, this will be quite a performance impact to your app. It's recommended to use file storage instead of BASE64 if possible.The following chart shows how much faster when loading data from storage than BASE64 encoded string on iPhone 6. - - - -**ASCII Encoding has /terrible Performance** - -Due to the [lack of typed array implementation in JavascriptCore, and limitation of React Native structure](https://github.com/facebook/react-native/issues/1424), to convert data to JS byte array spends lot of time. Use it only when needed, the following chart shows how much time it takes when reading a file with different encoding. - - - -**Concat and Replacing Files** - -If you're going to concatenate files, you don't have to read the data to JS context anymore! In `0.8.0` we introduced new encoding `uri` for writeFile and appendFile API, which make it possible to handle the whole process in native. - - - -## Caveats - -* This library does not urlencode unicode characters in URL automatically, see [#146](https://github.com/wkh237/react-native-fetch-blob/issues/146). -* When you create a `Blob` , from an existing file, the file **WILL BE REMOVED** if you `close` the blob. -* If you replaced `window.XMLHttpRequest` for some reason (e.g. make Firebase SDK work), it will also affect how official `fetch` works (basically it should work just fine). -* When file stream and upload/download progress event slow down your app, consider an upgrade to `0.9.6+`, use [additional arguments](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetchprogressconfig-eventlistenerpromisernfetchblobresponse) to limit its frequency. -* When passing a file path to the library, remove `file://` prefix. - -when you got a problem, have a look at [Trouble Shooting](https://github.com/wkh237/react-native-fetch-blob/wiki/Trouble-Shooting) or [issues labeled Trouble Shooting](https://github.com/wkh237/react-native-fetch-blob/issues?utf8=✓&q=label:%22trouble%20shooting%22%20), there'd be some helpful information. - -## Changes - -See [release notes](https://github.com/wkh237/react-native-fetch-blob/releases) - -### Development - -If you're interested in hacking this module, check our [development guide](https://github.com/wkh237/react-native-fetch-blob/wiki/Home), there might be some helpful information. -Please feel free to make a PR or file an issue. +Posted by https://github.com/lll000111/ ("Collaborator" in this repo) on 22 March, 2018 From 1fb974fc531fe93f7641b2f62a077bf6ed12a28f Mon Sep 17 00:00:00 2001 From: KittenWithHerbs Date: Fri, 23 Mar 2018 00:00:22 +0100 Subject: [PATCH 52/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 31bb6c652..bf0e3c458 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,4 @@ That means issues and PRs should be posted there. --- -Posted by https://github.com/lll000111/ ("Collaborator" in this repo) on 22 March, 2018 +Posted by [@lll000111](https://github.com/lll000111/) ("Collaborator" in this repo) on 22 March, 2018 From 80a0da3c1a4cb54fb0461e2222112c841c14f625 Mon Sep 17 00:00:00 2001 From: Mohd Mushfique Date: Thu, 11 Oct 2018 13:10:32 +0530 Subject: [PATCH 53/54] Update README.md Updating the correct url. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf0e3c458..a2a7e330e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ wkh237's last Github activity was in September 2017, and he has not reacted to e The owners of this fork have agreed to maintain this package: -https://github.com/joltup/react-native-fetch-blob +https://github.com/joltup/rn-fetch-blob That means issues and PRs should be posted there. From 57375d8bbed7c247c331d20b99ff39526a206edd Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Sat, 16 Mar 2019 16:25:38 +0800 Subject: [PATCH 54/54] Update README.md --- README.md | 793 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 791 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a2a7e330e..ab235d606 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # New Maintainers and Repository Location -wkh237's last Github activity was in September 2017, and he has not reacted to emails either. This repository no longer is the main location of "react-native-fetch-blob". +This repository no longer is the main location of "react-native-fetch-blob". The owners of this fork have agreed to maintain this package: @@ -10,4 +10,793 @@ That means issues and PRs should be posted there. --- -Posted by [@lll000111](https://github.com/lll000111/) ("Collaborator" in this repo) on 22 March, 2018 +# react-native-fetch-blob +[![release](https://img.shields.io/github/release/wkh237/react-native-fetch-blob.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/releases) [![npm](https://img.shields.io/npm/v/react-native-fetch-blob.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fetch-blob) ![](https://img.shields.io/badge/PR-Welcome-brightgreen.svg?style=flat-square) [![](https://img.shields.io/badge/Wiki-Public-brightgreen.svg?style=flat-square)](https://github.com/wkh237/react-native-fetch-blob/wiki) [![npm](https://img.shields.io/npm/l/react-native-fetch-blob.svg?maxAge=2592000&style=flat-square)]() + + +A project committed to making file access and data transfer easier and more efficient for React Native developers. +> For Firebase Storage solution, please upgrade to the latest version for the best compatibility. + +## Features +- Transfer data directly from/to storage without BASE64 bridging +- File API supports regular files, Asset files, and CameraRoll files +- Native-to-native file manipulation API, reduce JS bridging performance loss +- File stream support for dealing with large file +- Blob, File, XMLHttpRequest polyfills that make browser-based library available in RN (experimental) +- JSON stream supported base on [Oboe.js](https://github.com/jimhigson/oboe.js/) @jimhigson + +## TOC (visit [Wiki](https://github.com/wkh237/react-native-fetch-blob/wiki) to get the complete documentation) +* [About](#user-content-about) +* [Installation](#user-content-installation) +* [HTTP Data Transfer](#user-content-http-data-transfer) + * [Regular Request](#user-content-regular-request) + * [Download file](#user-content-download-example--fetch-files-that-needs-authorization-token) + * [Upload file](#user-content-upload-example--dropbox-files-upload-api) + * [Multipart/form upload](#user-content-multipartform-data-example--post-form-data-with-file-and-data) + * [Upload/Download progress](#user-content-uploaddownload-progress) + * [Cancel HTTP request](#user-content-cancel-request) + * [Android Media Scanner, and Download Manager Support](#user-content-android-media-scanner-and-download-manager-support) + * [Self-Signed SSL Server](#user-content-self-signed-ssl-server) + * [Transfer Encoding](#user-content-transfer-encoding) + * [Drop-in Fetch Replacement](#user-content-drop-in-fetch-replacement) +* [File System](#user-content-file-system) + * [File access](#user-content-file-access) + * [File stream](#user-content-file-stream) + * [Manage cached files](#user-content-cache-file-management) +* [Web API Polyfills](#user-content-web-api-polyfills) +* [Performance Tips](#user-content-performance-tips) +* [API References](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API) +* [Caveats](#user-content-caveats) +* [Development](#user-content-development) + +## About + +This project was started in the cause of solving issue [facebook/react-native#854](https://github.com/facebook/react-native/issues/854), React Native's lacks of `Blob` implementation which results into problems when transferring binary data. + +It is committed to making file access and transfer easier and more efficient for React Native developers. We've implemented highly customizable filesystem and network module which plays well together. For example, developers can upload and download data directly from/to storage, which is more efficient, especially for large files. The file system supports file stream, so you don't have to worry about OOM problem when accessing large files. + +In `0.8.0` we introduced experimental Web API polyfills that make it possible to use browser-based libraries in React Native, such as, [FireBase JS SDK](https://github.com/wkh237/rn-firebase-storage-upload-sample) + + +## Installation + +Install package from npm + +```sh +npm install --save react-native-fetch-blob +``` + +Or if using CocoaPods, add the pod to your `Podfile` + +``` +pod 'react-native-fetch-blob', + :path => '../node_modules/react-native-fetch-blob' +``` + +After `0.10.3` you can install this package directly from Github + +```sh +# replace with any one of the branches +npm install --save github:wkh237/react-native-fetch-blob-package# +``` +**Automatically Link Native Modules** + +For 0.29.2+ projects, simply link native packages via the following command (note: rnpm has been merged into react-native) + +``` +react-native link +``` + +As for projects < 0.29 you need `rnpm` to link native packages + +```sh +rnpm link +``` + +Optionally, use the following command to add Android permissions to `AndroidManifest.xml` automatically + +```sh +RNFB_ANDROID_PERMISSIONS=true react-native link +``` + +pre 0.29 projects + +```sh +RNFB_ANDROID_PERMISSIONS=true rnpm link +``` + +The link script might not take effect if you have non-default project structure, please visit [the wiki](https://github.com/wkh237/react-native-fetch-blob/wiki/Manually-Link-Package) to link the package manually. + +**Grant Permission to External storage for Android 5.0 or lower** + +The mechanism for granting Android permissions has slightly different since Android 6.0 released, please refer to [Official Document](https://developer.android.com/training/permissions/requesting.html). + +If you're going to access external storage (say, SD card storage) for `Android 5.0` (or lower) devices, you might have to add the following line to `AndroidManifest.xml`. + +```diff + + + + ++ ++ + + ... + +``` + +Also, if you're going to use `Android Download Manager` you have to add this to `AndroidManifest.xml` + +```diff + + + ++ + +``` + +**Grant Access Permission for Android 6.0** + +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). + +## Usage + +ES6 + +The module uses ES6 style export statement, simply use `import` to load the module. + +```js +import RNFetchBlob from 'react-native-fetch-blob' +``` + +ES5 + +If you're using ES5 require statement to load the module, please add `default`. See [here](https://github.com/wkh237/react-native-fetch-blob/wiki/Trouble-Shooting#rnfetchblobfetch-is-not-a-function) for more detail. + +``` +var RNFetchBlob = require('react-native-fetch-blob').default +``` + +## HTTP Data Transfer + + +### Regular Request + +After `0.8.0` react-native-fetch-blob automatically decides how to send the body by checking its type and `Content-Type` in the header. The rule is described in the following diagram + + + +To sum up: + +- To send a form data, the `Content-Type` header does not matter. When the body is an `Array` we will set proper content type for you. +- To send binary data, you have two choices, use BASE64 encoded string or path points to a file contains the body. + - If the `Content-Type` containing substring`;BASE64` or `application/octet` the given body will be considered as a BASE64 encoded data which will be decoded to binary data as the request body. + - Otherwise, if a string starts with `RNFetchBlob-file://` (which can simply be done by `RNFetchBlob.wrap(PATH_TO_THE_FILE)`), it will try to find the data from the URI string after `RNFetchBlob-file://` and use it as the request body. +- To send the body as-is, simply use a `Content-Type` header not containing `;BASE64` or `application/octet`. + +> It is Worth to mentioning that the HTTP request uses cache by default, if you're going to disable it simply add a Cache-Control header `'Cache-Control' : 'no-store'` + +> After 0.9.4, we disabled `Chunked` transfer encoding by default, if you're going to use it, you should explicitly set header `Transfer-Encoding` to `Chunked`. + +### Download example: Fetch files that need authorization token + +Most simple way is download to memory and stored as BASE64 encoded string, this is handy when the response data is small. + +```js + +// send http request in a new thread (using native code) +RNFetchBlob.fetch('GET', 'http://www.example.com/images/img1.png', { + Authorization : 'Bearer access-token...', + // more headers .. + }) + // when response status code is 200 + .then((res) => { + // the conversion is done in native code + let base64Str = res.base64() + // the following conversions are done in js, it's SYNC + let text = res.text() + let json = res.json() + + }) + // Status code is not 200 + .catch((errorMessage, statusCode) => { + // error handling + }) +``` + +### Download to storage directly + +If the response data is large, that would be a bad idea to convert it into BASE64 string. A better solution is streaming the response directly into a file, simply add a `fileCache` option to config, and set it to `true`. This will make incoming response data stored in a temporary path **without** any file extension. + +**These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)** + +```js +RNFetchBlob + .config({ + // add this option that makes response data to be stored as a file, + // this is much more performant. + fileCache : true, + }) + .fetch('GET', 'http://www.example.com/file/example.zip', { + //some headers .. + }) + .then((res) => { + // the temp file path + console.log('The file saved to ', res.path()) + }) +``` + +**Set Temp File Extension** + +Sometimes you might need a file extension for some reason. For example, when using file path as the source of `Image` component, the path should end with something like .png or .jpg, you can do this by add `appendExt` option to `config`. + +```js +RNFetchBlob + .config({ + fileCache : true, + // by adding this option, the temp files will have a file extension + appendExt : 'png' + }) + .fetch('GET', 'http://www.example.com/file/example.zip', { + //some headers .. + }) + .then((res) => { + // the temp file path with file extension `png` + console.log('The file saved to ', res.path()) + // Beware that when using a file path as Image source on Android, + // you must prepend "file://"" before the file path + imageView = + }) +``` + +**Use Specific File Path** + +If you prefer a particular file path rather than randomly generated one, you can use `path` option. We've added [several constants](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs) in v0.5.0 which represents commonly used directories. + +```js +let dirs = RNFetchBlob.fs.dirs +RNFetchBlob +.config({ + // response data will be saved to this path if it has access right. + path : dirs.DocumentDir + '/path-to-file.anything' +}) +.fetch('GET', 'http://www.example.com/file/example.zip', { + //some headers .. +}) +.then((res) => { + // the path should be dirs.DocumentDir + 'path-to-file.anything' + console.log('The file saved to ', res.path()) +}) +``` + +**These files won't be removed automatically, please refer to [Cache File Management](#user-content-cache-file-management)** + +#### Upload example : Dropbox [files-upload](https://www.dropbox.com/developers/documentation/http/documentation#files-upload) API + +`react-native-fetch-blob` 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. + +```js + +RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', { + Authorization : "Bearer access-token...", + 'Dropbox-API-Arg': JSON.stringify({ + path : '/img-from-react-native.png', + mode : 'add', + autorename : true, + mute : false + }), + 'Content-Type' : 'application/octet-stream', + // here's the body you're going to send, should be a BASE64 encoded string + // (you can use "base64"(refer to the library 'mathiasbynens/base64') APIs to make one). + // The data will be converted to "byte array"(say, blob) before request sent. + }, base64ImageString) + .then((res) => { + console.log(res.text()) + }) + .catch((err) => { + // error handling .. + }) +``` + +### Upload a file from storage + +If you're going to use a `file` as request body, just wrap the path with `wrap` API. + +```js +RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', { + // dropbox upload headers + Authorization : "Bearer access-token...", + 'Dropbox-API-Arg': JSON.stringify({ + path : '/img-from-react-native.png', + mode : 'add', + autorename : true, + mute : false + }), + 'Content-Type' : 'application/octet-stream', + // Change BASE64 encoded data to a file path with prefix `RNFetchBlob-file://`. + // Or simply wrap the file path with RNFetchBlob.wrap(). + }, RNFetchBlob.wrap(PATH_TO_THE_FILE)) + .then((res) => { + console.log(res.text()) + }) + .catch((err) => { + // error handling .. + }) +``` + +### Multipart/form-data example: Post form data with file and data + +In `version >= 0.3.0` you can also post files with form data, just put an array in `body`, with elements have property `name`, `data`, and `filename`(optional). + +Elements have property `filename` will be transformed into binary format, otherwise, it turns into utf8 string. + +```js + + RNFetchBlob.fetch('POST', 'http://www.example.com/upload-form', { + Authorization : "Bearer access-token", + otherHeader : "foo", + 'Content-Type' : 'multipart/form-data', + }, [ + // element with property `filename` will be transformed into `file` in form data + { name : 'avatar', filename : 'avatar.png', data: binaryDataInBase64}, + // custom content type + { name : 'avatar-png', filename : 'avatar-png.png', type:'image/png', data: binaryDataInBase64}, + // part file from storage + { name : 'avatar-foo', filename : 'avatar-foo.png', type:'image/foo', data: RNFetchBlob.wrap(path_to_a_file)}, + // elements without property `filename` will be sent as plain text + { name : 'name', data : 'user'}, + { name : 'info', data : JSON.stringify({ + mail : 'example@example.com', + tel : '12345678' + })}, + ]).then((resp) => { + // ... + }).catch((err) => { + // ... + }) +``` + +What if you want to append a file to form data? Just like [upload a file from storage](#user-content-upload-a-file-from-storage) example, wrap `data` by `wrap` API (this feature is only available for `version >= v0.5.0`). On version >= `0.6.2`, it is possible to set custom MIME type when appending a file to form data. But keep in mind when the file is large it's likely to crash your app. Please consider use other strategy (see [#94](https://github.com/wkh237/react-native-fetch-blob/issues/94)). + +```js + + RNFetchBlob.fetch('POST', 'http://www.example.com/upload-form', { + Authorization : "Bearer access-token", + otherHeader : "foo", + // this is required, otherwise it won't be process as a multipart/form-data request + 'Content-Type' : 'multipart/form-data', + }, [ + // append field data from file path + { + name : 'avatar', + filename : 'avatar.png', + // Change BASE64 encoded data to a file path with prefix `RNFetchBlob-file://`. + // Or simply wrap the file path with RNFetchBlob.wrap(). + data: RNFetchBlob.wrap(PATH_TO_THE_FILE) + }, + { + name : 'ringtone', + filename : 'ring.mp3', + // use custom MIME type + type : 'application/mp3', + // upload a file from asset is also possible in version >= 0.6.2 + data : RNFetchBlob.wrap(RNFetchBlob.fs.asset('default-ringtone.mp3')) + } + // elements without property `filename` will be sent as plain text + { name : 'name', data : 'user'}, + { name : 'info', data : JSON.stringify({ + mail : 'example@example.com', + tel : '12345678' + })}, + ]).then((resp) => { + // ... + }).catch((err) => { + // ... + }) +``` + +### Upload/Download progress + +In `version >= 0.4.2` it is possible to know the upload/download progress. After `0.7.0` IOS and Android upload progress are also supported. + +```js + RNFetchBlob.fetch('POST', 'http://www.example.com/upload', { + //... some headers, + 'Content-Type' : 'octet-stream' + }, base64DataString) + // listen to upload progress event + .uploadProgress((written, total) => { + console.log('uploaded', written / total) + }) + // listen to download progress event + .progress((received, total) => { + console.log('progress', received / total) + }) + .then((resp) => { + // ... + }) + .catch((err) => { + // ... + }) +``` + +In `0.9.6`, you can specify an object as the first argument which contains `count` and `interval`, to the frequency of progress event (this will be done in the native context a reduce RCT bridge overhead). Notice that `count` argument will not work if the server does not provide response content length. + + +```js + RNFetchBlob.fetch('POST', 'http://www.example.com/upload', { + //... some headers, + 'Content-Type' : 'octet-stream' + }, base64DataString) + // listen to upload progress event, emit every 250ms + .uploadProgress({ interval : 250 },(written, total) => { + console.log('uploaded', written / total) + }) + // listen to download progress event, every 10% + .progress({ count : 10 }, (received, total) => { + console.log('progress', received / total) + }) + .then((resp) => { + // ... + }) + .catch((err) => { + // ... + }) +``` + +### Cancel Request + +After `0.7.0` it is possible to cancel an HTTP request. Upon cancellation, it throws a promise rejection, be sure to catch it. + +```js +let task = RNFetchBlob.fetch('GET', 'http://example.com/file/1') + +task.then(() => { ... }) + // handle request cancelled rejection + .catch((err) => { + console.log(err) + }) +// cancel the request, the callback function is optional +task.cancel((err) => { ... }) + +``` + +### Drop-in Fetch Replacement + +0.9.0 + +If you have existing code that uses `whatwg-fetch`(the official **fetch**), it's not necessary to replace them with `RNFetchblob.fetch`, you can simply use our **Fetch Replacement**. The difference between Official them is official fetch uses [whatwg-fetch](https://github.com/github/fetch) which wraps XMLHttpRequest polyfill under the hood. It's a great library for web developers, but does not play very well with RN. Our implementation is simply a wrapper of our `fetch` and `fs` APIs, so you can access all the features we provided. + +[See document and examples](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetch-replacement) + +### Android Media Scanner, and Download Manager Support + +If you want to make a file in `External Storage` becomes visible in Picture, Downloads, or other built-in apps, you will have to use `Media Scanner` or `Download Manager`. + +**Media Scanner** + +Media scanner scans the file and categorizes by given MIME type, if MIME type not specified, it will try to resolve the file using its file extension. + +```js + +RNFetchBlob + .config({ + // DCIMDir is in external storage + path : dirs.DCIMDir + '/music.mp3' + }) + .fetch('GET', 'http://example.com/music.mp3') + .then((res) => RNFetchBlob.fs.scanFile([ { path : res.path(), mime : 'audio/mpeg' } ])) + .then(() => { + // scan file success + }) + .catch((err) => { + // scan file error + }) +``` + +**Download Manager** + +When downloading large files on Android it is recommended to use `Download Manager`, it supports a lot of native features like the progress bar, and notification, also the download task will be handled by OS, and more efficient. + + + +When using DownloadManager, `fileCache` and `path` properties in `config` will not take effect, because Android DownloadManager can only store files to external storage, also notice that Download Manager can only support `GET` method, which means the request body will be ignored. + +When download complete, DownloadManager will generate a file path so that you can deal with it. + +```js +RNFetchBlob + .config({ + addAndroidDownloads : { + useDownloadManager : true, // <-- this is the only thing required + // Optional, override notification setting (default to true) + notification : false, + // Optional, but recommended since android DownloadManager will fail when + // the url does not contains a file extension, by default the mime type will be text/plain + mime : 'text/plain', + description : 'File downloaded by download manager.' + } + }) + .fetch('GET', 'http://example.com/file/somefile') + .then((resp) => { + // the path of downloaded file + resp.path() + }) +``` + +Your app might not have right to remove/change the file created by Download Manager, therefore you might need to [set custom location to the download task](https://github.com/wkh237/react-native-fetch-blob/issues/236). + +**Download Notification and Visibility in Download App (Android Only)** + + + + + +If you need to display a notification upon the file is downloaded to storage (as the above) or make the downloaded file visible in "Downloads" app. You have to add some options to `config`. + +```js +RNFetchBlob.config({ + fileCache : true, + // android only options, these options be a no-op on IOS + addAndroidDownloads : { + // Show notification when response data transmitted + notification : true, + // Title of download notification + title : 'Great ! Download Success ! :O ', + // File description (not notification description) + description : 'An image file.', + mime : 'image/png', + // Make the file scannable by media scanner + mediaScannable : true, + } +}) +.fetch('GET', 'http://example.com/image1.png') +.then(...) +``` + +**Open Downloaded File with Intent** + +This is a new feature added in `0.9.0` if you're going to open a file path using official [Linking](https://facebook.github.io/react-native/docs/linking.html) API that might not work as expected, also, if you're going to install an APK in `Downloads` app, that will not function too. As an alternative, you can try `actionViewIntent` API, which will send an ACTION_VIEW intent for you which uses the given `MIME` type. + +Download and install an APK programmatically + +```js + +const android = RNFetchBlob.android + +RNFetchBlob.config({ + addAndroidDownloads : { + useDownloadManager : true, + title : 'awesome.apk', + description : 'An APK that will be installed', + mime : 'application/vnd.android.package-archive', + mediaScannable : true, + notification : true, + } + }) + .fetch('GET', `http://www.example.com/awesome.apk`) + .then((res) => { + android.actionViewIntent(res.path(), 'application/vnd.android.package-archive') + }) +``` + +Or show an image in image viewer + +```js + android.actionViewIntent(PATH_OF_IMG, 'image/png') +``` + +## File System + +### File Access + +File access APIs were made when developing `v0.5.0`, which helping us write tests, and was not planned to be a part of this module. However, we realized that it's hard to find a great solution to manage cached files, everyone who uses this module may need these APIs for their cases. + +Before start using file APIs, we recommend read [Differences between File Source](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#differences-between-file-source) first. + +File Access APIs +- [asset (0.6.2)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#assetfilenamestringstring) +- [dirs](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs) +- [createFile](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#createfilepath-data-encodingpromise) +- [writeFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise) +- [appendFile (0.6.0) ](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#appendfilepathstring-contentstring--array-encodingstringpromise) +- [readFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readfilepath-encodingpromise) +- [readStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readstreampath-encoding-buffersizepromise) +- [writeStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writestreampathstring-encodingstring-appendbooleanpromise) +- [unlink](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#unlinkpathstringpromise) +- [mkdir](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#mkdirpathstringpromise) +- [ls](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#lspathstringpromise) +- [mv](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#mvfromstring-tostringpromise) +- [cp](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#cpsrcstring-deststringpromise) +- [exists](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#existspathstringpromise) +- [isDir](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#isdirpathstringpromise) +- [stat](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#statpathstringpromise) +- [lstat](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#lstatpathstringpromise) +- [scanFile (Android only)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#scanfilepathstringpromise-androi-only) + +See [File API](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API) for more information + +### File Stream + +In `v0.5.0` we've added `writeStream` and `readStream`, which allows your app read/write data from the file path. This API creates a file stream, rather than convert entire data into BASE64 encoded string. It's handy when processing **large files**. + +When calling `readStream` method, you have to `open` the stream, and start to read data. When the file is large, consider using an appropriate `bufferSize` and `interval` to reduce the native event dispatching overhead (see [Performance Tips](#user-content-performance-tips)) + +> The file stream event has a default throttle(10ms) and buffer size which preventing it cause too much overhead to main thread, yo can also [tweak these values](#user-content-performance-tips). + +```js +let data = '' +RNFetchBlob.fs.readStream( + // file path + PATH_TO_THE_FILE, + // encoding, should be one of `base64`, `utf8`, `ascii` + 'base64', + // (optional) buffer size, default to 4096 (4095 for BASE64 encoded data) + // when reading file in BASE64 encoding, buffer size must be multiples of 3. + 4095) +.then((ifstream) => { + ifstream.open() + ifstream.onData((chunk) => { + // when encoding is `ascii`, chunk will be an array contains numbers + // otherwise it will be a string + data += chunk + }) + ifstream.onError((err) => { + console.log('oops', err) + }) + ifstream.onEnd(() => { + { + ofstream.write('foo') + ofstream.write('bar') + ofstream.close() +}) + +``` + +### Cache File Management + +When using `fileCache` or `path` options along with `fetch` API, response data will automatically store into the file system. The files will **NOT** removed unless you `unlink` it. There're several ways to remove the files + +```js + + // remove file using RNFetchblobResponse.flush() object method + RNFetchblob.config({ + fileCache : true + }) + .fetch('GET', 'http://example.com/download/file') + .then((res) => { + // remove cached file from storage + res.flush() + }) + + // remove file by specifying a path + RNFetchBlob.fs.unlink('some-file-path').then(() => { + // ... + }) + +``` + +You can also group requests by using `session` API and use `dispose` to remove them all when needed. + +```js + + RNFetchblob.config({ + fileCache : true + }) + .fetch('GET', 'http://example.com/download/file') + .then((res) => { + // set session of a response + res.session('foo') + }) + + RNFetchblob.config({ + // you can also set session beforehand + session : 'foo' + fileCache : true + }) + .fetch('GET', 'http://example.com/download/file') + .then((res) => { + // ... + }) + + // or put an existing file path to the session + RNFetchBlob.session('foo').add('some-file-path') + // remove a file path from the session + RNFetchBlob.session('foo').remove('some-file-path') + // list paths of a session + RNFetchBlob.session('foo').list() + // remove all files in a session + RNFetchBlob.session('foo').dispose().then(() => { ... }) + +``` + +### Transfer Encoding + +After `0.9.4`, the `Chunked` transfer encoding is disabled by default due to some service provider may not support chunked transfer. To enable it, set `Transfer-Encoding` header to `Chunked`. + +```js +RNFetchBlob.fetch('POST', 'http://example.com/upload', { 'Transfer-Encoding' : 'Chunked' }, bodyData) +``` + +### Self-Signed SSL Server + +By default, react-native-fetch-blob does NOT allow connection to unknown certification provider since it's dangerous. To connect a server with self-signed certification, you need to add `trusty` to `config` explicitly. This function is available for version >= `0.5.3` + +```js +RNFetchBlob.config({ + trusty : true +}) +.then('GET', 'https://mysite.com') +.then((resp) => { + // ... +}) +``` + +## Web API Polyfills + +After `0.8.0` we've made some [Web API polyfills](https://github.com/wkh237/react-native-fetch-blob/wiki/Web-API-Polyfills-(experimental)) that makes some browser-based library available in RN. + +- Blob +- XMLHttpRequest (Use our implementation if you're going to use it with Blob) + +Here's a [sample app](https://github.com/wkh237/rn-firebase-storage-upload-sample) that uses polyfills to upload files to FireBase. + +## Performance Tips + +**Read Stream and Progress Event Overhead** + +If the process seems to block JS thread when file is large when reading data via `fs.readStream`. It might because the default buffer size is quite small (4kb) which result in a lot of events triggered from JS thread. Try to increase the buffer size (for example 100kb = 102400) and set a larger interval (available for 0.9.4+, the default value is 10ms) to limit the frequency. + +**Reduce RCT Bridge and BASE64 Overhead** + +React Native connects JS and Native context by passing JSON around React Native bridge, and there will be an overhead to convert data before they sent to each side. When data is large, this will be quite a performance impact to your app. It's recommended to use file storage instead of BASE64 if possible.The following chart shows how much faster when loading data from storage than BASE64 encoded string on iPhone 6. + + + +**ASCII Encoding has /terrible Performance** + +Due to the [lack of typed array implementation in JavascriptCore, and limitation of React Native structure](https://github.com/facebook/react-native/issues/1424), to convert data to JS byte array spends lot of time. Use it only when needed, the following chart shows how much time it takes when reading a file with different encoding. + + + +**Concat and Replacing Files** + +If you're going to concatenate files, you don't have to read the data to JS context anymore! In `0.8.0` we introduced new encoding `uri` for writeFile and appendFile API, which make it possible to handle the whole process in native. + + + +## Caveats + +* This library does not urlencode unicode characters in URL automatically, see [#146](https://github.com/wkh237/react-native-fetch-blob/issues/146). +* When you create a `Blob` , from an existing file, the file **WILL BE REMOVED** if you `close` the blob. +* If you replaced `window.XMLHttpRequest` for some reason (e.g. make Firebase SDK work), it will also affect how official `fetch` works (basically it should work just fine). +* When file stream and upload/download progress event slow down your app, consider an upgrade to `0.9.6+`, use [additional arguments](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetchprogressconfig-eventlistenerpromisernfetchblobresponse) to limit its frequency. +* When passing a file path to the library, remove `file://` prefix. + +when you got a problem, have a look at [Trouble Shooting](https://github.com/wkh237/react-native-fetch-blob/wiki/Trouble-Shooting) or [issues labeled Trouble Shooting](https://github.com/wkh237/react-native-fetch-blob/issues?utf8=✓&q=label:%22trouble%20shooting%22%20), there'd be some helpful information. + +## Changes + +See [release notes](https://github.com/wkh237/react-native-fetch-blob/releases) + +### Development + +If you're interested in hacking this module, check our [development guide](https://github.com/wkh237/react-native-fetch-blob/wiki/Home), there might be some helpful information. +Please feel free to make a PR or file an issue.