Selaa lähdekoodia

Fix XMLHttpRequest auto strategy on Android

Ben Hsieh 7 vuotta sitten
vanhempi
commit
15d4068452

+ 5
- 1
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java Näytä tiedosto

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

+ 1
- 1
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java Näytä tiedosto

237
      * @return
237
      * @return
238
      */
238
      */
239
     static public String getTmpPath(ReactApplicationContext ctx, String taskId) {
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 Näytä tiedosto

275
 
275
 
276
             final Request req = builder.build();
276
             final Request req = builder.build();
277
 
277
 
278
-//          create response handler
278
+            // Create response body depends on the responseType
279
             clientBuilder.addInterceptor(new Interceptor() {
279
             clientBuilder.addInterceptor(new Interceptor() {
280
                 @Override
280
                 @Override
281
                 public Response intercept(Chain chain) throws IOException {
281
                 public Response intercept(Chain chain) throws IOException {
304
                                 break;
304
                                 break;
305
                         }
305
                         }
306
                         return originalResponse.newBuilder().body(extended).build();
306
                         return originalResponse.newBuilder().body(extended).build();
307
-                    } catch(SocketTimeoutException ex) {
307
+                    } catch(Exception ex) {
308
                         timeout = true;
308
                         timeout = true;
309
                     }
309
                     }
310
                     return chain.proceed(chain.request());
310
                     return chain.proceed(chain.request());
311
                 }
311
                 }
312
             });
312
             });
313
 
313
 
314
+
314
             if(options.timeout > 0) {
315
             if(options.timeout > 0) {
315
                 clientBuilder.connectTimeout(options.timeout, TimeUnit.MILLISECONDS);
316
                 clientBuilder.connectTimeout(options.timeout, TimeUnit.MILLISECONDS);
316
                 clientBuilder.readTimeout(options.timeout, TimeUnit.MILLISECONDS);
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
             clientBuilder.connectionPool(pool);
320
             clientBuilder.connectionPool(pool);
323
             clientBuilder.retryOnConnectionFailure(false);
321
             clientBuilder.retryOnConnectionFailure(false);
322
+            clientBuilder.followRedirects(true);
323
+
324
             OkHttpClient client = clientBuilder.build();
324
             OkHttpClient client = clientBuilder.build();
325
             Call call =  client.newCall(req);
325
             Call call =  client.newCall(req);
326
             taskTable.put(taskId, call);
326
             taskTable.put(taskId, call);
328
 
328
 
329
                 @Override
329
                 @Override
330
                 public void onFailure(Call call, IOException e) {
330
                 public void onFailure(Call call, IOException e) {
331
+                    cancelTask(taskId);
331
                     if(respInfo == null) {
332
                     if(respInfo == null) {
332
                         respInfo = Arguments.createMap();
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
                     if(e.getClass().equals(SocketTimeoutException.class)) {
337
                     if(e.getClass().equals(SocketTimeoutException.class)) {
337
                         respInfo.putBoolean("timeout", true);
338
                         respInfo.putBoolean("timeout", true);
338
                         callback.invoke("request timed out.", respInfo, null);
339
                         callback.invoke("request timed out.", respInfo, null);
345
                 @Override
346
                 @Override
346
                 public void onResponse(Call call, Response response) throws IOException {
347
                 public void onResponse(Call call, Response response) throws IOException {
347
                     ReadableMap notifyConfig = options.addAndroidDownloads;
348
                     ReadableMap notifyConfig = options.addAndroidDownloads;
348
-                    respInfo = getResponseInfo(response);
349
                     // Download manager settings
349
                     // Download manager settings
350
                     if(notifyConfig != null ) {
350
                     if(notifyConfig != null ) {
351
                         String title = "", desc = "", mime = "text/plain";
351
                         String title = "", desc = "", mime = "text/plain";
371
         } catch (Exception error) {
371
         } catch (Exception error) {
372
             error.printStackTrace();
372
             error.printStackTrace();
373
             taskTable.remove(taskId);
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
      * @param resp OkHttp response object
392
      * @param resp OkHttp response object
393
      */
393
      */
394
     private void done(Response resp) {
394
     private void done(Response resp) {
395
-        emitStateEvent(getResponseInfo(resp));
395
+        boolean isBlobResp = isBlobResponse(resp);
396
+        emitStateEvent(getResponseInfo(resp, isBlobResp));
396
         switch (responseType) {
397
         switch (responseType) {
397
             case KeepInMemory:
398
             case KeepInMemory:
398
                 try {
399
                 try {
399
                     // For XMLHttpRequest, automatic response data storing strategy, when response
400
                     // For XMLHttpRequest, automatic response data storing strategy, when response
400
                     // header is not `application/json` or `text/plain`, write response data to
401
                     // header is not `application/json` or `text/plain`, write response data to
401
                     // file system.
402
                     // file system.
402
-                    if(isBlobResponse(resp) && options.auto == true) {
403
+                    if(isBlobResp && options.auto == true) {
403
                         String dest = RNFetchBlobFS.getTmpPath(ctx, taskId);
404
                         String dest = RNFetchBlobFS.getTmpPath(ctx, taskId);
404
                         InputStream ins = resp.body().byteStream();
405
                         InputStream ins = resp.body().byteStream();
405
                         FileOutputStream os = new FileOutputStream(new File(dest));
406
                         FileOutputStream os = new FileOutputStream(new File(dest));
412
                         }
413
                         }
413
                         ins.close();
414
                         ins.close();
414
                         os.close();
415
                         os.close();
415
-                        WritableMap info = getResponseInfo(resp);
416
-                        callback.invoke(null, info, dest);
416
+                        callback.invoke(null, null, dest);
417
                     }
417
                     }
418
                     else {
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
                         String utf8Str;
422
                         String utf8Str;
423
                         byte[] b = resp.body().bytes();
423
                         byte[] b = resp.body().bytes();
424
                         CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
424
                         CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
425
                         try {
425
                         try {
426
                             encoder.encode(ByteBuffer.wrap(b).asCharBuffer());
426
                             encoder.encode(ByteBuffer.wrap(b).asCharBuffer());
427
                             // if the data can be encoded to UTF8 append URL encode
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
                         // This usually mean the data is binary data
430
                         // This usually mean the data is binary data
431
                         catch(CharacterCodingException e) {
431
                         catch(CharacterCodingException e) {
432
 
432
 
433
                         }
433
                         }
434
                         finally {
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
                 } catch (IOException e) {
438
                 } catch (IOException e) {
441
                 break;
441
                 break;
442
             case FileStorage:
442
             case FileStorage:
443
                 try{
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
                     resp.body().bytes();
447
                     resp.body().bytes();
445
                 } catch (Exception ignored) {
448
                 } catch (Exception ignored) {
446
 
449
 
447
                 }
450
                 }
448
-                callback.invoke(null, getResponseInfo(resp), this.destPath);
451
+                callback.invoke(null, null, this.destPath);
449
                 break;
452
                 break;
450
             default:
453
             default:
451
                 try {
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
                 } catch (IOException e) {
456
                 } catch (IOException e) {
454
                     callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
457
                     callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
455
                 }
458
                 }
458
         removeTaskInfo();
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
     public static boolean isReportProgress(String taskId) {
469
     public static boolean isReportProgress(String taskId) {
462
         if(!progressReport.containsKey(taskId)) return false;
470
         if(!progressReport.containsKey(taskId)) return false;
463
         return progressReport.get(taskId);
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
     public static boolean isReportUploadProgress(String taskId) {
479
     public static boolean isReportUploadProgress(String taskId) {
467
         if(!uploadProgressReport.containsKey(taskId)) return false;
480
         if(!uploadProgressReport.containsKey(taskId)) return false;
468
         return uploadProgressReport.get(taskId);
481
         return uploadProgressReport.get(taskId);
469
     }
482
     }
470
 
483
 
471
-    private WritableMap getResponseInfo(Response resp) {
484
+    private WritableMap getResponseInfo(Response resp, boolean isBlobResp) {
472
         WritableMap info = Arguments.createMap();
485
         WritableMap info = Arguments.createMap();
473
         info.putInt("status", resp.code());
486
         info.putInt("status", resp.code());
474
         info.putString("state", "2");
487
         info.putString("state", "2");
480
         }
493
         }
481
         info.putMap("headers", headers);
494
         info.putMap("headers", headers);
482
         Headers h = resp.headers();
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
             info.putString("respType", "text");
500
             info.putString("respType", "text");
485
         }
501
         }
486
         else if(getHeaderIgnoreCases(h, "content-type").contains("application/json")) {
502
         else if(getHeaderIgnoreCases(h, "content-type").contains("application/json")) {
487
             info.putString("respType", "json");
503
             info.putString("respType", "json");
488
         }
504
         }
489
-        else if(getHeaderIgnoreCases(h, "content-type").length() < 1) {
490
-            info.putString("respType", "blob");
491
-        }
492
         else {
505
         else {
493
-            info.putString("respType", "text");
506
+            info.putString("respType", "");
494
         }
507
         }
495
         return info;
508
         return info;
496
     }
509
     }
497
 
510
 
498
     private boolean isBlobResponse(Response resp) {
511
     private boolean isBlobResponse(Response resp) {
499
         Headers h = resp.headers();
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
     private String getHeaderIgnoreCases(Headers headers, String field) {
528
     private String getHeaderIgnoreCases(Headers headers, String field) {

+ 6
- 0
src/index.js Näytä tiedosto

247
             reject(err)
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
     /**