Browse Source

Change Android HTTP library to OkHttp #25, implement Android upload progress #49

Ben Hsieh 8 years ago
parent
commit
3e75d6f384

+ 1
- 0
src/android/build.gradle View File

@@ -35,4 +35,5 @@ android {
35 35
 dependencies {
36 36
     compile 'com.facebook.react:react-native:+'
37 37
     compile 'com.loopj.android:android-async-http:1.4.9'
38
+    compile 'com.squareup.okhttp3:okhttp:3.4.1'
38 39
 }

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

@@ -152,12 +152,12 @@ public class RNFetchBlob extends ReactContextBaseJavaModule {
152 152
 
153 153
     @ReactMethod
154 154
     public void fetchBlob(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, final Callback callback) {
155
-        new RNFetchBlobReq(this.getReactApplicationContext(), options, taskId, method, url, headers, body, callback).run();
155
+        new RNFetchBlobReq(options, taskId, method, url, headers, body, null, callback).run();
156 156
     }
157 157
 
158 158
     @ReactMethod
159 159
     public void fetchBlobForm(ReadableMap options, String taskId, String method, String url, ReadableMap headers, ReadableArray body, final Callback callback) {
160
-        new RNFetchBlobReq(this.getReactApplicationContext(), options, taskId, method, url, headers, body, callback).run();
160
+        new RNFetchBlobReq(options, taskId, method, url, headers, null, body, callback).run();
161 161
     }
162 162
 
163 163
 }

+ 0
- 55
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBinaryHandler.java View File

@@ -1,55 +0,0 @@
1
-package com.RNFetchBlob;
2
-
3
-import com.facebook.react.bridge.Callback;
4
-import com.facebook.react.bridge.WritableMap;
5
-import com.facebook.react.bridge.Arguments;
6
-import com.facebook.react.bridge.ReactContext;
7
-import com.facebook.react.modules.core.DeviceEventManagerModule;
8
-import com.loopj.android.http.AsyncHttpResponseHandler;
9
-import com.loopj.android.http.Base64;
10
-import com.loopj.android.http.FileAsyncHttpResponseHandler;
11
-
12
-import java.io.File;
13
-
14
-import cz.msebera.android.httpclient.Header;
15
-
16
-public class RNFetchBlobBinaryHandler extends AsyncHttpResponseHandler {
17
-
18
-    Callback onResponse;
19
-    ReactContext mCtx;
20
-    String mTaskId;
21
-
22
-    RNFetchBlobBinaryHandler(ReactContext ctx, String taskId, Callback onResponse) {
23
-
24
-        this.onResponse = onResponse;
25
-        this.mTaskId = taskId;
26
-        this.mCtx = ctx;
27
-    }
28
-
29
-    @Override
30
-    public void onSuccess(int statusCode, Header[] headers, byte[] binaryData) {
31
-        String value = Base64.encodeToString(binaryData, Base64.NO_WRAP);
32
-        this.onResponse.invoke(null, value);
33
-    }
34
-
35
-    @Override
36
-    public void onProgress(long bytesWritten, long totalSize) {
37
-        super.onProgress(bytesWritten, totalSize);
38
-
39
-        // on progress, emit RNFetchBlobProgress event with ticketId, bytesWritten, and totalSize
40
-        WritableMap args = Arguments.createMap();
41
-        args.putString("taskId", this.mTaskId);
42
-        args.putString("written", String.valueOf(bytesWritten));
43
-        args.putString("total", String.valueOf(totalSize));
44
-
45
-        // emit event to js context
46
-        this.mCtx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
47
-                .emit("RNFetchBlobProgress", args);
48
-    }
49
-
50
-    @Override
51
-    public void onFailure(final int statusCode, final Header[] headers, byte[] binaryData, final Throwable error) {
52
-        this.onResponse.invoke(statusCode, error.getMessage()+ ", "+ error.getCause());
53
-    }
54
-
55
-}

+ 95
- 0
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java View File

@@ -0,0 +1,95 @@
1
+package com.RNFetchBlob;
2
+
3
+import com.facebook.react.bridge.Arguments;
4
+import com.facebook.react.bridge.WritableMap;
5
+import com.facebook.react.modules.core.DeviceEventManagerModule;
6
+
7
+import java.io.IOException;
8
+import java.io.InputStream;
9
+
10
+import okhttp3.MediaType;
11
+import okhttp3.RequestBody;
12
+import okio.Buffer;
13
+import okio.BufferedSink;
14
+import okio.ForwardingSink;
15
+import okio.Okio;
16
+import okio.Sink;
17
+
18
+/**
19
+ * Created by wkh237on 2016/7/11.
20
+ */
21
+public class RNFetchBlobBody extends RequestBody{
22
+
23
+    InputStream requestStream;
24
+    long contentLength;
25
+    RequestBody originalBody;
26
+    String mTaskId;
27
+    RNFetchBlobReq.RequestType requestType;
28
+
29
+    public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, RequestBody body, InputStream stream, long size) {
30
+        this.mTaskId = taskId;
31
+        originalBody = body;
32
+        requestStream = stream;
33
+        contentLength = size;
34
+        requestType = type;
35
+    }
36
+
37
+    @Override
38
+    public MediaType contentType() {
39
+        return originalBody.contentType();
40
+    }
41
+
42
+    @Override
43
+    public void writeTo(BufferedSink sink) throws IOException {
44
+
45
+        ProgressReportingSource source = new ProgressReportingSource(sink, mTaskId, contentLength());
46
+        BufferedSink buffer = Okio.buffer(source);
47
+        switch (requestType) {
48
+            case Form:
49
+                originalBody.writeTo(buffer);
50
+                break;
51
+            case SingleFile:
52
+                byte [] chunk = new byte[10240];
53
+                int cursor = requestStream.read(chunk, 0, 10240);
54
+                while(cursor > 0) {
55
+                    cursor = requestStream.read(chunk, 0, 10240);
56
+                    buffer.write(chunk);
57
+                }
58
+                requestStream.close();
59
+                break;
60
+            case Others:
61
+                originalBody.writeTo(buffer);
62
+                break;
63
+        }
64
+        buffer.flush();
65
+    }
66
+
67
+    private final class ProgressReportingSource extends ForwardingSink {
68
+
69
+        private long bytesWritten = 0;
70
+        private String mTaskId;
71
+        private long mContentLength ;
72
+
73
+        public ProgressReportingSource (Sink delegate, String taskId, long contentLength) {
74
+            super(delegate);
75
+            this.mTaskId = taskId;
76
+            this.mContentLength = contentLength;
77
+        }
78
+
79
+        @Override
80
+        public void write(Buffer source, long byteCount) throws IOException {
81
+            super.write(source, byteCount);
82
+            // on progress, emit RNFetchBlobProgress upload progress event with ticketId,
83
+            // bytesWritten, and totalSize
84
+            bytesWritten += byteCount;
85
+            WritableMap args = Arguments.createMap();
86
+            args.putString("taskId", mTaskId);
87
+            args.putString("written", String.valueOf(bytesWritten));
88
+            args.putString("total", String.valueOf(mContentLength ));
89
+
90
+            // emit event to js context
91
+            RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
92
+                    .emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
93
+        }
94
+    }
95
+}

+ 2
- 0
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java View File

@@ -15,6 +15,7 @@ public class RNFetchBlobConfig {
15 15
     public ReadableMap addAndroidDownloads;
16 16
     public Boolean trusty;
17 17
     public String key;
18
+    public String mime;
18 19
 
19 20
     RNFetchBlobConfig(ReadableMap options) {
20 21
         if(options == null)
@@ -27,6 +28,7 @@ public class RNFetchBlobConfig {
27 28
             this.addAndroidDownloads = options.getMap("addAndroidDownloads");
28 29
         }
29 30
         this.key = options.hasKey("key") ? options.getString("key") : null;
31
+        this.mime = options.hasKey("contentType") ? options.getString("contentType") : null;
30 32
     }
31 33
 
32 34
 }

+ 17
- 0
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConst.java View File

@@ -0,0 +1,17 @@
1
+package com.RNFetchBlob;
2
+
3
+import okhttp3.MediaType;
4
+
5
+/**
6
+ * Created by wkh237 on 2016/7/11.
7
+ */
8
+public class RNFetchBlobConst {
9
+    public static final String EVENT_UPLOAD_PROGRESS = "RNFetchBlobProgress-upload";
10
+    public static final String EVENT_PROGRESS = "RNFetchBlobProgress";
11
+    public static final String FILE_PREFIX = "RNFetchBlob-file://";
12
+    public static final MediaType MIME_OCTET = MediaType.parse("application/octet-stream");
13
+    public static final MediaType MIME_MULTIPART = MediaType.parse("multipart/form-data");
14
+    public static final String FILE_PREFIX_BUNDLE_ASSET = "bundle-assets://";
15
+    public static final String FILE_PREFIX_CONTENT = "content://";
16
+
17
+}

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

@@ -1,10 +1,7 @@
1 1
 package com.RNFetchBlob;
2 2
 
3
-import android.app.Application;
4 3
 import android.content.CursorLoader;
5
-import android.content.Intent;
6 4
 import android.content.res.AssetFileDescriptor;
7
-import android.content.res.AssetManager;
8 5
 import android.database.Cursor;
9 6
 import android.media.MediaScannerConnection;
10 7
 import android.net.Uri;
@@ -17,7 +14,6 @@ import com.facebook.react.bridge.Callback;
17 14
 import com.facebook.react.bridge.Promise;
18 15
 import com.facebook.react.bridge.ReactApplicationContext;
19 16
 import com.facebook.react.bridge.ReadableArray;
20
-import com.facebook.react.bridge.ReadableMap;
21 17
 import com.facebook.react.bridge.WritableArray;
22 18
 import com.facebook.react.bridge.WritableMap;
23 19
 import com.facebook.react.modules.core.DeviceEventManagerModule;
@@ -25,21 +21,14 @@ import com.loopj.android.http.Base64;
25 21
 
26 22
 import java.io.File;
27 23
 import java.io.FileInputStream;
28
-import java.io.FileNotFoundException;
29 24
 import java.io.FileOutputStream;
30 25
 import java.io.IOException;
31 26
 import java.io.InputStream;
32 27
 import java.io.OutputStream;
33
-import java.io.OutputStreamWriter;
34 28
 import java.nio.charset.Charset;
35
-import java.nio.charset.StandardCharsets;
36
-import java.util.ArrayList;
37 29
 import java.util.HashMap;
38 30
 import java.util.Map;
39 31
 import java.util.UUID;
40
-import java.util.concurrent.ExecutionException;
41
-
42
-import cz.msebera.android.httpclient.util.EncodingUtils;
43 32
 
44 33
 /**
45 34
  * Created by wkh237 on 2016/5/26.
@@ -49,7 +38,6 @@ public class RNFetchBlobFS {
49 38
     ReactApplicationContext mCtx;
50 39
     DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
51 40
     String encoding = "base64";
52
-    static final String assetPrefix = "bundle-assets://";
53 41
     boolean append = false;
54 42
     OutputStream writeStreamInstance = null;
55 43
     static HashMap<String, RNFetchBlobFS> fileStreams = new HashMap<>();
@@ -151,8 +139,8 @@ public class RNFetchBlobFS {
151 139
                     String encoding = strings[1];
152 140
                     byte[] bytes;
153 141
 
154
-                    if(path.startsWith(assetPrefix)) {
155
-                        String assetName = path.replace(assetPrefix, "");
142
+                    if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {
143
+                        String assetName = path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
156 144
                         long length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
157 145
                         bytes = new byte[(int) length];
158 146
                         InputStream in = RNFetchBlob.RCTContext.getAssets().open(assetName);
@@ -245,8 +233,8 @@ public class RNFetchBlobFS {
245 233
                         chunkSize = bufferSize;
246 234
 
247 235
                     InputStream fs;
248
-                    if(path.startsWith(assetPrefix)) {
249
-                        fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
236
+                    if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {
237
+                        fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, ""));
250 238
                     }
251 239
                     else {
252 240
                         fs = new FileInputStream(new File(path));
@@ -486,7 +474,7 @@ public class RNFetchBlobFS {
486 474
         path = normalizePath(path);
487 475
         if(isAsset(path)) {
488 476
             try {
489
-                String filename = path.replace(assetPrefix, "");
477
+                String filename = path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
490 478
                 AssetFileDescriptor fd = RNFetchBlob.RCTContext.getAssets().openFd(filename);
491 479
                 callback.invoke(true, false);
492 480
             } catch (IOException e) {
@@ -575,7 +563,7 @@ public class RNFetchBlobFS {
575 563
             path = normalizePath(path);
576 564
             WritableMap stat = Arguments.createMap();
577 565
             if(isAsset(path)) {
578
-                String name = path.replace(assetPrefix, "");
566
+                String name = path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
579 567
                 AssetFileDescriptor fd = RNFetchBlob.RCTContext.getAssets().openFd(name);
580 568
                 stat.putString("filename", name);
581 569
                 stat.putString("path", path);
@@ -756,8 +744,8 @@ public class RNFetchBlobFS {
756 744
      * @throws IOException
757 745
      */
758 746
     static InputStream inputStreamFromPath(String path) throws IOException {
759
-        if (path.startsWith(assetPrefix)) {
760
-            return RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
747
+        if (path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {
748
+            return RNFetchBlob.RCTContext.getAssets().open(path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, ""));
761 749
         }
762 750
         return new FileInputStream(new File(path));
763 751
     }
@@ -768,9 +756,9 @@ public class RNFetchBlobFS {
768 756
      * @return
769 757
      */
770 758
     static boolean isPathExists(String path) {
771
-        if(path.startsWith(assetPrefix)) {
759
+        if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {
772 760
             try {
773
-                RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
761
+                RNFetchBlob.RCTContext.getAssets().open(path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, ""));
774 762
             } catch (IOException e) {
775 763
                 return false;
776 764
             }
@@ -782,15 +770,15 @@ public class RNFetchBlobFS {
782 770
 
783 771
     }
784 772
 
785
-    static boolean isAsset(String path) {
786
-        return path.startsWith(assetPrefix);
773
+    public static boolean isAsset(String path) {
774
+        return path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET);
787 775
     }
788 776
 
789
-    static String normalizePath(String path) {
790
-        if(path.startsWith(assetPrefix)) {
777
+    public static String normalizePath(String path) {
778
+        if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) {
791 779
             return path;
792 780
         }
793
-        else if (path.startsWith("content://")) {
781
+        else if (path.startsWith(RNFetchBlobConst.FILE_PREFIX_CONTENT)) {
794 782
             String[] proj = { MediaStore.Images.Media.DATA };
795 783
             Uri contentUri = Uri.parse(path);
796 784
             CursorLoader loader = new CursorLoader(RNFetchBlob.RCTContext, contentUri, proj, null, null, null);

+ 0
- 95
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFileHandler.java View File

@@ -1,95 +0,0 @@
1
-package com.RNFetchBlob;
2
-
3
-import android.app.DownloadManager;
4
-import android.content.Context;
5
-
6
-import com.facebook.react.bridge.Arguments;
7
-import com.facebook.react.bridge.Callback;
8
-import com.facebook.react.bridge.ReactApplicationContext;
9
-import com.facebook.react.bridge.ReactContext;
10
-import com.facebook.react.bridge.ReadableMap;
11
-import com.facebook.react.bridge.WritableMap;
12
-import com.facebook.react.modules.core.DeviceEventManagerModule;
13
-import com.loopj.android.http.FileAsyncHttpResponseHandler;
14
-
15
-import java.io.File;
16
-
17
-import cz.msebera.android.httpclient.Header;
18
-
19
-/**
20
- * Created by wkh237 on 2016/5/26.
21
- */
22
-public class RNFetchBlobFileHandler extends FileAsyncHttpResponseHandler {
23
-
24
-    public boolean isValid;
25
-    Callback onResponse;
26
-    ReactContext mCtx;
27
-    String mTaskId;
28
-    RNFetchBlobConfig mConfig;
29
-
30
-    RNFetchBlobFileHandler(ReactApplicationContext ctx, String taskId, String key, RNFetchBlobConfig config, Callback onResponse) {
31
-        super(new File( RNFetchBlobFileHandler.getFilePath(ctx, taskId, key, config)), false, false);
32
-        this.onResponse = onResponse;
33
-        this.mTaskId = taskId;
34
-        this.mConfig = config;
35
-        this.mCtx = ctx;
36
-        if(!new File(RNFetchBlobFileHandler.getFilePath(ctx, taskId, key, config)).isFile()) {
37
-            this.isValid = false;
38
-        }
39
-        this.isValid = true;
40
-    }
41
-
42
-    static String getFilePath(ReactApplicationContext ctx, String taskId, String key, RNFetchBlobConfig config) {
43
-        if(config.path != null)
44
-            return config.path;
45
-        else if(config.fileCache && config.appendExt != null)
46
-            return RNFetchBlobFS.getTmpPath(ctx, key) + "." + config.appendExt;
47
-        else
48
-            return RNFetchBlobFS.getTmpPath(ctx, key);
49
-    }
50
-
51
-    @Override
52
-    public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
53
-        this.onResponse.invoke(statusCode, throwable.getMessage()+ ", "+ throwable.getCause());
54
-    }
55
-
56
-    @Override
57
-    public void onProgress(long bytesWritten, long totalSize) {
58
-        super.onProgress(bytesWritten, totalSize);
59
-
60
-        // on progress, emit RNFetchBlobProgress event with ticketId, bytesWritten, and totalSize
61
-        WritableMap args = Arguments.createMap();
62
-        args.putString("taskId", this.mTaskId);
63
-        args.putString("written", String.valueOf(bytesWritten));
64
-        args.putString("total", String.valueOf(totalSize));
65
-
66
-        // emit event to js context
67
-        this.mCtx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
68
-                .emit("RNFetchBlobProgress", args);
69
-    }
70
-
71
-    @Override
72
-    public void onSuccess(int statusCode, Header[] headers, File file) {
73
-        ReadableMap notifyConfig = mConfig.addAndroidDownloads;
74
-
75
-        // Download manager settings
76
-        if(notifyConfig != null ) {
77
-            String title = "", desc = "", mime = "text/plain";
78
-            boolean scannable = false, notification = false;
79
-            if(notifyConfig.hasKey("title"))
80
-                title = mConfig.addAndroidDownloads.getString("title");
81
-            if(notifyConfig.hasKey("description"))
82
-                desc = notifyConfig.getString("description");
83
-            if(notifyConfig.hasKey("mime"))
84
-                mime = notifyConfig.getString("mime");
85
-            if(notifyConfig.hasKey("mediaScannable"))
86
-                scannable = notifyConfig.getBoolean("mediaScannable");
87
-            if(notifyConfig.hasKey("notification"))
88
-                notification = notifyConfig.getBoolean("notification");
89
-            DownloadManager dm = (DownloadManager)mCtx.getSystemService(mCtx.DOWNLOAD_SERVICE);
90
-            dm.addCompletedDownload(title, desc, scannable, mime, file.getAbsolutePath(), file.length(), notification);
91
-        }
92
-
93
-        this.onResponse.invoke(null, file.getAbsolutePath());
94
-    }
95
-}

+ 251
- 199
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java View File

@@ -8,114 +8,102 @@ import android.content.IntentFilter;
8 8
 import android.database.Cursor;
9 9
 import android.net.Uri;
10 10
 
11
+import com.RNFetchBlob.Request.FormPartBody;
12
+import com.RNFetchBlob.Response.RNFetchBlobDefaultResp;
13
+import com.RNFetchBlob.Response.RNFetchBlobFileResp;
11 14
 import com.facebook.react.bridge.Callback;
12 15
 import com.facebook.react.bridge.ReactApplicationContext;
13 16
 import com.facebook.react.bridge.ReadableArray;
14 17
 import com.facebook.react.bridge.ReadableMap;
15 18
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
16
-import com.loopj.android.http.AsyncHttpClient;
17
-import com.loopj.android.http.AsyncHttpResponseHandler;
18 19
 import com.loopj.android.http.Base64;
19
-import com.loopj.android.http.MySSLSocketFactory;
20 20
 
21
+import java.io.ByteArrayInputStream;
21 22
 import java.io.File;
23
+import java.io.FileInputStream;
24
+import java.io.FileNotFoundException;
22 25
 import java.io.IOException;
23 26
 import java.io.InputStream;
24
-import java.security.KeyStore;
25
-import java.security.MessageDigest;
26 27
 
27
-import cz.msebera.android.httpclient.HttpEntity;
28
-import cz.msebera.android.httpclient.entity.ByteArrayEntity;
29
-import cz.msebera.android.httpclient.entity.ContentType;
30
-import cz.msebera.android.httpclient.entity.FileEntity;
31
-import cz.msebera.android.httpclient.entity.mime.MultipartEntityBuilder;
28
+import okhttp3.Call;
29
+import okhttp3.Interceptor;
30
+import okhttp3.MediaType;
31
+import okhttp3.MultipartBody;
32
+import okhttp3.OkHttpClient;
33
+import okhttp3.Request;
34
+import okhttp3.RequestBody;
35
+import okhttp3.Response;
32 36
 
33 37
 /**
34 38
  * Created by wkh237 on 2016/6/21.
35 39
  */
36 40
 public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
37 41
 
38
-    final String filePathPrefix = "RNFetchBlob-file://";
42
+    enum RequestType  {
43
+        Form,
44
+        SingleFile,
45
+        Others
46
+    };
47
+
48
+    enum ResponseType {
49
+        MemoryCache,
50
+        FileCache
51
+    };
52
+
53
+    MediaType contentType = RNFetchBlobConst.MIME_OCTET;
39 54
     ReactApplicationContext ctx;
40 55
     RNFetchBlobConfig options;
41 56
     String taskId;
42 57
     String method;
43 58
     String url;
44
-    String boundary;
59
+    String rawRequestBody;
60
+    String destPath;
61
+    ReadableArray rawRequestBodyArray;
45 62
     ReadableMap headers;
46 63
     Callback callback;
47
-    HttpEntity entity;
64
+    long contentLength;
48 65
     long downloadManagerId;
49
-    AsyncHttpClient req;
50
-    String type;
66
+    RequestType requestType;
67
+    ResponseType responseType;
51 68
 
52
-    public RNFetchBlobReq(ReactApplicationContext ctx, ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, final Callback callback) {
53
-        this.ctx = ctx;
69
+    public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, final Callback callback) {
54 70
         this.method = method;
55
-        this.options= new RNFetchBlobConfig(options);
71
+        this.options = new RNFetchBlobConfig(options);
56 72
         this.taskId = taskId;
57 73
         this.url = url;
58 74
         this.headers = headers;
59 75
         this.callback = callback;
60
-        this.req = new AsyncHttpClient();
61
-        if(body != null) {
62
-            type = "octet";
63
-            buildEntity(body);
64
-        }
65
-    }
66
-
67
-    public RNFetchBlobReq(ReactApplicationContext ctx, ReadableMap options, String taskId, String method, String url, ReadableMap headers, ReadableArray body, final Callback callback) {
68
-        this.ctx = ctx;
69
-        this.method = method;
70
-        this.options= new RNFetchBlobConfig(options);
71
-        this.taskId = taskId;
72
-        this.url = url;
73
-        this.headers = headers;
74
-        this.callback = callback;
75
-        this.req = new AsyncHttpClient();
76
-        if(body != null) {
77
-            type = "form";
78
-            buildFormEntity(body);
79
-        }
80
-    }
81
-
82
-    public static String getMD5(String input) {
83
-        String result = null;
84
-
85
-        try {
86
-            MessageDigest md = MessageDigest.getInstance("MD5");
87
-            md.update(input.getBytes());
88
-            byte[] digest = md.digest();
89
-            
90
-            StringBuffer sb = new StringBuffer();
91
-            
92
-            for (byte b : digest) {
93
-                sb.append(String.format("%02x", b & 0xff));
94
-            }
95
-
96
-            result = sb.toString();
97
-        } catch(Exception ex) {
98
-            ex.printStackTrace();
99
-        }
100
-
101
-        return result;
76
+        this.rawRequestBody = body;
77
+        this.rawRequestBodyArray = arrayBody;
78
+
79
+        if(this.options.fileCache != null || this.options.path != null)
80
+            responseType = ResponseType.FileCache;
81
+        else
82
+            responseType = ResponseType.MemoryCache;
83
+
84
+        if (body != null)
85
+            requestType = RequestType.SingleFile;
86
+        else if (arrayBody != null)
87
+            requestType = RequestType.Form;
88
+        else
89
+            requestType = RequestType.Others;
102 90
     }
103 91
 
104 92
     @Override
105 93
     public void run() {
106 94
 
107 95
         // use download manager instead of default HTTP implementation
108
-        if(options.addAndroidDownloads != null && options.addAndroidDownloads.hasKey("useDownloadManager")) {
96
+        if (options.addAndroidDownloads != null && options.addAndroidDownloads.hasKey("useDownloadManager")) {
109 97
 
110
-            if(options.addAndroidDownloads.getBoolean("useDownloadManager")) {
98
+            if (options.addAndroidDownloads.getBoolean("useDownloadManager")) {
111 99
                 Uri uri = Uri.parse(url);
112 100
                 DownloadManager.Request req = new DownloadManager.Request(uri);
113 101
                 req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
114 102
 
115
-                if(options.addAndroidDownloads.hasKey("title")) {
103
+                if (options.addAndroidDownloads.hasKey("title")) {
116 104
                     req.setTitle(options.addAndroidDownloads.getString("title"));
117 105
                 }
118
-                if(options.addAndroidDownloads.hasKey("description")) {
106
+                if (options.addAndroidDownloads.hasKey("description")) {
119 107
                     req.setDescription(options.addAndroidDownloads.getString("description"));
120 108
                 }
121 109
                 // set headers
@@ -132,186 +120,244 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
132 120
 
133 121
         }
134 122
 
123
+        // find cached result if `key` property exists
135 124
         String cacheKey = this.taskId;
136 125
         if (this.options.key != null) {
137
-            cacheKey = RNFetchBlobReq.getMD5(this.options.key);
126
+            cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
138 127
             if (cacheKey == null) {
139 128
                 cacheKey = this.taskId;
140 129
             }
141 130
 
142
-            File file = new File(RNFetchBlobFileHandler.getFilePath(ctx, taskId, cacheKey, this.options));
131
+            File file = new File(RNFetchBlobFS.getTmpPath(ctx, cacheKey));
143 132
             if (file.exists()) {
144
-               callback.invoke(null, file.getAbsolutePath());
145
-               return;
133
+                callback.invoke(null, file.getAbsolutePath());
134
+                return;
146 135
             }
147 136
         }
148 137
 
149
-        try {
150
-
151
-            req = new AsyncHttpClient();
138
+        if(this.options.path != null)
139
+            destPath = this.options.path;
140
+        else if(this.options.fileCache)
141
+            destPath = RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey);
152 142
 
153
-            req.setLoggingEnabled(false);
143
+        OkHttpClient client;
144
+        try {
154 145
 
155 146
             // use trusty SSL socket
156
-            if(this.options.trusty) {
157
-                KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
158
-                trustStore.load(null, null);
159
-                MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
160
-                sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
161
-                req.setSSLSocketFactory(sf);
147
+            if (this.options.trusty) {
148
+                client = RNFetchBlobUtils.getUnsafeOkHttpClient();
149
+            } else {
150
+                client = new OkHttpClient();
162 151
             }
163 152
 
153
+            final Request.Builder builder = new Request.Builder();
154
+
164 155
             // set headers
165
-            if(headers != null) {
156
+            if (headers != null) {
166 157
                 ReadableMapKeySetIterator it = headers.keySetIterator();
167 158
                 while (it.hasNextKey()) {
168 159
                     String key = it.nextKey();
169
-                    req.addHeader(key, headers.getString(key));
160
+                    builder.addHeader(key, headers.getString(key));
170 161
                 }
171 162
             }
172 163
 
173
-            if(type != null)
174
-            {
175
-                if(type == "octet")
176
-                    req.addHeader("Content-Type", "application/octet-stream");
177
-                else if(type == "form")
178
-                    req.addHeader("Content-Type", "multipart/form-data; charset=utf8; boundary="+boundary);
164
+            // set request body
165
+            switch (requestType) {
166
+                case SingleFile:
167
+                    builder.method(method, new RNFetchBlobBody(
168
+                            taskId,
169
+                            RequestType.SingleFile,
170
+                            null,
171
+                            buildOctetBody(rawRequestBody),
172
+                            contentLength
173
+                    ));
174
+                    break;
175
+                case Form:
176
+                    builder.method(method, new RNFetchBlobBody(
177
+                            taskId,
178
+                            RequestType.Form,
179
+                            buildFormBody(rawRequestBodyArray),
180
+                            null,
181
+                            contentLength
182
+                    ));
183
+                case Others:
184
+                    builder.method(method, new RNFetchBlobBody(
185
+                            taskId,
186
+                            RequestType.Others,
187
+                            buildRawBody(rawRequestBody),
188
+                            null,
189
+                            contentLength
190
+                    ));
191
+                    break;
179 192
             }
180 193
 
181
-            AsyncHttpResponseHandler handler;
194
+            final Request req = builder.build();
195
+
196
+            // create response handler
197
+            client.networkInterceptors().add(new Interceptor() {
198
+                @Override
199
+                public Response intercept(Chain chain) throws IOException {
200
+                    Response originalResponse = chain.proceed(req);
201
+                    RNFetchBlobDefaultResp exetneded;
202
+                    switch (responseType) {
203
+                        case MemoryCache:
204
+                            exetneded = new RNFetchBlobDefaultResp(
205
+                                    RNFetchBlob.RCTContext,
206
+                                    taskId,
207
+                                    originalResponse.body());
208
+                            break;
209
+                        case FileCache:
210
+                            exetneded = new RNFetchBlobFileResp(
211
+                                    RNFetchBlob.RCTContext,
212
+                                    taskId,
213
+                                    originalResponse.body(),
214
+                                    destPath);
215
+                            break;
216
+                        default:
217
+                            exetneded = new RNFetchBlobDefaultResp(
218
+                                    RNFetchBlob.RCTContext,
219
+                                    taskId,
220
+                                    originalResponse.body());
221
+                            break;
222
+                    }
223
+                    return originalResponse.newBuilder().body(exetneded).build();
224
+                }
225
+            });
182 226
 
183
-            // create handler
184
-            if(options.fileCache || options.path != null) {
185
-                handler = new RNFetchBlobFileHandler(ctx, taskId, cacheKey, options, callback);
186
-                // if path format invalid, throw error
187
-                if (!((RNFetchBlobFileHandler)handler).isValid) {
188
-                    callback.invoke("RNFetchBlob fetch error, configuration path `"+ options.path  +"` is not a valid path.");
189
-                    return;
227
+            client.newCall(req).enqueue(new okhttp3.Callback() {
228
+                @Override
229
+                public void onFailure(Call call, IOException e) {
230
+                    callback.invoke(e.getLocalizedMessage(), null);
190 231
                 }
191
-            }
192
-            else
193
-                handler = new RNFetchBlobBinaryHandler(this.ctx, taskId, callback);
194 232
 
195
-            // send request
196
-            switch(method.toLowerCase()) {
197
-                case "get" :
198
-                    req.get(url, handler);
199
-                    break;
200
-                case "post" :
201
-                    if(this.type == null || this.type.equalsIgnoreCase("octet"))
202
-                        req.post(ctx, url, entity, "application/octet-stream", handler);
203
-                    else
204
-                        req.post(ctx, url, entity, "multipart/form-data", handler);
205
-                    break;
206
-                case "put" :
207
-                    if(this.type == null || this.type.equalsIgnoreCase("octet"))
208
-                        req.put(ctx, url, entity, "application/octet-stream", handler);
209
-                    else
210
-                        req.put(ctx, url, entity, "multipart/form-data", handler);
211
-                    break;
212
-                case "delete" :
213
-                    req.delete(url, handler);
214
-                    break;
215
-            }
216
-        } catch(Exception error) {
217
-            callback.invoke( "RNFetchBlob serialize request data failed: " + error.getMessage() + error.getCause());
233
+                @Override
234
+                public void onResponse(Call call, Response response) throws IOException {
235
+                    ReadableMap notifyConfig = options.addAndroidDownloads;
236
+                    // Download manager settings
237
+                    if(notifyConfig != null ) {
238
+                        String title = "", desc = "", mime = "text/plain";
239
+                        boolean scannable = false, notification = false;
240
+                        if(notifyConfig.hasKey("title"))
241
+                            title = options.addAndroidDownloads.getString("title");
242
+                        if(notifyConfig.hasKey("description"))
243
+                            desc = notifyConfig.getString("description");
244
+                        if(notifyConfig.hasKey("mime"))
245
+                            mime = notifyConfig.getString("mime");
246
+                        if(notifyConfig.hasKey("mediaScannable"))
247
+                            scannable = notifyConfig.getBoolean("mediaScannable");
248
+                        if(notifyConfig.hasKey("notification"))
249
+                            notification = notifyConfig.getBoolean("notification");
250
+                        DownloadManager dm = (DownloadManager)RNFetchBlob.RCTContext.getSystemService(RNFetchBlob.RCTContext.DOWNLOAD_SERVICE);
251
+                        dm.addCompletedDownload(title, desc, scannable, mime, destPath, contentLength, notification);
252
+                    }
253
+                    done(response);
254
+                }
255
+            });
256
+
257
+
258
+        } catch (Exception error) {
259
+            callback.invoke("RNFetchBlob request error: " + error.getMessage() + error.getCause());
218 260
         }
219 261
     }
220 262
 
221 263
     /**
222
-     * Build Mutipart body
223
-     * @param body  Body in array format
264
+     * Send response data back to javascript context.
265
+     * @param resp OkHttp response object
224 266
      */
225
-    void buildFormEntity(ReadableArray body) {
226
-        if(body != null && (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put"))) {
227
-            Long tsLong = System.currentTimeMillis()/1000;
228
-            String ts = tsLong.toString();
229
-            boundary = "RNFetchBlob".concat(ts);
230
-            MultipartEntityBuilder form = MultipartEntityBuilder.create();
231
-            form.setBoundary(boundary);
232
-            for( int i = 0; i< body.size(); i++) {
233
-                ReadableMap map = body.getMap(i);
234
-                String name = map.getString("name");
235
-                if(!map.hasKey("data"))
236
-                    continue;
237
-                String data = map.getString("data");
238
-                // file field
239
-                if(map.hasKey("filename")) {
240
-                    String mime = map.hasKey("type") ? map.getString("type") : ContentType.APPLICATION_OCTET_STREAM.getMimeType();
241
-                    String filename = map.getString("filename");
242
-                    // upload from storage
243
-                    if(data.startsWith(filePathPrefix)) {
244
-                        String orgPath = data.substring(filePathPrefix.length());
245
-                        orgPath = RNFetchBlobFS.normalizePath(orgPath);
246
-                        // path starts with content://
247
-                        if(RNFetchBlobFS.isAsset(orgPath)) {
248
-                            try {
249
-                                String assetName = orgPath.replace(RNFetchBlobFS.assetPrefix, "");
250
-                                InputStream in = RNFetchBlob.RCTContext.getAssets().open(assetName);
251
-                                long length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
252
-                                byte [] bytes = new byte[(int) length];
253
-                                in.read(bytes, 0, (int) length);
254
-                                in.close();
255
-                                form.addBinaryBody(name, bytes, ContentType.create(mime), filename);
256
-                            } catch (IOException e) {
257
-//                                e.printStackTrace();
258
-                            }
259
-                        }
260
-                        else {
261
-                            File file = new File(RNFetchBlobFS.normalizePath(orgPath));
262
-                            form.addBinaryBody(name, file, ContentType.create(mime), filename);
263
-                        }
264
-                    }
265
-                    // base64 embedded file content
266
-                    else {
267
-                        form.addBinaryBody(name, Base64.decode(data, 0), ContentType.create(mime), filename);
268
-                    }
267
+    private void done(Response resp) {
268
+        switch (responseType) {
269
+            case MemoryCache:
270
+                try {
271
+                    callback.invoke(null, android.util.Base64.encode(resp.body().bytes(), 0));
272
+                } catch (IOException e) {
273
+                    callback.invoke("RNFetchBlob failed to encode response data to BASE64 string.", null);
269 274
                 }
270
-                // data field
271
-                else {
272
-                    form.addTextBody(name, map.getString("data"));
275
+                break;
276
+            case FileCache:
277
+                callback.invoke(null, destPath);
278
+                break;
279
+            default:
280
+                try {
281
+                    callback.invoke(null, new String(resp.body().bytes(), "UTF-8"));
282
+                } catch (IOException e) {
283
+                    callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
273 284
                 }
285
+                break;
286
+        }
287
+    }
288
+
289
+    /**
290
+     * Build request body by given string
291
+     * @param body Content of request body in UTF8 string format.
292
+     * @return
293
+     */
294
+    RequestBody buildRawBody(String body) {
295
+        if(body != null) {
296
+            this.contentType = MediaType.parse(options.mime);
297
+        }
298
+        return RequestBody.create(this.contentType, body);
299
+    }
300
+
301
+    /**
302
+     * When request body is a multipart form data, build a MultipartBody object.
303
+     * @param body Body in array format
304
+     * @return
305
+     */
306
+    MultipartBody buildFormBody(ReadableArray body) {
307
+        if (body != null && (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put"))) {
308
+            this.contentType = RNFetchBlobConst.MIME_MULTIPART;
309
+            MultipartBody.Builder formBuilder = new MultipartBody.Builder();
310
+            formBuilder.setType(MultipartBody.FORM);
311
+
312
+            for (int i = 0; i < body.size(); i++) {
313
+                ReadableMap map = body.getMap(i);
314
+                FormPartBody fieldData = new FormPartBody(RNFetchBlob.RCTContext, map);
315
+                if(fieldData.filename == null)
316
+                    formBuilder.addFormDataPart(fieldData.fieldName, fieldData.stringBody);
317
+                else
318
+                    formBuilder.addFormDataPart(fieldData.fieldName, fieldData.filename, fieldData.partBody);
274 319
             }
275
-            entity = form.build();
320
+            return formBuilder.build();
276 321
         }
322
+        return null;
277 323
     }
278 324
 
279 325
     /**
280
-     * Build Octet-Stream body
281
-     * @param body  Body in string format
326
+     * Get InputStream of request body when request body contains a single file.
327
+     *
328
+     * @param body Body in string format
329
+     * @return InputStream When there's no request body, returns null
282 330
      */
283
-    void buildEntity(String body) {
331
+    InputStream buildOctetBody(String body) {
284 332
         // set body for POST and PUT
285
-        if(body != null && (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put"))) {
286
-
287
-            byte [] blob;
333
+        if (body != null && (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put"))) {
334
+            this.contentType = RNFetchBlobConst.MIME_OCTET;
335
+            byte[] blob;
288 336
             // upload from storage
289
-            if(body.startsWith(filePathPrefix)) {
290
-                String orgPath = body.substring(filePathPrefix.length());
337
+            if (body.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
338
+                String orgPath = body.substring(RNFetchBlobConst.FILE_PREFIX.length());
291 339
                 orgPath = RNFetchBlobFS.normalizePath(orgPath);
292 340
                 // handle
293
-                if(RNFetchBlobFS.isAsset(orgPath)) {
341
+                if (RNFetchBlobFS.isAsset(orgPath)) {
294 342
                     try {
295
-                        String assetName = orgPath.replace(RNFetchBlobFS.assetPrefix, "");
296
-                        InputStream in = RNFetchBlob.RCTContext.getAssets().open(assetName);
297
-                        long length = 0;
298
-                        length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
299
-                        byte [] bytes = new byte[(int) length];
300
-                        in.read(bytes, 0, (int) length);
301
-                        in.close();
302
-                        entity = new ByteArrayEntity(bytes);
343
+                        String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
344
+                        return RNFetchBlob.RCTContext.getAssets().open(assetName);
303 345
                     } catch (IOException e) {
304 346
 //                        e.printStackTrace();
305 347
                     }
348
+                } else {
349
+                    File f = new File(RNFetchBlobFS.normalizePath(orgPath));
350
+                    try {
351
+                        return new FileInputStream(f);
352
+                    } catch (FileNotFoundException e) {
353
+                        callback.invoke(e.getLocalizedMessage(), null);
354
+                    }
306 355
                 }
307
-                else
308
-                    entity = new FileEntity(new File(RNFetchBlobFS.normalizePath(orgPath)));
309
-            }
310
-            else {
311
-                blob = Base64.decode(body, 0);
312
-                entity = new ByteArrayEntity(blob);
356
+            } else {
357
+                return new ByteArrayInputStream(Base64.decode(body, 0));
313 358
             }
314 359
         }
360
+        return null;
315 361
 
316 362
     }
317 363
 
@@ -320,7 +366,7 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
320 366
         String action = intent.getAction();
321 367
         if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
322 368
             long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
323
-            if(id == this.downloadManagerId) {
369
+            if (id == this.downloadManagerId) {
324 370
                 DownloadManager.Query query = new DownloadManager.Query();
325 371
                 query.setFilterById(downloadManagerId);
326 372
                 DownloadManager dm = (DownloadManager) ctx.getSystemService(Context.DOWNLOAD_SERVICE);
@@ -329,13 +375,19 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
329 375
                 if (c.moveToFirst()) {
330 376
                     String contentUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
331 377
                     Uri uri = Uri.parse(contentUri);
332
-                    Cursor cursor = ctx.getContentResolver().query(uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
333
-                    cursor.moveToFirst();
334
-                    String filePath = cursor.getString(0);
335
-                    cursor.close();
336
-                    this.callback.invoke(null, filePath);
378
+                    Cursor cursor = ctx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null);
379
+                    if (cursor != null) {
380
+                        cursor.moveToFirst();
381
+                        String filePath = cursor.getString(0);
382
+                        cursor.close();
383
+                        this.callback.invoke(null, filePath);
384
+                    }
385
+                    else
386
+                        this.callback.invoke(null, null);
337 387
                 }
338 388
             }
339 389
         }
340 390
     }
391
+
392
+
341 393
 }

+ 83
- 0
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java View File

@@ -0,0 +1,83 @@
1
+package com.RNFetchBlob;
2
+
3
+import java.security.MessageDigest;
4
+import java.security.cert.CertificateException;
5
+
6
+import javax.net.ssl.HostnameVerifier;
7
+import javax.net.ssl.SSLContext;
8
+import javax.net.ssl.SSLSession;
9
+import javax.net.ssl.SSLSocketFactory;
10
+import javax.net.ssl.TrustManager;
11
+import javax.net.ssl.X509TrustManager;
12
+
13
+import okhttp3.OkHttpClient;
14
+
15
+/**
16
+ * Created by wkh237 on 2016/7/11.
17
+ */
18
+public class RNFetchBlobUtils {
19
+    public static String getMD5(String input) {
20
+        String result = null;
21
+
22
+        try {
23
+            MessageDigest md = MessageDigest.getInstance("MD5");
24
+            md.update(input.getBytes());
25
+            byte[] digest = md.digest();
26
+
27
+            StringBuffer sb = new StringBuffer();
28
+
29
+            for (byte b : digest) {
30
+                sb.append(String.format("%02x", b & 0xff));
31
+            }
32
+
33
+            result = sb.toString();
34
+        } catch (Exception ex) {
35
+            ex.printStackTrace();
36
+        } finally {
37
+            return result;
38
+        }
39
+
40
+    }
41
+
42
+    public static OkHttpClient getUnsafeOkHttpClient() {
43
+        try {
44
+            // Create a trust manager that does not validate certificate chains
45
+            final TrustManager[] trustAllCerts = new TrustManager[]{
46
+                    new X509TrustManager() {
47
+                        @Override
48
+                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
49
+                        }
50
+
51
+                        @Override
52
+                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
53
+                        }
54
+
55
+                        @Override
56
+                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
57
+                            return new java.security.cert.X509Certificate[]{};
58
+                        }
59
+                    }
60
+            };
61
+
62
+            // Install the all-trusting trust manager
63
+            final SSLContext sslContext = SSLContext.getInstance("SSL");
64
+            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
65
+            // Create an ssl socket factory with our all-trusting manager
66
+            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
67
+
68
+            OkHttpClient.Builder builder = new OkHttpClient.Builder();
69
+            builder.sslSocketFactory(sslSocketFactory);
70
+            builder.hostnameVerifier(new HostnameVerifier() {
71
+                @Override
72
+                public boolean verify(String hostname, SSLSession session) {
73
+                    return true;
74
+                }
75
+            });
76
+
77
+            OkHttpClient okHttpClient = builder.build();
78
+            return okHttpClient;
79
+        } catch (Exception e) {
80
+            throw new RuntimeException(e);
81
+        }
82
+    }
83
+}

+ 71
- 0
src/android/src/main/java/com/RNFetchBlob/Request/FormPartBody.java View File

@@ -0,0 +1,71 @@
1
+package com.RNFetchBlob.Request;
2
+
3
+import com.RNFetchBlob.RNFetchBlobConst;
4
+import com.RNFetchBlob.RNFetchBlobFS;
5
+import com.facebook.react.bridge.ReactApplicationContext;
6
+import com.facebook.react.bridge.ReadableMap;
7
+import com.loopj.android.http.Base64;
8
+
9
+import java.io.File;
10
+import java.io.IOException;
11
+import java.io.InputStream;
12
+
13
+import okhttp3.MediaType;
14
+import okhttp3.RequestBody;
15
+
16
+/**
17
+ * Created by wkh237 on 2016/7/12.
18
+ */
19
+public class FormPartBody {
20
+
21
+    public String fieldName;
22
+    public String filename;
23
+    public RequestBody partBody;
24
+    public String stringBody;
25
+
26
+    public FormPartBody(ReactApplicationContext ctx, ReadableMap field) {
27
+        String data = field.getString("data");
28
+        String name = field.getString("name");
29
+        RequestBody partBody;
30
+        if (field.hasKey("filename")) {
31
+            MediaType mime = field.hasKey("type") ?
32
+                    MediaType.parse(field.getString("type")) : RNFetchBlobConst.MIME_OCTET;
33
+            String filename = field.getString("filename");
34
+            // upload from storage
35
+            if (data.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
36
+                String orgPath = data.substring(RNFetchBlobConst.FILE_PREFIX.length());
37
+                orgPath = RNFetchBlobFS.normalizePath(orgPath);
38
+                // path starts with content://
39
+                if (RNFetchBlobFS.isAsset(orgPath)) {
40
+                    try {
41
+                        String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
42
+                        InputStream in = ctx.getAssets().open(assetName);
43
+                        long length = ctx.getAssets().openFd(assetName).getLength();
44
+                        byte[] bytes = new byte[(int) length];
45
+                        in.read(bytes, 0, (int) length);
46
+                        in.close();
47
+                        partBody = RequestBody.create(mime, bytes, 0, (int) length);
48
+                    } catch (IOException e) {
49
+                        partBody = null;
50
+                    }
51
+                } else {
52
+                    File file = new File(RNFetchBlobFS.normalizePath(orgPath));
53
+                    partBody = RequestBody.create(mime, file);
54
+                }
55
+            }
56
+            // base64 embedded file content
57
+            else {
58
+                byte[] bytes = Base64.decode(data, 0);
59
+                partBody = RequestBody.create(mime, bytes, 0, bytes.length);
60
+            }
61
+            this.filename = filename;
62
+            this.fieldName = name;
63
+            this.partBody = partBody;
64
+        }
65
+        // data field
66
+        else {
67
+            this.fieldName = name;
68
+            this.stringBody = field.getString("data");
69
+        }
70
+    }
71
+}

+ 86
- 0
src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobDefaultResp.java View File

@@ -0,0 +1,86 @@
1
+package com.RNFetchBlob.Response;
2
+
3
+import com.RNFetchBlob.RNFetchBlob;
4
+import com.facebook.react.bridge.Arguments;
5
+import com.facebook.react.bridge.ReactApplicationContext;
6
+import com.facebook.react.bridge.WritableMap;
7
+import com.facebook.react.modules.core.DeviceEventManagerModule;
8
+
9
+import java.io.IOException;
10
+
11
+import okhttp3.Call;
12
+import okhttp3.Callback;
13
+import okhttp3.MediaType;
14
+import okhttp3.Response;
15
+import okhttp3.ResponseBody;
16
+import okio.Buffer;
17
+import okio.BufferedSource;
18
+import okio.ForwardingSource;
19
+import okio.Okio;
20
+import okio.Source;
21
+import okio.Timeout;
22
+
23
+/**
24
+ * Created by wkh237 on 2016/7/11.
25
+ */
26
+public class RNFetchBlobDefaultResp extends ResponseBody {
27
+
28
+    String mTaskId;
29
+    ReactApplicationContext rctContext;
30
+    ResponseBody originalBody;
31
+
32
+    public RNFetchBlobDefaultResp(ReactApplicationContext ctx, String taskId, ResponseBody body) {
33
+        this.rctContext = ctx;
34
+        this.mTaskId = taskId;
35
+        this.originalBody = body;
36
+    }
37
+
38
+    @Override
39
+    public MediaType contentType() {
40
+        return originalBody.contentType();
41
+    }
42
+
43
+    @Override
44
+    public long contentLength() {
45
+        return originalBody.contentLength();
46
+    }
47
+
48
+    @Override
49
+    public BufferedSource source() {
50
+        return Okio.buffer(new ProgressReportingSource(originalBody.source()));
51
+    }
52
+
53
+    private class ProgressReportingSource implements Source {
54
+
55
+        BufferedSource mOriginalSource;
56
+        long bytesRead = 0;
57
+
58
+        ProgressReportingSource(BufferedSource originalSource) {
59
+            mOriginalSource = originalSource;
60
+        }
61
+
62
+        @Override
63
+        public long read(Buffer sink, long byteCount) throws IOException {
64
+            bytesRead += byteCount;
65
+            long read =  mOriginalSource.read(sink, byteCount);
66
+            WritableMap args = Arguments.createMap();
67
+            args.putString("taskId", mTaskId);
68
+            args.putString("written", String.valueOf(bytesRead));
69
+            args.putString("total", String.valueOf(contentLength()));
70
+            rctContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
71
+                    .emit("RNFetchBlobProgress", args);
72
+            return read;
73
+        }
74
+
75
+        @Override
76
+        public Timeout timeout() {
77
+            return null;
78
+        }
79
+
80
+        @Override
81
+        public void close() throws IOException {
82
+
83
+        }
84
+    }
85
+
86
+}

+ 84
- 0
src/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java View File

@@ -0,0 +1,84 @@
1
+package com.RNFetchBlob.Response;
2
+
3
+import com.RNFetchBlob.RNFetchBlob;
4
+import com.facebook.react.bridge.Arguments;
5
+import com.facebook.react.bridge.ReactApplicationContext;
6
+import com.facebook.react.bridge.WritableMap;
7
+import com.facebook.react.modules.core.DeviceEventManagerModule;
8
+
9
+import java.io.File;
10
+import java.io.FileOutputStream;
11
+import java.io.IOException;
12
+
13
+import okhttp3.ResponseBody;
14
+import okio.Buffer;
15
+import okio.BufferedSource;
16
+import okio.Okio;
17
+import okio.Source;
18
+import okio.Timeout;
19
+
20
+/**
21
+ * Created by wkh237 on 2016/7/11.
22
+ */
23
+public class RNFetchBlobFileResp extends RNFetchBlobDefaultResp {
24
+
25
+    String mPath;
26
+    ReactApplicationContext rctContext;
27
+    FileOutputStream ofStream;
28
+
29
+    public RNFetchBlobFileResp(ReactApplicationContext ctx, String taskId, ResponseBody body, String path) throws IOException {
30
+        super(ctx, taskId, body);
31
+        this.rctContext = ctx;
32
+        this.mTaskId = taskId;
33
+        this.originalBody = body;
34
+        this.mPath = path;
35
+        File f = new File(path);
36
+        if(f.exists() == false)
37
+            f.createNewFile();
38
+        ofStream = new FileOutputStream(new File(path));
39
+    }
40
+
41
+
42
+    @Override
43
+    public BufferedSource source() {
44
+        ProgressReportingSource source = new ProgressReportingSource(originalBody.source());
45
+        return Okio.buffer(source);
46
+    }
47
+
48
+    private class ProgressReportingSource implements Source {
49
+
50
+        BufferedSource mOriginalSource;
51
+        long bytesRead = 0;
52
+
53
+        ProgressReportingSource(BufferedSource originalSource) {
54
+            mOriginalSource = originalSource;
55
+        }
56
+
57
+        @Override
58
+        public long read(Buffer sink, long byteCount) throws IOException {
59
+            bytesRead += byteCount;
60
+            byte [] bytes = new byte[10240];
61
+            long read = mOriginalSource.read(bytes);
62
+            ofStream.write(bytes);
63
+            WritableMap args = Arguments.createMap();
64
+            args.putString("taskId", mTaskId);
65
+            args.putString("written", String.valueOf(bytesRead));
66
+            args.putString("total", String.valueOf(contentLength()));
67
+            rctContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
68
+                    .emit("RNFetchBlobProgress", args);
69
+            return read;
70
+        }
71
+
72
+        @Override
73
+        public Timeout timeout() {
74
+            return null;
75
+        }
76
+
77
+        @Override
78
+        public void close() throws IOException {
79
+            ofStream.close();
80
+
81
+        }
82
+    }
83
+
84
+}

+ 12
- 1
src/index.js View File

@@ -82,7 +82,7 @@ function wrap(path:string):string {
82 82
  *         @property {string} key
83 83
  *                   If this property is set, it will be converted to md5, to
84 84
  *                   check if a file with this name exists.
85
- *                   If it exists, the absolute path is returned (no network 
85
+ *                   If it exists, the absolute path is returned (no network
86 86
  *                   activity takes place )
87 87
  *                   If it doesn't exist, the file is downloaded as usual
88 88
  *
@@ -121,6 +121,12 @@ function fetch(...args:any):Promise {
121 121
       }
122 122
     })
123 123
 
124
+    let subscription = emitter.addListener('RNFetchBlobProgress-upload', (e) => {
125
+      if(e.taskId === taskId && promise.onProgress) {
126
+        promise.onUploadProgress(e.written, e.total)
127
+      }
128
+    })
129
+
124 130
     let req = RNFetchBlob[nativeMethodName]
125 131
     req(options, taskId, method, url, headers || {}, body, (err, data) => {
126 132
 
@@ -149,6 +155,11 @@ function fetch(...args:any):Promise {
149 155
     return promise
150 156
   }
151 157
 
158
+  promise.uploadProgress = (fn) => {
159
+    promise.onUploadProgress = fn
160
+    return promise
161
+  }
162
+
152 163
   return promise
153 164
 
154 165
 }