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

Commit 298d6ae

Browse files
committed
Add ‘largeFileUpload’ to config, for android in this to spin off IntentService instead.
1 parent 05841bc commit 298d6ae

File tree

4 files changed

+257
-0
lines changed

4 files changed

+257
-0
lines changed

src/android/src/main/AndroidManifest.xml

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

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

1115
</manifest>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class RNFetchBlobConfig {
2020
public long timeout = 60000;
2121
public Boolean increment = false;
2222
public ReadableArray binaryContentTypes = null;
23+
public Boolean largeFileUpload;
2324

2425
RNFetchBlobConfig(ReadableMap options) {
2526
if(options == null)
@@ -46,6 +47,7 @@ public class RNFetchBlobConfig {
4647
if(options.hasKey("timeout")) {
4748
this.timeout = options.getInt("timeout");
4849
}
50+
this.largeFileUpload = options.hasKey("largeFileUpload") ? options.getBoolean("largeFileUpload") : false;
4951
}
5052

5153
}

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

Lines changed: 64 additions & 0 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.largeFileUpload) {
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;
@@ -662,6 +697,35 @@ public void onReceive(Context context, Intent intent) {
662697
}
663698

664699
}
700+
} else if (RNFetchBlobService.RNFetchBlobServiceBroadcast.equals(action)) {
701+
if (intent.hasCategory(RNFetchBlobService.CategoryProgress)) {
702+
HashMap map = (HashMap)intent.getSerializableExtra(RNFetchBlobService.BroadcastProgressMap);
703+
String taskId = (String)map.get(RNFetchBlobService.KeyTaskId);
704+
WritableMap args = Arguments.createMap();
705+
args.putString("taskId", taskId);
706+
args.putString("written", String.valueOf(map.get(RNFetchBlobService.KeyWritten)));
707+
args.putString("total", String.valueOf(map.get(RNFetchBlobService.KeyTotal)));
708+
709+
// emit event to js context
710+
RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
711+
.emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
712+
} else if (intent.hasCategory(RNFetchBlobService.CategorySuccess)) {
713+
// Could be fail.
714+
try {
715+
byte[] bytes = intent.getByteArrayExtra(RNFetchBlobService.BroadcastMsg);
716+
callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_UTF8, new String(bytes, "UTF-8"));
717+
} catch (IOException e) {
718+
callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
719+
} finally {
720+
// lets unregister.
721+
Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
722+
appCtx.unregisterReceiver(this);
723+
}
724+
} else if (intent.hasCategory(RNFetchBlobService.CategoryFail)) {
725+
callback.invoke("Request failed.", null, null);
726+
Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
727+
appCtx.unregisterReceiver(this);
728+
}
665729
}
666730
}
667731

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+
8+
import com.facebook.react.modules.network.*;
9+
10+
import java.io.File;
11+
import java.io.IOException;
12+
import java.net.MalformedURLException;
13+
import java.net.URL;
14+
import java.util.ArrayList;
15+
import java.util.HashMap;
16+
import java.util.concurrent.TimeUnit;
17+
18+
import okhttp3.Call;
19+
import okhttp3.Headers;
20+
import okhttp3.MediaType;
21+
import okhttp3.MultipartBody;
22+
import okhttp3.OkHttpClient;
23+
import okhttp3.Request;
24+
import okhttp3.RequestBody;
25+
import okhttp3.Response;
26+
27+
/**
28+
* Created by pkwak on 11/15/16.
29+
* IntentService for POSTing multi-form data. Designed for long-uploads with file
30+
* (but can use data body).
31+
*/
32+
33+
public class RNFetchBlobService extends IntentService implements ProgressListener {
34+
public static String RNFetchBlobServiceBroadcast = "RNFetchBlobServiceBroadcast";
35+
36+
public static String CategorySuccess = "CategorySuccess";
37+
public static String CategoryFail = "CategoryFail";
38+
public static String CategoryProgress = "CategoryProgress";
39+
40+
public static String BroadcastMsg = "BroadcastMsg";
41+
public static String BroadcastProgressMap = "BroadcastProgressMap";
42+
43+
public static String KeyTaskId = "KeyTaskId";
44+
public static String KeyWritten = "KeyWritten";
45+
public static String KeyTotal = "KeyTotal";
46+
47+
/**
48+
* Creates an IntentService. Invoked by your subclass's constructor.
49+
*/
50+
public RNFetchBlobService() {
51+
super("RNFetchBlobService");
52+
}
53+
54+
private String _taskId = null;
55+
@Override
56+
protected void onHandleIntent(final Intent intent) {
57+
58+
Bundle bundle = intent.getExtras();
59+
_taskId = bundle.getString("taskId");
60+
String url = bundle.getString("url");
61+
HashMap<String, String> mheaders = (HashMap<String, String>)bundle.getSerializable("mheaders");
62+
ArrayList<Object> requestBodyArray = (ArrayList<Object>)bundle.getSerializable("requestBodyArray");
63+
64+
MultipartBody.Builder requestBuilder = new MultipartBody.Builder()
65+
.setType(MultipartBody.FORM);
66+
for(Object bodyPart : requestBodyArray) {
67+
if (bodyPart instanceof HashMap) {
68+
HashMap<String, String> bodyMap = (HashMap<String, String>)bodyPart;
69+
String name = bodyMap.get("name");
70+
String type = bodyMap.get("type");
71+
String filename = bodyMap.get("filename");
72+
String data = bodyMap.get("data");
73+
File file = null;
74+
MediaType mediaType = type != null
75+
? MediaType.parse(type)
76+
: filename == null
77+
? MediaType.parse("text/plain")
78+
: MediaType.parse("application/octet-stream");
79+
if(filename != null && data.startsWith("RNFetchBlob-")) {
80+
try {
81+
String newData = data.replace(RNFetchBlobConst.FILE_PREFIX, "");
82+
String normalizedUri = RNFetchBlobFS.normalizePath(newData);
83+
file = new File(String.valueOf(Uri.parse(normalizedUri)));
84+
} catch (Exception e) {
85+
//
86+
}
87+
}
88+
89+
String contentDisposition = "form-data"
90+
+ (name != null && name.length() > 0 ? "; name=" + name : "")
91+
+ (filename != null && filename.length() > 0 ? "; 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+
sendBroadcast(broadcastIntent);
137+
call.cancel();
138+
}
139+
140+
@Override
141+
public void onResponse(Call call, Response response) throws IOException {
142+
// This is http-response success. Can be 2xx/4xx/5xx/etc.
143+
Intent broadcastIntent = new Intent();
144+
broadcastIntent.setAction(RNFetchBlobServiceBroadcast);
145+
broadcastIntent.addCategory(CategorySuccess);
146+
broadcastIntent.putExtra(BroadcastMsg, response.body().bytes());
147+
sendBroadcast(broadcastIntent);
148+
149+
response.body().close();
150+
151+
}
152+
153+
});
154+
}
155+
156+
private int _progressPercent = 0;
157+
private long _lastProgressTime = 0;
158+
@Override
159+
public void onProgress(long bytesWritten, long contentLength, boolean done) {
160+
161+
// no more than once per %
162+
int currentPercent = (int)((bytesWritten * 100) / contentLength);
163+
if (currentPercent <= _progressPercent) {
164+
return;
165+
}
166+
_progressPercent = currentPercent;
167+
168+
// no more than twice a second.
169+
long now = System.currentTimeMillis();
170+
if (_lastProgressTime + 500 > now) {
171+
return;
172+
}
173+
_lastProgressTime = now;
174+
175+
Intent broadcastIntent = new Intent();
176+
broadcastIntent.setAction(RNFetchBlobServiceBroadcast);
177+
broadcastIntent.addCategory(CategoryProgress);
178+
HashMap map = new HashMap();
179+
map.put(KeyWritten, Long.valueOf(bytesWritten));
180+
map.put(KeyTotal, Long.valueOf(contentLength));
181+
map.put(KeyTaskId, this._taskId);
182+
broadcastIntent.putExtra(BroadcastProgressMap, map);
183+
sendBroadcast(broadcastIntent);
184+
}
185+
}
186+
187+

0 commit comments

Comments
 (0)