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

Commit 4944e39

Browse files
committed
Merge branch 'master' into 0.10.0
2 parents eef6fcc + 81ff2f6 commit 4944e39

File tree

11 files changed

+125
-121
lines changed

11 files changed

+125
-121
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ A project committed to make file acess and data transfer easier, efficient for R
3838

3939
## About
4040

41-
This project was initially for solving the issue [facebook/react-native#854](https://github.com/facebook/react-native/issues/854), because React Native lack of `Blob` implementation and it will cause some problem when transfering binary data. Now, this project is committed to make file access and transfer more easier, efficient for React Native developers. We've implemented highly customizable filesystem and network module which plays well together. For example, upload and download data directly from/to storage which is much more efficient in some cases(especially for large ones). The file system supports file stream, so you don't have to worry about OOM problem when accessing large files.
41+
This project was initially for solving the issue [facebook/react-native#854](https://github.com/facebook/react-native/issues/854), because React Native lack of `Blob` implementation and it will cause some problem when transferring binary data. Now, this project is committed to make file access and transfer more easier, efficient for React Native developers. We've implemented highly customizable filesystem and network module which plays well together. For example, upload and download data directly from/to storage which is much more efficient in some cases(especially for large ones). The file system supports file stream, so you don't have to worry about OOM problem when accessing large files.
4242

43-
In `0.8.0` we introduced experimential Web API polyfills that make it possible to use browser-based libraries in React Native, for example, [FireBase JS SDK](https://github.com/wkh237/rn-firebase-storage-upload-sample)
43+
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)
4444

4545

4646
## Installation

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"name": "fetchblob-dev",
3-
"author": "wkh237 <[email protected]>",
4-
"version": "0.9.2",
3+
"version": "0.9.3",
54
"private": true,
65
"scripts": {
76
"start": "node node_modules/react-native/local-cli/cli.js start",

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

Lines changed: 76 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.RNFetchBlob;
22

33
import android.util.Base64;
4-
import android.util.Log;
54

65
import com.facebook.react.bridge.Arguments;
76
import com.facebook.react.bridge.ReactApplicationContext;
@@ -13,23 +12,14 @@
1312
import java.io.ByteArrayInputStream;
1413
import java.io.File;
1514
import java.io.FileInputStream;
16-
import java.io.FileNotFoundException;
1715
import java.io.FileOutputStream;
1816
import java.io.IOException;
1917
import java.io.InputStream;
20-
import java.nio.ByteBuffer;
21-
import java.nio.MappedByteBuffer;
2218
import java.util.ArrayList;
23-
import java.util.HashMap;
2419

2520
import okhttp3.MediaType;
2621
import okhttp3.RequestBody;
27-
import okhttp3.FormBody;
28-
import okio.Buffer;
2922
import okio.BufferedSink;
30-
import okio.ForwardingSink;
31-
import okio.Okio;
32-
import okio.Sink;
3323

3424
/**
3525
* Created by wkh237 on 2016/7/11.
@@ -38,7 +28,6 @@ public class RNFetchBlobBody extends RequestBody{
3828

3929
InputStream requestStream;
4030
long contentLength = 0;
41-
long bytesWritten = 0;
4231
ReadableArray form;
4332
String mTaskId;
4433
String rawBody;
@@ -47,58 +36,84 @@ public class RNFetchBlobBody extends RequestBody{
4736
File bodyCache;
4837

4938

39+
/**
40+
* Single file or raw content request constructor
41+
* @param taskId
42+
* @param type
43+
* @param form
44+
* @param contentType
45+
*/
5046
public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, ReadableArray form, MediaType contentType) {
5147
this.mTaskId = taskId;
5248
this.form = form;
5349
requestType = type;
5450
mime = contentType;
5551
try {
5652
bodyCache = createMultipartBodyCache();
53+
requestStream = new FileInputStream(bodyCache);
5754
contentLength = bodyCache.length();
58-
} catch (IOException e) {
59-
e.printStackTrace();
55+
} catch(Exception ex) {
56+
ex.printStackTrace();
57+
RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create request multipart body :" + ex.getLocalizedMessage());
6058
}
6159
}
6260

61+
/**
62+
* Multipart request constructor
63+
* @param taskId
64+
* @param type
65+
* @param rawBody
66+
* @param contentType
67+
*/
6368
public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, String rawBody, MediaType contentType) {
6469
this.mTaskId = taskId;
6570
requestType = type;
6671
this.rawBody = rawBody;
6772
mime = contentType;
68-
if(rawBody != null) {
69-
if(requestType == RNFetchBlobReq.RequestType.AsIs)
70-
contentLength = rawBody.length();
71-
else
72-
contentLength = caculateOctetContentLength();
73+
if(rawBody == null) {
74+
this.rawBody = "";
75+
requestType = RNFetchBlobReq.RequestType.AsIs;
76+
}
77+
try {
78+
switch (requestType) {
79+
case SingleFile:
80+
requestStream = getReuqestStream();
81+
contentLength = requestStream.available();
82+
break;
83+
case AsIs:
84+
contentLength = this.rawBody.getBytes().length;
85+
break;
86+
case Others:
87+
break;
88+
}
89+
} catch(Exception ex) {
90+
ex.printStackTrace();
91+
RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create single content request body :" + ex.getLocalizedMessage() + "\r\n");
7392
}
93+
7494
}
7595

76-
// ${RN 0.26+ ONLY} @Override
77-
// ${RN 0.26+ ONLY} public long contentLength() { return contentLength; }
96+
@Override
97+
public long contentLength() {
98+
return contentLength;
99+
}
78100

79101
@Override
80102
public MediaType contentType() {
81103
return mime;
82104
}
83105

84106
@Override
85-
public void writeTo(BufferedSink sink) throws IOException {
86-
87-
ProgressReportingSource source = new ProgressReportingSource(sink, mTaskId);
88-
BufferedSink buffer = Okio.buffer(source);
89-
switch (requestType) {
90-
case Form:
91-
pipeStreamToSink(new FileInputStream(bodyCache), sink);
92-
break;
93-
case SingleFile:
94-
if(requestStream != null)
95-
pipeStreamToSink(requestStream, sink);
96-
break;
97-
case AsIs:
98-
writeRawData(sink);
99-
break;
107+
public void writeTo(BufferedSink sink) {
108+
try {
109+
if (requestType == RNFetchBlobReq.RequestType.AsIs)
110+
sink.write(rawBody.getBytes());
111+
else
112+
pipeStreamToSink(requestStream, sink);
113+
} catch(Exception ex) {
114+
RNFetchBlobUtils.emitWarningEvent(ex.getLocalizedMessage());
115+
ex.printStackTrace();
100116
}
101-
buffer.flush();
102117
}
103118

104119
boolean clearRequestBody() {
@@ -113,8 +128,8 @@ boolean clearRequestBody() {
113128
return true;
114129
}
115130

116-
private long caculateOctetContentLength() {
117-
long total = 0;
131+
private InputStream getReuqestStream() throws Exception {
132+
118133
// upload from storage
119134
if (rawBody.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
120135
String orgPath = rawBody.substring(RNFetchBlobConst.FILE_PREFIX.length());
@@ -123,32 +138,30 @@ private long caculateOctetContentLength() {
123138
if (RNFetchBlobFS.isAsset(orgPath)) {
124139
try {
125140
String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
126-
total += RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
127-
requestStream = RNFetchBlob.RCTContext.getAssets().open(assetName);
128-
} catch (IOException e) {
129-
RNFetchBlobUtils.emitWarningEvent(e.getLocalizedMessage());
141+
return RNFetchBlob.RCTContext.getAssets().open(assetName);
142+
} catch (Exception e) {
143+
throw new Exception("error when getting request stream from asset : " +e.getLocalizedMessage());
130144
}
131145
} else {
132146
File f = new File(RNFetchBlobFS.normalizePath(orgPath));
133147
try {
134148
if(!f.exists())
135149
f.createNewFile();
136-
total += f.length();
137-
requestStream = new FileInputStream(f);
150+
return new FileInputStream(f);
138151
} catch (Exception e) {
139-
RNFetchBlobUtils.emitWarningEvent("RNetchBlob error when counting content length: " +e.getLocalizedMessage());
152+
throw new Exception("error when getting request stream: " +e.getLocalizedMessage());
140153
}
141154
}
142-
} else {
155+
}
156+
// base 64 encoded
157+
else {
143158
try {
144159
byte[] bytes = Base64.decode(rawBody, 0);
145-
requestStream = new ByteArrayInputStream(bytes);
146-
total += requestStream.available();
160+
return new ByteArrayInputStream(bytes);
147161
} catch(Exception ex) {
148-
RNFetchBlobUtils.emitWarningEvent("RNetchBlob error when counting content length: " +ex.getLocalizedMessage());
162+
throw new Exception("error when getting request stream: " + ex.getLocalizedMessage());
149163
}
150164
}
151-
return total;
152165
}
153166

154167
/**
@@ -191,7 +204,7 @@ private File createMultipartBodyCache() throws IOException {
191204
InputStream in = ctx.getAssets().open(assetName);
192205
pipeStreamToFileStream(in, os);
193206
} catch (IOException e) {
194-
RNFetchBlobUtils.emitWarningEvent("RNFetchBlob Failed to create form data asset :" + orgPath + ", " + e.getLocalizedMessage() );
207+
RNFetchBlobUtils.emitWarningEvent("Failed to create form data asset :" + orgPath + ", " + e.getLocalizedMessage() );
195208
}
196209
}
197210
// data from normal files
@@ -202,16 +215,14 @@ private File createMultipartBodyCache() throws IOException {
202215
pipeStreamToFileStream(fs, os);
203216
}
204217
else {
205-
RNFetchBlobUtils.emitWarningEvent("RNFetchBlob Failed to create form data from path :" + orgPath + ", file not exists.");
218+
RNFetchBlobUtils.emitWarningEvent("Failed to create form data from path :" + orgPath + ", file not exists.");
206219
}
207220
}
208221
}
209222
// base64 embedded file content
210223
else {
211224
byte[] b = Base64.decode(data, 0);
212225
os.write(b);
213-
bytesWritten += b.length;
214-
emitUploadProgress();
215226
}
216227

217228
}
@@ -221,7 +232,6 @@ private File createMultipartBodyCache() throws IOException {
221232
header += "Content-Type: " + field.mime + "\r\n\r\n";
222233
os.write(header.getBytes());
223234
byte[] fieldData = field.data.getBytes();
224-
bytesWritten += fieldData.length;
225235
os.write(fieldData);
226236
}
227237
// form end
@@ -235,28 +245,22 @@ private File createMultipartBodyCache() throws IOException {
235245
return outputFile;
236246
}
237247

238-
/**
239-
* Write data to request body as-is
240-
* @param sink
241-
*/
242-
private void writeRawData(BufferedSink sink) throws IOException {
243-
byte[] bytes = rawBody.getBytes();
244-
contentLength = bytes.length;
245-
sink.write(bytes);
246-
}
247-
248248
/**
249249
* Pipe input stream to request body output stream
250250
* @param stream The input stream
251251
* @param sink The request body buffer sink
252252
* @throws IOException
253253
*/
254-
private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws IOException {
254+
private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws Exception {
255+
255256
byte [] chunk = new byte[10240];
257+
int totalWritten = 0;
256258
int read;
257259
while((read = stream.read(chunk, 0, 10240)) > 0) {
258260
if(read > 0) {
259261
sink.write(chunk, 0, read);
262+
totalWritten += read;
263+
emitUploadProgress(totalWritten);
260264
}
261265
}
262266
stream.close();
@@ -355,45 +359,19 @@ public FormField(ReadableMap rawData) {
355359
}
356360
}
357361

358-
private void emitUploadProgress() {
362+
/**
363+
* Emit progress event
364+
* @param written
365+
*/
366+
private void emitUploadProgress(int written) {
359367
WritableMap args = Arguments.createMap();
360368
args.putString("taskId", mTaskId);
361-
args.putString("written", String.valueOf(bytesWritten));
369+
args.putString("written", String.valueOf(written));
362370
args.putString("total", String.valueOf(contentLength));
363371

364372
// emit event to js context
365373
RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
366374
.emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
367375
}
368376

369-
private final class ProgressReportingSource extends ForwardingSink {
370-
371-
private long bytesWritten = 0;
372-
private String mTaskId;
373-
private Sink delegate;
374-
375-
public ProgressReportingSource (Sink delegate, String taskId) {
376-
super(delegate);
377-
this.mTaskId = taskId;
378-
this.delegate = delegate;
379-
}
380-
381-
@Override
382-
public void write(Buffer source, long byteCount) throws IOException {
383-
delegate.write(source, byteCount);
384-
// on progress, emit RNFetchBlobProgress upload progress event with ticketId,
385-
// bytesWritten, and totalSize
386-
bytesWritten += byteCount;
387-
WritableMap args = Arguments.createMap();
388-
args.putString("taskId", mTaskId);
389-
args.putString("written", String.valueOf(bytesWritten));
390-
args.putString("total", String.valueOf(contentLength));
391-
392-
if(RNFetchBlobReq.isReportUploadProgress(mTaskId)) {
393-
// emit event to js context
394-
RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
395-
.emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
396-
}
397-
}
398-
}
399377
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ public Response intercept(Chain chain) throws IOException {
332332
clientBuilder.retryOnConnectionFailure(false);
333333
clientBuilder.followRedirects(true);
334334

335-
OkHttpClient client = clientBuilder.build();
335+
OkHttpClient client = clientBuilder.retryOnConnectionFailure(true).build();
336336
Call call = client.newCall(req);
337337
taskTable.put(taskId, call);
338338
call.enqueue(new okhttp3.Callback() {

src/class/StatefulPromise.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright 2016 wkh237@github. All rights reserved.
2+
// Use of this source code is governed by a MIT-style license that can be
3+
// found in the LICENSE file.
4+
// @flow
5+
6+
export default class StatefulPromise extends Promise {
7+
8+
}

src/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
RNFetchBlobStream,
1717
RNFetchBlobResponseInfo
1818
} from './types'
19+
import StatefulPromise from './class/StatefulPromise.js'
1920
import fs from './fs'
2021
import getUUID from './utils/uuid'
2122
import base64 from 'base-64'
@@ -167,6 +168,13 @@ function fetch(...args:any):Promise {
167168
subscription.remove()
168169
subscriptionUpload.remove()
169170
stateEvent.remove()
171+
delete promise['progress']
172+
delete promise['uploadProgress']
173+
delete promise['stateChange']
174+
delete promise['cancel']
175+
promise.cancel = () => {
176+
console.warn('finished request could not be canceled')
177+
}
170178

171179
if(err)
172180
reject(new Error(err, respInfo))

0 commit comments

Comments
 (0)