Browse Source

Fix XMLHttpRequest auto strategy on Android

Ben Hsieh 7 years ago
parent
commit
15d4068452

+ 5
- 1
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java View File

@@ -1,5 +1,6 @@
1 1
 package com.RNFetchBlob;
2 2
 
3
+import com.facebook.react.bridge.ReadableArray;
3 4
 import com.facebook.react.bridge.ReadableMap;
4 5
 
5 6
 import java.util.HashMap;
@@ -17,7 +18,8 @@ public class RNFetchBlobConfig {
17 18
     public String key;
18 19
     public String mime;
19 20
     public Boolean auto;
20
-    public long timeout = 30000;
21
+    public long timeout = -1;
22
+    public ReadableArray binaryContentTypes = null;
21 23
 
22 24
     RNFetchBlobConfig(ReadableMap options) {
23 25
         if(options == null)
@@ -29,6 +31,8 @@ public class RNFetchBlobConfig {
29 31
         if(options.hasKey("addAndroidDownloads")) {
30 32
             this.addAndroidDownloads = options.getMap("addAndroidDownloads");
31 33
         }
34
+        if(options.hasKey("binaryContentTypes"))
35
+            this.binaryContentTypes = options.getArray("binaryContentTypes");
32 36
         this.key = options.hasKey("key") ? options.getString("key") : null;
33 37
         this.mime = options.hasKey("contentType") ? options.getString("contentType") : null;
34 38
         this.auto = options.hasKey("auto") ? options.getBoolean("auto") : false;

+ 1
- 1
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java View File

@@ -237,7 +237,7 @@ public class RNFetchBlobFS {
237 237
      * @return
238 238
      */
239 239
     static public String getTmpPath(ReactApplicationContext ctx, String taskId) {
240
-        return ctx.getFilesDir() + "/RNFetchBlobTmp_" + taskId;
240
+        return RNFetchBlob.RCTContext.getFilesDir() + "/RNFetchBlobTmp_" + taskId;
241 241
     }
242 242
 
243 243
     /**

+ 52
- 29
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java View File

@@ -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) {

+ 6
- 0
src/index.js View File

@@ -247,6 +247,12 @@ class FetchBlobResponse {
247 247
             reject(err)
248 248
           }
249 249
         }
250
+        else {
251
+          polyfill.Blob.build(wrap(this.data))
252
+          .then((b) => {
253
+            resolve(b)
254
+          })
255
+        }
250 256
       })
251 257
     }
252 258
     /**