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

Commit 57bfea9

Browse files
committed
Merge branch '0.11.0' of github.com:wkh237/react-native-fetch-blob into 0.11.0
2 parents 7365e56 + 0e335ab commit 57bfea9

File tree

6 files changed

+312
-3
lines changed

6 files changed

+312
-3
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,48 @@ What if you want to append a file to form data? Just like [upload a file from st
382382
})
383383
```
384384

385+
When you append files to form-data and you want to continue uploading while the app is in background on Android, you can use IntentService to upload by adding `multipartFileUpload` flag to config.
386+
387+
```js
388+
389+
RNFetchBlob.config({
390+
multipartFileUpload: true,
391+
})
392+
.fetch('POST', 'http://www.example.com/upload-form', {
393+
Authorization : "Bearer access-token",
394+
otherHeader : "foo",
395+
// this is required, otherwise it won't be process as a multipart/form-data request
396+
'Content-Type' : 'multipart/form-data',
397+
}, [
398+
// append field data from file path
399+
{
400+
name : 'avatar',
401+
filename : 'avatar.png',
402+
// Change BASE64 encoded data to a file path with prefix `RNFetchBlob-file://`.
403+
// Or simply wrap the file path with RNFetchBlob.wrap().
404+
data: RNFetchBlob.wrap(PATH_TO_THE_FILE)
405+
},
406+
{
407+
name : 'ringtone',
408+
filename : 'ring.mp3',
409+
// use custom MIME type
410+
type : 'application/mp3',
411+
// upload a file from asset is also possible in version >= 0.6.2
412+
data : RNFetchBlob.wrap(RNFetchBlob.fs.asset('default-ringtone.mp3'))
413+
}
414+
// elements without property `filename` will be sent as plain text
415+
{ name : 'name', data : 'user'},
416+
{ name : 'info', data : JSON.stringify({
417+
418+
tel : '12345678'
419+
})},
420+
]).then((resp) => {
421+
// ...
422+
}).catch((err) => {
423+
// ...
424+
})
425+
```
426+
385427
### Upload/Download progress
386428

387429
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.

android/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
android:label="@string/app_name"
66
android:supportsRtl="true">
77

8+
<service
9+
android:name=".RNFetchBlobService"
10+
>
11+
</service>
812
</application>
913

1014
</manifest>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public class RNFetchBlobConfig {
2121
public Boolean increment = false;
2222
public Boolean followRedirect = true;
2323
public ReadableArray binaryContentTypes = null;
24+
public boolean multipartFileUpload;
2425

2526
RNFetchBlobConfig(ReadableMap options) {
2627
if(options == null)
@@ -49,6 +50,7 @@ public class RNFetchBlobConfig {
4950
if(options.hasKey("timeout")) {
5051
this.timeout = options.getInt("timeout");
5152
}
53+
this.multipartFileUpload = options.hasKey("multipartFileUpload") ? options.getBoolean("multipartFileUpload") : false;
5254
}
5355

5456
}

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

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import android.content.IntentFilter;
88
import android.database.Cursor;
99
import android.net.Uri;
10+
import android.os.Bundle;
1011
import android.util.Base64;
1112

1213
import com.RNFetchBlob.Response.RNFetchBlobDefaultResp;
@@ -18,6 +19,7 @@
1819
import com.facebook.react.bridge.ReadableArray;
1920
import com.facebook.react.bridge.ReadableMap;
2021
import com.facebook.react.bridge.ReadableMapKeySetIterator;
22+
import com.facebook.react.bridge.ReadableNativeArray;
2123
import com.facebook.react.bridge.WritableArray;
2224
import com.facebook.react.bridge.WritableMap;
2325
import com.facebook.react.modules.core.DeviceEventManagerModule;
@@ -165,6 +167,39 @@ public void run() {
165167

166168
}
167169

170+
if(this.options.multipartFileUpload) {
171+
HashMap<String, String> mheaders = new HashMap<>();
172+
// set headers
173+
if (headers != null) {
174+
ReadableMapKeySetIterator it = headers.keySetIterator();
175+
while (it.hasNextKey()) {
176+
String key = it.nextKey();
177+
String value = headers.getString(key);
178+
mheaders.put(key, value);
179+
}
180+
}
181+
182+
Bundle bundle = new Bundle();
183+
bundle.putString("taskId", taskId);
184+
bundle.putString("url", url);
185+
bundle.putSerializable("mheaders", mheaders);
186+
bundle.putSerializable("requestBodyArray", ((ReadableNativeArray)rawRequestBodyArray).toArrayList());
187+
188+
Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
189+
190+
IntentFilter filter = new IntentFilter(RNFetchBlobService.RNFetchBlobServiceBroadcast);
191+
filter.addCategory(RNFetchBlobService.CategoryProgress);
192+
filter.addCategory(RNFetchBlobService.CategorySuccess);
193+
filter.addCategory(RNFetchBlobService.CategoryFail);
194+
appCtx.registerReceiver(this, filter);
195+
196+
Intent intent = new Intent(appCtx, RNFetchBlobService.class);
197+
intent.putExtras(bundle);
198+
appCtx.startService(intent);
199+
200+
return;
201+
}
202+
168203
// find cached result if `key` property exists
169204
String cacheKey = this.taskId;
170205
String ext = this.options.appendExt.isEmpty() ? "" : "." + this.options.appendExt;
@@ -684,8 +719,38 @@ public void onReceive(Context context, Intent intent) {
684719
}
685720

686721
}
722+
} else if (RNFetchBlobService.RNFetchBlobServiceBroadcast.equals(action)) {
723+
String _taskId = intent.getStringExtra(RNFetchBlobService.BroadcastTaskId);
724+
if (this.taskId.equals(_taskId)) {
725+
if (intent.hasCategory(RNFetchBlobService.CategoryProgress)) {
726+
HashMap map = (HashMap) intent.getSerializableExtra(RNFetchBlobService.BroadcastProgressMap);
727+
728+
WritableMap args = Arguments.createMap();
729+
args.putString("taskId", _taskId);
730+
args.putString("written", String.valueOf(map.get(RNFetchBlobService.KeyWritten)));
731+
args.putString("total", String.valueOf(map.get(RNFetchBlobService.KeyTotal)));
732+
733+
// emit event to js context
734+
RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
735+
.emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
736+
} else if (intent.hasCategory(RNFetchBlobService.CategorySuccess)) {
737+
// Could be fail.
738+
try {
739+
byte[] bytes = intent.getByteArrayExtra(RNFetchBlobService.BroadcastMsg);
740+
callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_UTF8, new String(bytes, "UTF-8"));
741+
} catch (IOException e) {
742+
callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
743+
} finally {
744+
// lets unregister.
745+
Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
746+
appCtx.unregisterReceiver(this);
747+
}
748+
} else if (intent.hasCategory(RNFetchBlobService.CategoryFail)) {
749+
callback.invoke("Request failed.", null, null);
750+
Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
751+
appCtx.unregisterReceiver(this);
752+
}
753+
}
687754
}
688755
}
689-
690-
691756
}

ios/RNFetchBlobNetwork.m

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
196196
{
197197
defaultConfigObject.timeoutIntervalForRequest = timeout/1000;
198198
}
199+
200+
defaultConfigObject.sessionSendsLaunchEvents = YES;
201+
199202
defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
200203
session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue];
201204
if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil)
@@ -227,7 +230,13 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
227230
respFile = NO;
228231
}
229232

230-
__block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
233+
__block NSURLSessionDataTask * task;
234+
if (path && req.HTTPMethod == @"POST") {
235+
task = [session uploadTaskWithRequest:req fromFile:path];
236+
} else {
237+
task = [session dataTaskWithRequest:req];
238+
}
239+
231240
[taskTable setObject:task forKey:taskId];
232241
[task resume];
233242

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package com.RNFetchBlob;
2+
3+
import android.app.IntentService;
4+
import android.content.Intent;
5+
import android.net.Uri;
6+
import android.os.Bundle;
7+
import android.text.TextUtils;
8+
9+
import com.facebook.react.modules.network.*;
10+
11+
import java.io.File;
12+
import java.io.IOException;
13+
import java.net.MalformedURLException;
14+
import java.net.URL;
15+
import java.util.ArrayList;
16+
import java.util.HashMap;
17+
import java.util.concurrent.TimeUnit;
18+
19+
import okhttp3.Call;
20+
import okhttp3.Headers;
21+
import okhttp3.MediaType;
22+
import okhttp3.MultipartBody;
23+
import okhttp3.OkHttpClient;
24+
import okhttp3.Request;
25+
import okhttp3.RequestBody;
26+
import okhttp3.Response;
27+
28+
/**
29+
* Created by pkwak on 11/15/16.
30+
* IntentService for POSTing multi-form data. Designed for long-uploads with file
31+
* (but can use data body).
32+
*/
33+
34+
public class RNFetchBlobService extends IntentService implements ProgressListener {
35+
public static String RNFetchBlobServiceBroadcast = "RNFetchBlobServiceBroadcast";
36+
37+
public static String CategorySuccess = "CategorySuccess";
38+
public static String CategoryFail = "CategoryFail";
39+
public static String CategoryProgress = "CategoryProgress";
40+
41+
public static String BroadcastMsg = "BroadcastMsg";
42+
public static String BroadcastProgressMap = "BroadcastProgressMap";
43+
public static String BroadcastTaskId = "BroadcastTaskId";
44+
45+
public static String KeyWritten = "KeyWritten";
46+
public static String KeyTotal = "KeyTotal";
47+
48+
/**
49+
* Creates an IntentService. Invoked by your subclass's constructor.
50+
*/
51+
public RNFetchBlobService() {
52+
super("RNFetchBlobService");
53+
}
54+
55+
private String _taskId = null;
56+
@Override
57+
protected void onHandleIntent(final Intent intent) {
58+
59+
Bundle bundle = intent.getExtras();
60+
_taskId = bundle.getString("taskId");
61+
String url = bundle.getString("url");
62+
HashMap<String, String> mheaders = (HashMap<String, String>)bundle.getSerializable("mheaders");
63+
ArrayList<Object> requestBodyArray = (ArrayList<Object>)bundle.getSerializable("requestBodyArray");
64+
65+
MultipartBody.Builder requestBuilder = new MultipartBody.Builder()
66+
.setType(MultipartBody.FORM);
67+
for(Object bodyPart : requestBodyArray) {
68+
if (bodyPart instanceof HashMap) {
69+
HashMap<String, String> bodyMap = (HashMap<String, String>)bodyPart;
70+
String name = bodyMap.get("name");
71+
String type = bodyMap.get("type");
72+
String filename = bodyMap.get("filename");
73+
String data = bodyMap.get("data");
74+
File file = null;
75+
MediaType mediaType = type != null
76+
? MediaType.parse(type)
77+
: filename == null
78+
? MediaType.parse("text/plain")
79+
: MediaType.parse("application/octet-stream");
80+
if(filename != null && data.startsWith("RNFetchBlob-")) {
81+
try {
82+
String normalizedUri = RNFetchBlobFS.normalizePath(data.replace(RNFetchBlobConst.FILE_PREFIX, ""));
83+
file = new File(String.valueOf(Uri.parse(normalizedUri)));
84+
} catch (Exception e) {
85+
file = null;
86+
}
87+
}
88+
89+
String contentDisposition = "form-data"
90+
+ (!TextUtils.isEmpty(name) ? "; name=" + name : "")
91+
+ (!TextUtils.isEmpty(filename) ? "; filename=" + filename : "");
92+
93+
requestBuilder.addPart(
94+
Headers.of("Content-Disposition", contentDisposition),
95+
file != null
96+
? RequestBody.create(mediaType, file)
97+
: RequestBody.create(mediaType, data)
98+
);
99+
}
100+
}
101+
RequestBody innerRequestBody = requestBuilder.build();
102+
103+
ProgressRequestBody requestBody = new ProgressRequestBody(innerRequestBody, this);
104+
105+
final Request.Builder builder = new Request.Builder();
106+
try {
107+
builder.url(new URL(url));
108+
} catch (MalformedURLException e) {
109+
Intent broadcastIntent = new Intent();
110+
broadcastIntent.setAction(RNFetchBlobServiceBroadcast);
111+
broadcastIntent.addCategory(CategoryFail);
112+
broadcastIntent.putExtra(BroadcastMsg, "Could not create URL : " + e.getMessage().getBytes());
113+
sendBroadcast(broadcastIntent);
114+
return;
115+
}
116+
117+
builder.post(requestBody);
118+
for(String key : mheaders.keySet()) {
119+
builder.addHeader(key, mheaders.get(key));
120+
}
121+
122+
OkHttpClient client = new OkHttpClient.Builder()
123+
.writeTimeout(24, TimeUnit.HOURS)
124+
.readTimeout(24, TimeUnit.HOURS)
125+
.build();
126+
127+
final Call call = client.newCall(builder.build());
128+
call.enqueue(new okhttp3.Callback() {
129+
130+
@Override
131+
public void onFailure(Call call, IOException e) {
132+
Intent broadcastIntent = new Intent();
133+
broadcastIntent.setAction(RNFetchBlobServiceBroadcast);
134+
broadcastIntent.addCategory(CategoryFail);
135+
broadcastIntent.putExtra(BroadcastMsg, e.getMessage().getBytes());
136+
broadcastIntent.putExtra(BroadcastTaskId, _taskId);
137+
sendBroadcast(broadcastIntent);
138+
call.cancel();
139+
}
140+
141+
@Override
142+
public void onResponse(Call call, Response response) throws IOException {
143+
// This is http-response success. Can be 2xx/4xx/5xx/etc.
144+
Intent broadcastIntent = new Intent();
145+
broadcastIntent.setAction(RNFetchBlobServiceBroadcast);
146+
broadcastIntent.addCategory(CategorySuccess);
147+
broadcastIntent.putExtra(BroadcastMsg, response.body().bytes());
148+
broadcastIntent.putExtra(BroadcastTaskId, _taskId);
149+
sendBroadcast(broadcastIntent);
150+
151+
response.body().close();
152+
153+
}
154+
155+
});
156+
}
157+
158+
private int _progressPercent = 0;
159+
private long _lastProgressTime = 0;
160+
@Override
161+
public void onProgress(long bytesWritten, long contentLength, boolean done) {
162+
163+
// no more than once per %
164+
int currentPercent = (int)((bytesWritten * 100) / contentLength);
165+
if (currentPercent <= _progressPercent) {
166+
return;
167+
}
168+
_progressPercent = currentPercent;
169+
170+
// no more than twice a second.
171+
long now = System.currentTimeMillis();
172+
if (_lastProgressTime + 500 > now) {
173+
return;
174+
}
175+
_lastProgressTime = now;
176+
177+
Intent broadcastIntent = new Intent();
178+
broadcastIntent.setAction(RNFetchBlobServiceBroadcast);
179+
broadcastIntent.addCategory(CategoryProgress);
180+
broadcastIntent.putExtra(BroadcastTaskId, _taskId);
181+
HashMap map = new HashMap();
182+
map.put(KeyWritten, Long.valueOf(bytesWritten));
183+
map.put(KeyTotal, Long.valueOf(contentLength));
184+
broadcastIntent.putExtra(BroadcastProgressMap, map);
185+
sendBroadcast(broadcastIntent);
186+
}
187+
}

0 commit comments

Comments
 (0)