Skip to content

Commit 7cebeaa

Browse files
authored
Merge branch '0.10.9' into exception_fixes
2 parents a46bf81 + 4205ebc commit 7cebeaa

25 files changed

+1031
-589
lines changed

README.md

+50-4
Original file line numberDiff line numberDiff line change
@@ -590,10 +590,12 @@ File Access APIs
590590
- [dirs](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#dirs)
591591
- [createFile](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#createfilepath-data-encodingpromise)
592592
- [writeFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writefilepathstring-contentstring--array-encodingstring-appendbooleanpromise)
593-
- [appendFile (0.6.0) ](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#appendfilepathstring-contentstring--array-encodingstringpromise)
593+
- [appendFile (0.6.0) ](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#appendfilepathstring-contentstring--arraynumber-encodingstring-promisenumber)
594594
- [readFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readfilepath-encodingpromise)
595-
- [readStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readstreampath-encoding-buffersizepromise)
596-
- [writeStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writestreampathstring-encodingstring-appendbooleanpromise)
595+
- [readStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readstreampath-encoding-buffersize-interval-promisernfbreadstream)
596+
- [hash (0.10.9)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#hashpath-algorithm-promise)
597+
- [writeStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writestreampathstring-encodingstringpromise)
598+
- [hash](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#hashpath-algorithmpromise)
597599
- [unlink](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#unlinkpathstringpromise)
598600
- [mkdir](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#mkdirpathstringpromise)
599601
- [ls](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#lspathstringpromise)
@@ -643,6 +645,45 @@ RNFetchBlob.fs.readStream(
643645

644646
When using `writeStream`, the stream object becomes writable, and you can then perform operations like `write` and `close`.
645647

648+
Since version 0.10.9 `write()` resolves with the `RNFetchBlob` instance so you can promise-chain write calls:
649+
650+
```js
651+
RNFetchBlob.fs.writeStream(
652+
PATH_TO_FILE,
653+
// encoding, should be one of `base64`, `utf8`, `ascii`
654+
'utf8',
655+
// should data append to existing content ?
656+
true
657+
)
658+
.then(ofstream => ofstream.write('foo'))
659+
.then(ofstream => ofstream.write('bar'))
660+
.then(ofstream => ofstream.write('foobar'))
661+
.then(ofstream => ofstream.close())
662+
.catch(console.error)
663+
```
664+
665+
or
666+
667+
```js
668+
RNFetchBlob.fs.writeStream(
669+
PATH_TO_FILE,
670+
// encoding, should be one of `base64`, `utf8`, `ascii`
671+
'utf8',
672+
// should data append to existing content ?
673+
true
674+
)
675+
.then(stream => Promise.all([
676+
stream.write('foo'),
677+
stream.write('bar'),
678+
stream.write('foobar')
679+
]))
680+
// Use array destructuring to get the stream object from the first item of the array we get from Promise.all()
681+
.then(([stream]) => stream.close())
682+
.catch(console.error)
683+
```
684+
685+
You should **NOT** do something like this:
686+
646687
```js
647688
RNFetchBlob.fs.writeStream(
648689
PATH_TO_FILE,
@@ -651,13 +692,18 @@ RNFetchBlob.fs.writeStream(
651692
// should data append to existing content ?
652693
true)
653694
.then((ofstream) => {
695+
// BAD IDEA - Don't do this, those writes are unchecked:
654696
ofstream.write('foo')
655697
ofstream.write('bar')
656698
ofstream.close()
657699
})
658-
700+
.catch(console.error) // Cannot catch any write() errors!
659701
```
660702

703+
The problem with the above code is that the promises from the `ofstream.write()` calls are detached and "Lost".
704+
That means the entire promise chain A) resolves without waiting for the writes to finish and B) any errors caused by them are lost.
705+
That code may _seem_ to work if there are no errors, but those writes are of the type "fire and forget": You start them and then turn away and never know if they really succeeded.
706+
661707
### Cache File Management
662708

663709
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

android.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const RNFetchBlob:RNFetchBlobNative = NativeModules.RNFetchBlob
1313

1414
/**
1515
* Send an intent to open the file.
16-
* @param {string]} path Path of the file to be open.
16+
* @param {string} path Path of the file to be open.
1717
* @param {string} mime MIME type string
1818
* @return {Promise}
1919
*/

android/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ android {
3434

3535
dependencies {
3636
compile 'com.facebook.react:react-native:+'
37+
//compile 'com.squareup.okhttp3:okhttp:+'
3738
//{RNFetchBlob_PRE_0.28_DEPDENDENCY}
3839
}

android/gradle/wrapper/gradle-wrapper.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#Wed May 18 12:33:41 CST 2016
1+
#Sat Aug 12 07:48:35 CEST 2017
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME

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

+38-20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.app.DownloadManager;
55
import android.content.Intent;
66
import android.net.Uri;
7+
import android.util.SparseArray;
78

89
import com.facebook.react.bridge.ActivityEventListener;
910
import com.facebook.react.bridge.Callback;
@@ -34,26 +35,23 @@
3435

3536
public class RNFetchBlob extends ReactContextBaseJavaModule {
3637

37-
// Cookies
38-
private final ForwardingCookieHandler mCookieHandler;
39-
private final CookieJarContainer mCookieJarContainer;
4038
private final OkHttpClient mClient;
4139

4240
static ReactApplicationContext RCTContext;
43-
static LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
44-
static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
41+
private static LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
42+
private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
4543
static LinkedBlockingQueue<Runnable> fsTaskQueue = new LinkedBlockingQueue<>();
46-
static ThreadPoolExecutor fsThreadPool = new ThreadPoolExecutor(2, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
47-
static public boolean ActionViewVisible = false;
48-
static HashMap<Integer, Promise> promiseTable = new HashMap<>();
44+
private static ThreadPoolExecutor fsThreadPool = new ThreadPoolExecutor(2, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
45+
private static boolean ActionViewVisible = false;
46+
private static SparseArray<Promise> promiseTable = new SparseArray<>();
4947

5048
public RNFetchBlob(ReactApplicationContext reactContext) {
5149

5250
super(reactContext);
5351

5452
mClient = OkHttpClientProvider.getOkHttpClient();
55-
mCookieHandler = new ForwardingCookieHandler(reactContext);
56-
mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
53+
ForwardingCookieHandler mCookieHandler = new ForwardingCookieHandler(reactContext);
54+
CookieJarContainer mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
5755
mCookieJarContainer.setCookieJar(new JavaNetCookieJar(mCookieHandler));
5856

5957
RCTContext = reactContext;
@@ -85,11 +83,21 @@ public Map<String, Object> getConstants() {
8583
}
8684

8785
@ReactMethod
88-
public void createFile(final String path, final String content, final String encode, final Callback callback) {
86+
public void createFile(final String path, final String content, final String encode, final Promise promise) {
8987
threadPool.execute(new Runnable() {
9088
@Override
9189
public void run() {
92-
RNFetchBlobFS.createFile(path, content, encode, callback);
90+
RNFetchBlobFS.createFile(path, content, encode, promise);
91+
}
92+
});
93+
}
94+
95+
@ReactMethod
96+
public void createFileASCII(final String path, final ReadableArray dataArray, final Promise promise) {
97+
threadPool.execute(new Runnable() {
98+
@Override
99+
public void run() {
100+
RNFetchBlobFS.createFileASCII(path, dataArray, promise);
93101
}
94102
});
95103
}
@@ -123,7 +131,7 @@ public void onHostDestroy() {
123131
};
124132
RCTContext.addLifecycleEventListener(listener);
125133
} catch(Exception ex) {
126-
promise.reject(ex.getLocalizedMessage());
134+
promise.reject("EUNSPECIFIED", ex.getLocalizedMessage());
127135
}
128136
}
129137

@@ -148,8 +156,8 @@ public void unlink(String path, Callback callback) {
148156
}
149157

150158
@ReactMethod
151-
public void mkdir(String path, Callback callback) {
152-
RNFetchBlobFS.mkdir(path, callback);
159+
public void mkdir(String path, Promise promise) {
160+
RNFetchBlobFS.mkdir(path, promise);
153161
}
154162

155163
@ReactMethod
@@ -173,8 +181,8 @@ public void mv(String path, String dest, Callback callback) {
173181
}
174182

175183
@ReactMethod
176-
public void ls(String path, Callback callback) {
177-
RNFetchBlobFS.ls(path, callback);
184+
public void ls(String path, Promise promise) {
185+
RNFetchBlobFS.ls(path, promise);
178186
}
179187

180188
@ReactMethod
@@ -262,11 +270,21 @@ public void run() {
262270
}
263271

264272
@ReactMethod
273+
public void hash(final String path, final String algorithm, final Promise promise) {
274+
threadPool.execute(new Runnable() {
275+
@Override
276+
public void run() {
277+
RNFetchBlobFS.hash(path, algorithm, promise);
278+
}
279+
});
280+
}
281+
265282
/**
266283
* @param path Stream file path
267284
* @param encoding Stream encoding, should be one of `base64`, `ascii`, and `utf8`
268285
* @param bufferSize Stream buffer size, default to 4096 or 4095(base64).
269286
*/
287+
@ReactMethod
270288
public void readStream(final String path, final String encoding, final int bufferSize, final int tick, final String streamId) {
271289
final ReactApplicationContext ctx = this.getReactApplicationContext();
272290
fsThreadPool.execute(new Runnable() {
@@ -340,10 +358,10 @@ public void getContentIntent(String mime, Promise promise) {
340358

341359
@ReactMethod
342360
public void addCompleteDownload (ReadableMap config, Promise promise) {
343-
DownloadManager dm = (DownloadManager) RNFetchBlob.RCTContext.getSystemService(RNFetchBlob.RCTContext.DOWNLOAD_SERVICE);
361+
DownloadManager dm = (DownloadManager) RCTContext.getSystemService(RCTContext.DOWNLOAD_SERVICE);
344362
String path = RNFetchBlobFS.normalizePath(config.getString("path"));
345363
if(path == null) {
346-
promise.reject("RNFetchblob.addCompleteDownload can not resolve URI:" + config.getString("path"), "RNFetchblob.addCompleteDownload can not resolve URI:" + path);
364+
promise.reject("EINVAL", "RNFetchblob.addCompleteDownload can not resolve URI:" + config.getString("path"));
347365
return;
348366
}
349367
try {
@@ -360,7 +378,7 @@ public void addCompleteDownload (ReadableMap config, Promise promise) {
360378
promise.resolve(null);
361379
}
362380
catch(Exception ex) {
363-
promise.reject("RNFetchblob.addCompleteDownload failed", ex.getStackTrace().toString());
381+
promise.reject("EUNSPECIFIED", ex.getLocalizedMessage());
364382
}
365383

366384
}

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

+28-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.RNFetchBlob;
22

3+
import android.support.annotation.NonNull;
34
import android.util.Base64;
45

56
import com.facebook.react.bridge.Arguments;
@@ -21,21 +22,20 @@
2122
import okhttp3.RequestBody;
2223
import okio.BufferedSink;
2324

24-
public class RNFetchBlobBody extends RequestBody{
25+
class RNFetchBlobBody extends RequestBody{
2526

26-
InputStream requestStream;
27-
long contentLength = 0;
28-
ReadableArray form;
29-
String mTaskId;
30-
String rawBody;
31-
RNFetchBlobReq.RequestType requestType;
32-
MediaType mime;
33-
File bodyCache;
27+
private InputStream requestStream;
28+
private long contentLength = 0;
29+
private ReadableArray form;
30+
private String mTaskId;
31+
private String rawBody;
32+
private RNFetchBlobReq.RequestType requestType;
33+
private MediaType mime;
34+
private File bodyCache;
3435
int reported = 0;
35-
Boolean chunkedEncoding = false;
36+
private Boolean chunkedEncoding = false;
3637

37-
38-
public RNFetchBlobBody(String taskId) {
38+
RNFetchBlobBody(String taskId) {
3939
this.mTaskId = taskId;
4040
}
4141

@@ -49,7 +49,7 @@ RNFetchBlobBody setMIME(MediaType mime) {
4949
return this;
5050
}
5151

52-
RNFetchBlobBody setRequestType( RNFetchBlobReq.RequestType type) {
52+
RNFetchBlobBody setRequestType(RNFetchBlobReq.RequestType type) {
5353
this.requestType = type;
5454
return this;
5555
}
@@ -114,7 +114,7 @@ public MediaType contentType() {
114114
}
115115

116116
@Override
117-
public void writeTo(BufferedSink sink) {
117+
public void writeTo(@NonNull BufferedSink sink) {
118118
try {
119119
pipeStreamToSink(requestStream, sink);
120120
} catch(Exception ex) {
@@ -186,8 +186,7 @@ private File createMultipartBodyCache() throws IOException {
186186
ArrayList<FormField> fields = countFormDataLength();
187187
ReactApplicationContext ctx = RNFetchBlob.RCTContext;
188188

189-
for(int i = 0;i < fields.size(); i++) {
190-
FormField field = fields.get(i);
189+
for(FormField field : fields) {
191190
String data = field.data;
192191
String name = field.name;
193192
// skip invalid fields
@@ -258,17 +257,14 @@ private File createMultipartBodyCache() throws IOException {
258257
* @param sink The request body buffer sink
259258
* @throws IOException
260259
*/
261-
private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws Exception {
262-
263-
byte [] chunk = new byte[10240];
260+
private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws IOException {
261+
byte[] chunk = new byte[10240];
264262
int totalWritten = 0;
265263
int read;
266264
while((read = stream.read(chunk, 0, 10240)) > 0) {
267-
if(read > 0) {
268-
sink.write(chunk, 0, read);
269-
totalWritten += read;
270-
emitUploadProgress(totalWritten);
271-
}
265+
sink.write(chunk, 0, read);
266+
totalWritten += read;
267+
emitUploadProgress(totalWritten);
272268
}
273269
stream.close();
274270
}
@@ -291,7 +287,7 @@ private void pipeStreamToFileStream(InputStream is, FileOutputStream os) throws
291287

292288
/**
293289
* Compute approximate content length for form data
294-
* @return
290+
* @return ArrayList<FormField>
295291
*/
296292
private ArrayList<FormField> countFormDataLength() {
297293
long total = 0;
@@ -300,11 +296,11 @@ private ArrayList<FormField> countFormDataLength() {
300296
for(int i = 0;i < form.size(); i++) {
301297
FormField field = new FormField(form.getMap(i));
302298
list.add(field);
303-
String data = field.data;
304-
if(data == null) {
299+
if(field.data == null) {
305300
RNFetchBlobUtils.emitWarningEvent("RNFetchBlob multipart request builder has found a field without `data` property, the field `"+ field.name +"` will be removed implicitly.");
306301
}
307302
else if (field.filename != null) {
303+
String data = field.data;
308304
// upload from storage
309305
if (data.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
310306
String orgPath = data.substring(RNFetchBlobConst.FILE_PREFIX.length());
@@ -333,7 +329,7 @@ else if (field.filename != null) {
333329
}
334330
// data field
335331
else {
336-
total += field.data != null ? field.data.getBytes().length : 0;
332+
total += field.data.getBytes().length;
337333
}
338334
}
339335
contentLength = total;
@@ -346,11 +342,11 @@ else if (field.filename != null) {
346342
*/
347343
private class FormField {
348344
public String name;
349-
public String filename;
350-
public String mime;
345+
String filename;
346+
String mime;
351347
public String data;
352348

353-
public FormField(ReadableMap rawData) {
349+
FormField(ReadableMap rawData) {
354350
if(rawData.hasKey("name"))
355351
name = rawData.getString("name");
356352
if(rawData.hasKey("filename"))
@@ -368,7 +364,7 @@ public FormField(ReadableMap rawData) {
368364

369365
/**
370366
* Emit progress event
371-
* @param written
367+
* @param written Integer
372368
*/
373369
private void emitUploadProgress(int written) {
374370
RNFetchBlobProgressConfig config = RNFetchBlobReq.getReportUploadProgress(mTaskId);

0 commit comments

Comments
 (0)