Browse Source

0.7.0 Android code refactor

Ben Hsieh 8 years ago
parent
commit
1538401e15

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

@@ -151,8 +151,13 @@ public class RNFetchBlob extends ReactContextBaseJavaModule {
151 151
     }
152 152
 
153 153
     @ReactMethod
154
-    public void cancel(String taskId) {
155
-        RNFetchBlobReq.cancelTask(taskId);
154
+    public void cancelRequest(String taskId, Callback callback) {
155
+        try {
156
+            RNFetchBlobReq.cancelTask(taskId);
157
+            callback.invoke(null, taskId);
158
+        } catch (Exception ex) {
159
+            callback.invoke(ex.getLocalizedMessage(), null);
160
+        }
156 161
     }
157 162
 
158 163
     @ReactMethod

+ 150
- 92
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java View File

@@ -1,6 +1,5 @@
1 1
 package com.RNFetchBlob;
2 2
 
3
-import android.net.Uri;
4 3
 import android.util.Base64;
5 4
 import android.util.Log;
6 5
 
@@ -8,13 +7,12 @@ import com.facebook.react.bridge.Arguments;
8 7
 import com.facebook.react.bridge.ReactApplicationContext;
9 8
 import com.facebook.react.bridge.ReadableArray;
10 9
 import com.facebook.react.bridge.ReadableMap;
11
-import com.facebook.react.bridge.WritableArray;
12 10
 import com.facebook.react.bridge.WritableMap;
13 11
 import com.facebook.react.modules.core.DeviceEventManagerModule;
14 12
 
13
+import java.io.ByteArrayInputStream;
15 14
 import java.io.File;
16 15
 import java.io.FileInputStream;
17
-import java.io.FileNotFoundException;
18 16
 import java.io.IOException;
19 17
 import java.io.InputStream;
20 18
 import java.util.ArrayList;
@@ -24,7 +22,6 @@ import okhttp3.MediaType;
24 22
 import okhttp3.RequestBody;
25 23
 import okio.Buffer;
26 24
 import okio.BufferedSink;
27
-import okio.ByteString;
28 25
 import okio.ForwardingSink;
29 26
 import okio.Okio;
30 27
 import okio.Sink;
@@ -39,18 +36,24 @@ public class RNFetchBlobBody extends RequestBody{
39 36
     long bytesWritten = 0;
40 37
     ReadableArray form;
41 38
     String mTaskId;
39
+    String rawBody;
42 40
     RNFetchBlobReq.RequestType requestType;
43 41
     MediaType mime;
44 42
 
45
-    public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, ReadableArray form, InputStream stream, long size, MediaType contentType) {
43
+    public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, ReadableArray form, MediaType contentType) {
46 44
         this.mTaskId = taskId;
47 45
         this.form = form;
48
-        requestStream = stream;
49
-        contentLength = size;
50 46
         requestType = type;
51 47
         mime = contentType;
52 48
     }
53 49
 
50
+    public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, String rawBody, MediaType contentType) {
51
+        this.mTaskId = taskId;
52
+        requestType = type;
53
+        this.rawBody = rawBody;
54
+        mime = contentType;
55
+    }
56
+
54 57
     @Override
55 58
     public MediaType contentType() {
56 59
         return mime;
@@ -59,85 +62,134 @@ public class RNFetchBlobBody extends RequestBody{
59 62
     @Override
60 63
     public void writeTo(BufferedSink sink) throws IOException {
61 64
 
62
-        ProgressReportingSource source = new ProgressReportingSource(sink, mTaskId, contentLength());
65
+        ProgressReportingSource source = new ProgressReportingSource(sink, mTaskId);
63 66
         BufferedSink buffer = Okio.buffer(source);
64 67
         switch (requestType) {
65 68
             case Form:
66
-                String boundary = "RNFetchBlob-" + mTaskId;
67
-                ArrayList<FormField> fields = countFormDataLength();
68
-                ReactApplicationContext ctx = RNFetchBlob.RCTContext;
69
-                for(int i = 0;i < fields.size(); i++) {
70
-                    FormField field = fields.get(i);
71
-                    String data = field.data;
72
-                    String name = field.name;
73
-                    // skip invalid fields
74
-                    if(name == null || data == null)
75
-                        continue;
76
-                    // form begin
77
-                    String header = "--" + boundary + "\r\n";
78
-                    if (field.filename != null) {
79
-                        header += "Content-Disposition: form-data; name=" + name + "; filename=" + field.filename + "\r\n";
80
-                        header += "Content-Type: " + field.mime+ "\r\n\r\n";
81
-                        sink.write(header.getBytes());
82
-                        // file field header end
83
-                        // upload from storage
84
-                        if (data.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
85
-                            String orgPath = data.substring(RNFetchBlobConst.FILE_PREFIX.length());
86
-                            orgPath = RNFetchBlobFS.normalizePath(orgPath);
87
-                            // path starts with content://
88
-                            if (RNFetchBlobFS.isAsset(orgPath)) {
89
-                                try {
90
-                                    String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
91
-                                    InputStream in = ctx.getAssets().open(assetName);
92
-                                    pipeStreamToSink(in, sink);
93
-                                } catch (IOException e) {
94
-                                    Log.e("RNFetchBlob", "Failed to create form data asset :" + orgPath + ", " + e.getLocalizedMessage() );
95
-                                }
96
-                            }
97
-                            // data from normal files
98
-                            else {
99
-                                File file = new File(RNFetchBlobFS.normalizePath(orgPath));
100
-                                if(file.exists()) {
101
-                                    FileInputStream fs = new FileInputStream(file);
102
-                                    pipeStreamToSink(fs, sink);
103
-                                }
104
-                                else {
105
-                                    Log.e("RNFetchBlob", "Failed to create form data from path :" + orgPath + "file not exists.");
106
-                                }
107
-                            }
108
-                        }
109
-                        // base64 embedded file content
110
-                        else {
111
-                            byte[] b = Base64.decode(data, 0);
112
-                            sink.write(b);
113
-                            bytesWritten += b.length;
114
-                            emitUploadProgress(bytesWritten, contentLength);
115
-                        }
69
+                writeFormData(sink);
70
+                break;
71
+            case SingleFile:
72
+                writeOctetData(sink);
73
+                break;
74
+        }
75
+        buffer.flush();
76
+    }
116 77
 
78
+    private void writeFormData(BufferedSink sink) throws IOException {
79
+        String boundary = "RNFetchBlob-" + mTaskId;
80
+        ArrayList<FormField> fields = countFormDataLength();
81
+        ReactApplicationContext ctx = RNFetchBlob.RCTContext;
82
+        for(int i = 0;i < fields.size(); i++) {
83
+            FormField field = fields.get(i);
84
+            String data = field.data;
85
+            String name = field.name;
86
+            // skip invalid fields
87
+            if(name == null || data == null)
88
+                continue;
89
+            // form begin
90
+            String header = "--" + boundary + "\r\n";
91
+            if (field.filename != null) {
92
+                header += "Content-Disposition: form-data; name=" + name + "; filename=" + field.filename + "\r\n";
93
+                header += "Content-Type: " + field.mime+ "\r\n\r\n";
94
+                sink.write(header.getBytes());
95
+                // file field header end
96
+                // upload from storage
97
+                if (data.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
98
+                    String orgPath = data.substring(RNFetchBlobConst.FILE_PREFIX.length());
99
+                    orgPath = RNFetchBlobFS.normalizePath(orgPath);
100
+                    // path starts with content://
101
+                    if (RNFetchBlobFS.isAsset(orgPath)) {
102
+                        try {
103
+                            String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
104
+                            InputStream in = ctx.getAssets().open(assetName);
105
+                            pipeStreamToSink(in, sink);
106
+                        } catch (IOException e) {
107
+                            Log.e("RNFetchBlob", "Failed to create form data asset :" + orgPath + ", " + e.getLocalizedMessage() );
108
+                        }
117 109
                     }
118
-                    // data field
110
+                    // data from normal files
119 111
                     else {
120
-                        header += "Content-Disposition: form-data; name=" + name + "\r\n";
121
-                        header += "Content-Type: " + field.mime + "\r\n\r\n";
122
-                        sink.write(header.getBytes());
123
-                        byte[] fieldData = field.data.getBytes();
124
-                        bytesWritten += fieldData.length;
125
-                        sink.write(fieldData);
112
+                        File file = new File(RNFetchBlobFS.normalizePath(orgPath));
113
+                        if(file.exists()) {
114
+                            FileInputStream fs = new FileInputStream(file);
115
+                            pipeStreamToSink(fs, sink);
116
+                        }
117
+                        else {
118
+                            Log.e("RNFetchBlob", "Failed to create form data from path :" + orgPath + "file not exists.");
119
+                        }
126 120
                     }
127
-                    // form end
128
-                    sink.write("\r\n".getBytes());
129 121
                 }
130
-                // close the form
131
-                byte[] end = ("--" + boundary + "--\r\n").getBytes();
132
-                sink.write(end);
133
-                break;
134
-            case SingleFile:
135
-                pipeStreamToSink(requestStream, sink);
136
-                break;
122
+                // base64 embedded file content
123
+                else {
124
+                    byte[] b = Base64.decode(data, 0);
125
+                    sink.write(b);
126
+                    bytesWritten += b.length;
127
+                    emitUploadProgress();
128
+                }
129
+
130
+            }
131
+            // data field
132
+            else {
133
+                header += "Content-Disposition: form-data; name=" + name + "\r\n";
134
+                header += "Content-Type: " + field.mime + "\r\n\r\n";
135
+                sink.write(header.getBytes());
136
+                byte[] fieldData = field.data.getBytes();
137
+                bytesWritten += fieldData.length;
138
+                sink.write(fieldData);
139
+            }
140
+            // form end
141
+            sink.write("\r\n".getBytes());
137 142
         }
138
-        buffer.flush();
143
+        // close the form
144
+        byte[] end = ("--" + boundary + "--\r\n").getBytes();
145
+        sink.write(end);
146
+    }
147
+
148
+    /**
149
+     * Write octet stream data to request body
150
+     * @param sink
151
+     */
152
+    private void writeOctetData(BufferedSink sink) throws IOException {
153
+        // upload from storage
154
+        if (rawBody.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
155
+            String orgPath = rawBody.substring(RNFetchBlobConst.FILE_PREFIX.length());
156
+            orgPath = RNFetchBlobFS.normalizePath(orgPath);
157
+            // upload file from assets
158
+            if (RNFetchBlobFS.isAsset(orgPath)) {
159
+                try {
160
+                    String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
161
+                    contentLength = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
162
+                    requestStream = RNFetchBlob.RCTContext.getAssets().open(assetName);
163
+                } catch (IOException e) {
164
+//                        e.printStackTrace();
165
+                }
166
+            } else {
167
+                File f = new File(RNFetchBlobFS.normalizePath(orgPath));
168
+                try {
169
+                    if(!f.exists())
170
+                        f.createNewFile();
171
+                    contentLength = f.length();
172
+                    requestStream = new FileInputStream(f);
173
+                } catch (Exception e) {
174
+//                        callback.invoke(e.getLocalizedMessage(), null);
175
+                }
176
+            }
177
+        } else {
178
+            byte[] bytes = Base64.decode(rawBody, 0);
179
+            contentLength = bytes.length;
180
+            requestStream = new ByteArrayInputStream(bytes);
181
+        }
182
+        if(requestStream != null)
183
+            pipeStreamToSink(requestStream, sink);
184
+
139 185
     }
140 186
 
187
+    /**
188
+     * Pipe input stream to request body output stream
189
+     * @param stream    The input stream
190
+     * @param sink      The request body buffer sink
191
+     * @throws IOException
192
+     */
141 193
     private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws IOException {
142 194
         byte [] chunk = new byte[10240];
143 195
         int read = stream.read(chunk, 0, 10240);
@@ -150,28 +202,21 @@ public class RNFetchBlobBody extends RequestBody{
150 202
             if(read > 0) {
151 203
                 sink.write(chunk, 0, read);
152 204
                 bytesWritten += read;
153
-                emitUploadProgress(bytesWritten, contentLength);
205
+                emitUploadProgress();
154 206
             }
155 207
 
156 208
         }
157 209
         stream.close();
158 210
     }
159 211
 
160
-    private void emitUploadProgress(long current, long total) {
161
-        WritableMap args = Arguments.createMap();
162
-        args.putString("taskId", mTaskId);
163
-        args.putString("written", String.valueOf(bytesWritten));
164
-        args.putString("total", String.valueOf(contentLength));
165
-
166
-        // emit event to js context
167
-        RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
168
-                .emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
212
+    private void writeBufferToSink(byte [] bytes, BufferedSink sink) throws IOException {
213
+        bytesWritten += bytes.length;
214
+        sink.write(bytes);
215
+        emitUploadProgress();
169 216
     }
170 217
 
171
-
172
-
173 218
     /**
174
-     * Compute a proximate content length for form data
219
+     * Compute approximate content length for form data
175 220
      * @return
176 221
      */
177 222
     private ArrayList<FormField> countFormDataLength() {
@@ -218,6 +263,10 @@ public class RNFetchBlobBody extends RequestBody{
218 263
         return list;
219 264
     }
220 265
 
266
+    /**
267
+     * Since ReadableMap could only be access once, we have to store the field into a map for
268
+     * repeatedly access.
269
+     */
221 270
     private class FormField {
222 271
         public String name;
223 272
         public String filename;
@@ -239,17 +288,26 @@ public class RNFetchBlobBody extends RequestBody{
239 288
         }
240 289
     }
241 290
 
291
+    private void emitUploadProgress() {
292
+        WritableMap args = Arguments.createMap();
293
+        args.putString("taskId", mTaskId);
294
+        args.putString("written", String.valueOf(bytesWritten));
295
+        args.putString("total", String.valueOf(contentLength));
296
+
297
+        // emit event to js context
298
+        RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
299
+                .emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
300
+    }
301
+
242 302
     private final class ProgressReportingSource extends ForwardingSink {
243 303
 
244 304
         private long bytesWritten = 0;
245 305
         private String mTaskId;
246
-        private long mContentLength ;
247 306
         private Sink delegate;
248 307
 
249
-        public ProgressReportingSource (Sink delegate, String taskId, long contentLength) {
308
+        public ProgressReportingSource (Sink delegate, String taskId) {
250 309
             super(delegate);
251 310
             this.mTaskId = taskId;
252
-            this.mContentLength = contentLength;
253 311
             this.delegate = delegate;
254 312
         }
255 313
 
@@ -262,7 +320,7 @@ public class RNFetchBlobBody extends RequestBody{
262 320
             WritableMap args = Arguments.createMap();
263 321
             args.putString("taskId", mTaskId);
264 322
             args.putString("written", String.valueOf(bytesWritten));
265
-            args.putString("total", String.valueOf(mContentLength ));
323
+            args.putString("total", String.valueOf(contentLength));
266 324
 
267 325
             // emit event to js context
268 326
             RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)

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

@@ -182,13 +182,10 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
182 182
             // set request body
183 183
             switch (requestType) {
184 184
                 case SingleFile:
185
-                    InputStream dataStream = buildOctetBody(rawRequestBody);
186 185
                     builder.method(method, new RNFetchBlobBody(
187 186
                             taskId,
188 187
                             RequestType.SingleFile,
189
-                            null,
190
-                            dataStream,
191
-                            contentLength,
188
+                            rawRequestBody,
192 189
                             RNFetchBlobConst.MIME_OCTET
193 190
                     ));
194 191
                     break;
@@ -197,8 +194,6 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
197 194
                             taskId,
198 195
                             RequestType.Form,
199 196
                             rawRequestBodyArray,
200
-                            null,
201
-                            0,
202 197
                             MediaType.parse("multipart/form-data; boundary=RNFetchBlob-" + taskId)
203 198
                     ));
204 199
                     break;
@@ -329,51 +324,6 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
329 324
 
330 325
     }
331 326
 
332
-    /**
333
-     * Get InputStream of request body when request body contains a single file.
334
-     *
335
-     * @param body Body in string format
336
-     * @return InputStream When there's no request body, returns null
337
-     */
338
-    InputStream buildOctetBody(String body) {
339
-        // set body for POST and PUT
340
-        if (body != null && (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put"))) {
341
-            this.contentType = RNFetchBlobConst.MIME_OCTET;
342
-            byte[] blob;
343
-            // upload from storage
344
-            if (body.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
345
-                String orgPath = body.substring(RNFetchBlobConst.FILE_PREFIX.length());
346
-                orgPath = RNFetchBlobFS.normalizePath(orgPath);
347
-                // upload file from assets
348
-                if (RNFetchBlobFS.isAsset(orgPath)) {
349
-                    try {
350
-                        String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
351
-                        contentLength = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
352
-                        return RNFetchBlob.RCTContext.getAssets().open(assetName);
353
-                    } catch (IOException e) {
354
-//                        e.printStackTrace();
355
-                    }
356
-                } else {
357
-                    File f = new File(RNFetchBlobFS.normalizePath(orgPath));
358
-                    try {
359
-                        if(!f.exists())
360
-                            f.createNewFile();
361
-                        contentLength = f.length();
362
-                        return new FileInputStream(f);
363
-                    } catch (Exception e) {
364
-                        callback.invoke(e.getLocalizedMessage(), null);
365
-                    }
366
-                }
367
-            } else {
368
-                byte[] bytes = Base64.decode(body, 0);
369
-                contentLength = bytes.length;
370
-                return new ByteArrayInputStream(bytes);
371
-            }
372
-        }
373
-        return null;
374
-
375
-    }
376
-
377 327
     @Override
378 328
     public void onReceive(Context context, Intent intent) {
379 329
         String action = intent.getAction();

+ 5
- 3
src/index.js View File

@@ -149,17 +149,19 @@ function fetch(...args:any):Promise {
149 149
 
150 150
   })
151 151
 
152
-  // extend Promise object, add a `progress` method for register progress event
153
-  // handler.
152
+  // extend Promise object, add `progress`, `uploadProgress`, and `cancel`
153
+  // method for register progress event handler and cancel request.
154 154
   promise.progress = (fn) => {
155 155
     promise.onProgress = fn
156 156
     return promise
157 157
   }
158
-
159 158
   promise.uploadProgress = (fn) => {
160 159
     promise.onUploadProgress = fn
161 160
     return promise
162 161
   }
162
+  promise.cancel = (fn) => {
163
+    RNFetchBlob.cancelRequest(taskId, fn)
164
+  }
163 165
 
164 166
   return promise
165 167
 

+ 0
- 60
test/test-0.6.3.js View File

@@ -47,63 +47,3 @@ false && describe('massive HTTP request', (report, done) => {
47 47
   })
48 48
 
49 49
 })
50
-
51
-RNTest.config({
52
-  group : '0.7.0',
53
-  run : true,
54
-  expand : false,
55
-  timeout : 600000,
56
-})('Upload and download large file', (report, done) => {
57
-  let filename = '22mb-dummy-' + Date.now()
58
-  let begin = -1
59
-  let begin2 = -1
60
-  let deb = Date.now()
61
-  RNFetchBlob.config({
62
-    fileCache : true
63
-  })
64
-  .fetch('GET', `${TEST_SERVER_URL}/public/22mb-dummy`)
65
-  .progress((now, total) => {
66
-    if(begin === -1)
67
-      begin = Date.now()
68
-    if(Date.now() - deb < 1000)
69
-      return
70
-    deb = Date.now()
71
-    report(<Info uid="200" key="progress">
72
-      <Text>
73
-        {`download ${now} / ${total} bytes (${Math.floor(now / (Date.now() - begin))} kb/s)`}
74
-      </Text>
75
-    </Info>)
76
-  })
77
-  .then((res) => {
78
-    try {
79
-    deb = Date.now()
80
-    let promise =  RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
81
-      Authorization : `Bearer ${DROPBOX_TOKEN}`,
82
-      'Dropbox-API-Arg': '{\"path\": \"/rn-upload/'+filename+'\",\"mode\": \"add\",\"autorename\": true,\"mute\": false}',
83
-      'Content-Type' : 'application/octet-stream',
84
-    }, RNFetchBlob.wrap(res.path()))
85
-    promise.uploadProgress((now, total) => {
86
-      if(Date.now() - deb < 1000)
87
-        return
88
-      deb = Date.now()
89
-      if(begin2 === -1)
90
-        begin2 = Date.now()
91
-      let speed = Math.floor(now / (Date.now() - begin2))
92
-      report(<Info uid="100"  key="progress">
93
-        <Text>
94
-          {`upload ${now} / ${total} bytes (${speed} kb/s)`}
95
-          {` ${Math.floor((total-now)/speed/1000)} seconds left`}
96
-        </Text>
97
-      </Info>)
98
-    })
99
-    return promise
100
-  } catch(err) { console.log(err) }
101
-  })
102
-  .then((res) => {
103
-    report(<Assert
104
-      key="upload should success without crashing app"
105
-      expect={filename}
106
-      actual={res.json().name}/>)
107
-    done()
108
-  })
109
-})