|
@@ -275,7 +275,7 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
275
|
275
|
|
276
|
276
|
final Request req = builder.build();
|
277
|
277
|
|
278
|
|
-// create response handler
|
|
278
|
+ // Create response body depends on the responseType
|
279
|
279
|
clientBuilder.addInterceptor(new Interceptor() {
|
280
|
280
|
@Override
|
281
|
281
|
public Response intercept(Chain chain) throws IOException {
|
|
@@ -304,23 +304,23 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
304
|
304
|
break;
|
305
|
305
|
}
|
306
|
306
|
return originalResponse.newBuilder().body(extended).build();
|
307
|
|
- } catch(SocketTimeoutException ex) {
|
|
307
|
+ } catch(Exception ex) {
|
308
|
308
|
timeout = true;
|
309
|
309
|
}
|
310
|
310
|
return chain.proceed(chain.request());
|
311
|
311
|
}
|
312
|
312
|
});
|
313
|
313
|
|
|
314
|
+
|
314
|
315
|
if(options.timeout > 0) {
|
315
|
316
|
clientBuilder.connectTimeout(options.timeout, TimeUnit.MILLISECONDS);
|
316
|
317
|
clientBuilder.readTimeout(options.timeout, TimeUnit.MILLISECONDS);
|
317
|
318
|
}
|
318
|
|
- else {
|
319
|
|
- clientBuilder.connectTimeout(-1, TimeUnit.MILLISECONDS);
|
320
|
|
- clientBuilder.readTimeout(-1, TimeUnit.MILLISECONDS);
|
321
|
|
- }
|
|
319
|
+
|
322
|
320
|
clientBuilder.connectionPool(pool);
|
323
|
321
|
clientBuilder.retryOnConnectionFailure(false);
|
|
322
|
+ clientBuilder.followRedirects(true);
|
|
323
|
+
|
324
|
324
|
OkHttpClient client = clientBuilder.build();
|
325
|
325
|
Call call = client.newCall(req);
|
326
|
326
|
taskTable.put(taskId, call);
|
|
@@ -328,11 +328,12 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
328
|
328
|
|
329
|
329
|
@Override
|
330
|
330
|
public void onFailure(Call call, IOException e) {
|
|
331
|
+ cancelTask(taskId);
|
331
|
332
|
if(respInfo == null) {
|
332
|
333
|
respInfo = Arguments.createMap();
|
333
|
334
|
}
|
334
|
335
|
|
335
|
|
- // check if this error caused by timeout
|
|
336
|
+ // check if this error caused by socket timeout
|
336
|
337
|
if(e.getClass().equals(SocketTimeoutException.class)) {
|
337
|
338
|
respInfo.putBoolean("timeout", true);
|
338
|
339
|
callback.invoke("request timed out.", respInfo, null);
|
|
@@ -345,7 +346,6 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
345
|
346
|
@Override
|
346
|
347
|
public void onResponse(Call call, Response response) throws IOException {
|
347
|
348
|
ReadableMap notifyConfig = options.addAndroidDownloads;
|
348
|
|
- respInfo = getResponseInfo(response);
|
349
|
349
|
// Download manager settings
|
350
|
350
|
if(notifyConfig != null ) {
|
351
|
351
|
String title = "", desc = "", mime = "text/plain";
|
|
@@ -371,7 +371,7 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
371
|
371
|
} catch (Exception error) {
|
372
|
372
|
error.printStackTrace();
|
373
|
373
|
taskTable.remove(taskId);
|
374
|
|
- callback.invoke("RNFetchBlob request error: " + error.getMessage() + error.getCause(), this.respInfo);
|
|
374
|
+ callback.invoke("RNFetchBlob request error: " + error.getMessage() + error.getCause());
|
375
|
375
|
}
|
376
|
376
|
}
|
377
|
377
|
|
|
@@ -392,14 +392,15 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
392
|
392
|
* @param resp OkHttp response object
|
393
|
393
|
*/
|
394
|
394
|
private void done(Response resp) {
|
395
|
|
- emitStateEvent(getResponseInfo(resp));
|
|
395
|
+ boolean isBlobResp = isBlobResponse(resp);
|
|
396
|
+ emitStateEvent(getResponseInfo(resp, isBlobResp));
|
396
|
397
|
switch (responseType) {
|
397
|
398
|
case KeepInMemory:
|
398
|
399
|
try {
|
399
|
400
|
// For XMLHttpRequest, automatic response data storing strategy, when response
|
400
|
401
|
// header is not `application/json` or `text/plain`, write response data to
|
401
|
402
|
// file system.
|
402
|
|
- if(isBlobResponse(resp) && options.auto == true) {
|
|
403
|
+ if(isBlobResp && options.auto == true) {
|
403
|
404
|
String dest = RNFetchBlobFS.getTmpPath(ctx, taskId);
|
404
|
405
|
InputStream ins = resp.body().byteStream();
|
405
|
406
|
FileOutputStream os = new FileOutputStream(new File(dest));
|
|
@@ -412,27 +413,26 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
412
|
413
|
}
|
413
|
414
|
ins.close();
|
414
|
415
|
os.close();
|
415
|
|
- WritableMap info = getResponseInfo(resp);
|
416
|
|
- callback.invoke(null, info, dest);
|
|
416
|
+ callback.invoke(null, null, dest);
|
417
|
417
|
}
|
418
|
418
|
else {
|
419
|
|
- // we should check if the response data is a UTF8 string, because BASE64
|
420
|
|
- // encoding will somehow break the UTF8 string format. In order to encode
|
421
|
|
- // UTF8 string correctly, we should do URL encoding before BASE64.
|
|
419
|
+ // #73 Check if the response data contains valid UTF8 string, since BASE64
|
|
420
|
+ // encoding will somehow break the UTF8 string format, to encode UTF8
|
|
421
|
+ // string correctly, we should do URL encoding before BASE64.
|
422
|
422
|
String utf8Str;
|
423
|
423
|
byte[] b = resp.body().bytes();
|
424
|
424
|
CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
|
425
|
425
|
try {
|
426
|
426
|
encoder.encode(ByteBuffer.wrap(b).asCharBuffer());
|
427
|
427
|
// if the data can be encoded to UTF8 append URL encode
|
428
|
|
- b = URLEncoder.encode(new String(b), "UTF-8").getBytes();
|
|
428
|
+ b = URLEncoder.encode(new String(b), "UTF-8").replace("+", "%20").getBytes();
|
429
|
429
|
}
|
430
|
430
|
// This usually mean the data is binary data
|
431
|
431
|
catch(CharacterCodingException e) {
|
432
|
432
|
|
433
|
433
|
}
|
434
|
434
|
finally {
|
435
|
|
- callback.invoke(null, getResponseInfo(resp), android.util.Base64.encodeToString(b, Base64.NO_WRAP));
|
|
435
|
+ callback.invoke(null, null, android.util.Base64.encodeToString(b, Base64.NO_WRAP));
|
436
|
436
|
}
|
437
|
437
|
}
|
438
|
438
|
} catch (IOException e) {
|
|
@@ -441,15 +441,18 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
441
|
441
|
break;
|
442
|
442
|
case FileStorage:
|
443
|
443
|
try{
|
|
444
|
+ // In order to write response data to `destPath` we have to invoke this method.
|
|
445
|
+ // It uses customized response body which is able to report download progress
|
|
446
|
+ // and write response data to destination path.
|
444
|
447
|
resp.body().bytes();
|
445
|
448
|
} catch (Exception ignored) {
|
446
|
449
|
|
447
|
450
|
}
|
448
|
|
- callback.invoke(null, getResponseInfo(resp), this.destPath);
|
|
451
|
+ callback.invoke(null, null, this.destPath);
|
449
|
452
|
break;
|
450
|
453
|
default:
|
451
|
454
|
try {
|
452
|
|
- callback.invoke(null, getResponseInfo(resp), new String(resp.body().bytes(), "UTF-8"));
|
|
455
|
+ callback.invoke(null, null, new String(resp.body().bytes(), "UTF-8"));
|
453
|
456
|
} catch (IOException e) {
|
454
|
457
|
callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
|
455
|
458
|
}
|
|
@@ -458,17 +461,27 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
458
|
461
|
removeTaskInfo();
|
459
|
462
|
}
|
460
|
463
|
|
|
464
|
+ /**
|
|
465
|
+ * Invoke this method to enable download progress reporting.
|
|
466
|
+ * @param taskId Task ID of the HTTP task.
|
|
467
|
+ * @return Task ID of the target task
|
|
468
|
+ */
|
461
|
469
|
public static boolean isReportProgress(String taskId) {
|
462
|
470
|
if(!progressReport.containsKey(taskId)) return false;
|
463
|
471
|
return progressReport.get(taskId);
|
464
|
472
|
}
|
465
|
473
|
|
|
474
|
+ /**
|
|
475
|
+ * Invoke this method to enable download progress reporting.
|
|
476
|
+ * @param taskId Task ID of the HTTP task.
|
|
477
|
+ * @return Task ID of the target task
|
|
478
|
+ */
|
466
|
479
|
public static boolean isReportUploadProgress(String taskId) {
|
467
|
480
|
if(!uploadProgressReport.containsKey(taskId)) return false;
|
468
|
481
|
return uploadProgressReport.get(taskId);
|
469
|
482
|
}
|
470
|
483
|
|
471
|
|
- private WritableMap getResponseInfo(Response resp) {
|
|
484
|
+ private WritableMap getResponseInfo(Response resp, boolean isBlobResp) {
|
472
|
485
|
WritableMap info = Arguments.createMap();
|
473
|
486
|
info.putInt("status", resp.code());
|
474
|
487
|
info.putString("state", "2");
|
|
@@ -480,26 +493,36 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
480
|
493
|
}
|
481
|
494
|
info.putMap("headers", headers);
|
482
|
495
|
Headers h = resp.headers();
|
483
|
|
- if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/")) {
|
|
496
|
+ if(isBlobResp) {
|
|
497
|
+ info.putString("respType", "blob");
|
|
498
|
+ }
|
|
499
|
+ else if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/")) {
|
484
|
500
|
info.putString("respType", "text");
|
485
|
501
|
}
|
486
|
502
|
else if(getHeaderIgnoreCases(h, "content-type").contains("application/json")) {
|
487
|
503
|
info.putString("respType", "json");
|
488
|
504
|
}
|
489
|
|
- else if(getHeaderIgnoreCases(h, "content-type").length() < 1) {
|
490
|
|
- info.putString("respType", "blob");
|
491
|
|
- }
|
492
|
505
|
else {
|
493
|
|
- info.putString("respType", "text");
|
|
506
|
+ info.putString("respType", "");
|
494
|
507
|
}
|
495
|
508
|
return info;
|
496
|
509
|
}
|
497
|
510
|
|
498
|
511
|
private boolean isBlobResponse(Response resp) {
|
499
|
512
|
Headers h = resp.headers();
|
500
|
|
- boolean isText = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/");
|
501
|
|
- boolean isJSON = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("application/json");
|
502
|
|
- return !(isJSON || isText);
|
|
513
|
+ String ctype = getHeaderIgnoreCases(h, "Content-Type");
|
|
514
|
+ boolean isText = !ctype.equalsIgnoreCase("text/");
|
|
515
|
+ boolean isJSON = !ctype.equalsIgnoreCase("application/json");
|
|
516
|
+ boolean isCustomBinary = false;
|
|
517
|
+ if(options.binaryContentTypes != null) {
|
|
518
|
+ for(int i = 0; i< options.binaryContentTypes.size();i++) {
|
|
519
|
+ if(ctype.toLowerCase().contains(options.binaryContentTypes.getString(i).toLowerCase())) {
|
|
520
|
+ isCustomBinary = true;
|
|
521
|
+ break;
|
|
522
|
+ }
|
|
523
|
+ }
|
|
524
|
+ }
|
|
525
|
+ return (!(isJSON || isText)) || isCustomBinary;
|
503
|
526
|
}
|
504
|
527
|
|
505
|
528
|
private String getHeaderIgnoreCases(Headers headers, String field) {
|