Browse Source

Fix Android upload progress #94

Ben Hsieh 8 years ago
parent
commit
0fb6635c6a
1 changed files with 73 additions and 96 deletions
  1. 73
    96
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java

+ 73
- 96
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java View File

1
 package com.RNFetchBlob;
1
 package com.RNFetchBlob;
2
 
2
 
3
 import android.util.Base64;
3
 import android.util.Base64;
4
-import android.util.Log;
5
 
4
 
6
 import com.facebook.react.bridge.Arguments;
5
 import com.facebook.react.bridge.Arguments;
7
 import com.facebook.react.bridge.ReactApplicationContext;
6
 import com.facebook.react.bridge.ReactApplicationContext;
13
 import java.io.ByteArrayInputStream;
12
 import java.io.ByteArrayInputStream;
14
 import java.io.File;
13
 import java.io.File;
15
 import java.io.FileInputStream;
14
 import java.io.FileInputStream;
16
-import java.io.FileNotFoundException;
17
 import java.io.FileOutputStream;
15
 import java.io.FileOutputStream;
18
 import java.io.IOException;
16
 import java.io.IOException;
19
 import java.io.InputStream;
17
 import java.io.InputStream;
20
-import java.nio.ByteBuffer;
21
-import java.nio.MappedByteBuffer;
22
 import java.util.ArrayList;
18
 import java.util.ArrayList;
23
-import java.util.HashMap;
24
 
19
 
25
 import okhttp3.MediaType;
20
 import okhttp3.MediaType;
26
 import okhttp3.RequestBody;
21
 import okhttp3.RequestBody;
27
-import okhttp3.FormBody;
28
-import okio.Buffer;
29
 import okio.BufferedSink;
22
 import okio.BufferedSink;
30
-import okio.ForwardingSink;
31
-import okio.Okio;
32
-import okio.Sink;
33
 
23
 
34
 /**
24
 /**
35
  * Created by wkh237 on 2016/7/11.
25
  * Created by wkh237 on 2016/7/11.
38
 
28
 
39
     InputStream requestStream;
29
     InputStream requestStream;
40
     long contentLength = 0;
30
     long contentLength = 0;
41
-    long bytesWritten = 0;
42
     ReadableArray form;
31
     ReadableArray form;
43
     String mTaskId;
32
     String mTaskId;
44
     String rawBody;
33
     String rawBody;
47
     File bodyCache;
36
     File bodyCache;
48
 
37
 
49
 
38
 
39
+    /**
40
+     * Single file or raw content request constructor
41
+     * @param taskId
42
+     * @param type
43
+     * @param form
44
+     * @param contentType
45
+     */
50
     public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, ReadableArray form, MediaType contentType) {
46
     public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, ReadableArray form, MediaType contentType) {
51
         this.mTaskId = taskId;
47
         this.mTaskId = taskId;
52
         this.form = form;
48
         this.form = form;
54
         mime = contentType;
50
         mime = contentType;
55
         try {
51
         try {
56
             bodyCache = createMultipartBodyCache();
52
             bodyCache = createMultipartBodyCache();
53
+            requestStream = new FileInputStream(bodyCache);
57
             contentLength = bodyCache.length();
54
             contentLength = bodyCache.length();
58
-        } catch (IOException e) {
59
-            e.printStackTrace();
55
+        } catch(Exception ex) {
56
+            ex.printStackTrace();
57
+            RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create request multipart body :" + ex.getLocalizedMessage());
60
         }
58
         }
61
     }
59
     }
62
 
60
 
61
+    /**
62
+     * Multipart request constructor
63
+     * @param taskId
64
+     * @param type
65
+     * @param rawBody
66
+     * @param contentType
67
+     */
63
     public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, String rawBody, MediaType contentType) {
68
     public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, String rawBody, MediaType contentType) {
64
         this.mTaskId = taskId;
69
         this.mTaskId = taskId;
65
         requestType = type;
70
         requestType = type;
66
         this.rawBody = rawBody;
71
         this.rawBody = rawBody;
67
         mime = contentType;
72
         mime = contentType;
68
-        if(rawBody != null) {
69
-            if(requestType == RNFetchBlobReq.RequestType.AsIs)
70
-                contentLength = rawBody.length();
71
-            else
72
-                contentLength = caculateOctetContentLength();
73
+        if(rawBody == null) {
74
+            this.rawBody = "";
75
+            requestType = RNFetchBlobReq.RequestType.AsIs;
76
+        }
77
+        try {
78
+            switch (requestType) {
79
+                case SingleFile:
80
+                    requestStream = getReuqestStream();
81
+                    contentLength = requestStream.available();
82
+                    break;
83
+                case AsIs:
84
+                    contentLength = this.rawBody.getBytes().length;
85
+                    break;
86
+                case Others:
87
+                    break;
88
+            }
89
+        } catch(Exception ex) {
90
+            ex.printStackTrace();
91
+            RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create single content request body :" + ex.getLocalizedMessage() + "\r\n");
73
         }
92
         }
93
+
74
     }
94
     }
75
 
95
 
76
     @Override
96
     @Override
77
     public long contentLength() {
97
     public long contentLength() {
78
         return contentLength;
98
         return contentLength;
79
     }
99
     }
100
+
80
     @Override
101
     @Override
81
     public MediaType contentType() {
102
     public MediaType contentType() {
82
         return mime;
103
         return mime;
83
     }
104
     }
84
 
105
 
85
     @Override
106
     @Override
86
-    public void writeTo(BufferedSink sink) throws IOException {
87
-
88
-        ProgressReportingSource source = new ProgressReportingSource(sink, mTaskId);
89
-        BufferedSink buffer = Okio.buffer(source);
90
-        switch (requestType) {
91
-            case Form:
92
-                pipeStreamToSink(new FileInputStream(bodyCache), sink);
93
-                break;
94
-            case SingleFile:
95
-                if(requestStream != null)
96
-                    pipeStreamToSink(requestStream, sink);
97
-                break;
98
-            case AsIs:
99
-				writeRawData(sink);
100
-				break;
107
+    public void writeTo(BufferedSink sink) {
108
+        try {
109
+            if (requestType == RNFetchBlobReq.RequestType.AsIs)
110
+                sink.write(rawBody.getBytes());
111
+            else
112
+                pipeStreamToSink(requestStream, sink);
113
+        } catch(Exception ex) {
114
+            RNFetchBlobUtils.emitWarningEvent(ex.getLocalizedMessage());
115
+            ex.printStackTrace();
101
         }
116
         }
102
-        buffer.flush();
103
     }
117
     }
104
 
118
 
105
     boolean clearRequestBody() {
119
     boolean clearRequestBody() {
114
         return true;
128
         return true;
115
     }
129
     }
116
 
130
 
117
-    private long caculateOctetContentLength() {
118
-        long total = 0;
131
+    private InputStream getReuqestStream() throws Exception {
132
+
119
         // upload from storage
133
         // upload from storage
120
         if (rawBody.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
134
         if (rawBody.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
121
             String orgPath = rawBody.substring(RNFetchBlobConst.FILE_PREFIX.length());
135
             String orgPath = rawBody.substring(RNFetchBlobConst.FILE_PREFIX.length());
124
             if (RNFetchBlobFS.isAsset(orgPath)) {
138
             if (RNFetchBlobFS.isAsset(orgPath)) {
125
                 try {
139
                 try {
126
                     String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
140
                     String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
127
-                    total += RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
128
-                    requestStream = RNFetchBlob.RCTContext.getAssets().open(assetName);
129
-                } catch (IOException e) {
130
-                    RNFetchBlobUtils.emitWarningEvent(e.getLocalizedMessage());
141
+                    return RNFetchBlob.RCTContext.getAssets().open(assetName);
142
+                } catch (Exception e) {
143
+                    throw new Exception("error when getting request stream from asset : " +e.getLocalizedMessage());
131
                 }
144
                 }
132
             } else {
145
             } else {
133
                 File f = new File(RNFetchBlobFS.normalizePath(orgPath));
146
                 File f = new File(RNFetchBlobFS.normalizePath(orgPath));
134
                 try {
147
                 try {
135
                     if(!f.exists())
148
                     if(!f.exists())
136
                         f.createNewFile();
149
                         f.createNewFile();
137
-                    total += f.length();
138
-                    requestStream = new FileInputStream(f);
150
+                    return new FileInputStream(f);
139
                 } catch (Exception e) {
151
                 } catch (Exception e) {
140
-                    RNFetchBlobUtils.emitWarningEvent("RNetchBlob error when counting content length: " +e.getLocalizedMessage());
152
+                    throw new Exception("error when getting request stream: " +e.getLocalizedMessage());
141
                 }
153
                 }
142
             }
154
             }
143
-        } else {
155
+        }
156
+        // base 64 encoded
157
+        else {
144
             try {
158
             try {
145
                 byte[] bytes = Base64.decode(rawBody, 0);
159
                 byte[] bytes = Base64.decode(rawBody, 0);
146
-                requestStream = new ByteArrayInputStream(bytes);
147
-                total += requestStream.available();
160
+                return  new ByteArrayInputStream(bytes);
148
             } catch(Exception ex) {
161
             } catch(Exception ex) {
149
-                RNFetchBlobUtils.emitWarningEvent("RNetchBlob error when counting content length: " +ex.getLocalizedMessage());
162
+                throw new Exception("error when getting request stream: " + ex.getLocalizedMessage());
150
             }
163
             }
151
         }
164
         }
152
-        return total;
153
     }
165
     }
154
 
166
 
155
     /**
167
     /**
192
                             InputStream in = ctx.getAssets().open(assetName);
204
                             InputStream in = ctx.getAssets().open(assetName);
193
                             pipeStreamToFileStream(in, os);
205
                             pipeStreamToFileStream(in, os);
194
                         } catch (IOException e) {
206
                         } catch (IOException e) {
195
-                            RNFetchBlobUtils.emitWarningEvent("RNFetchBlob Failed to create form data asset :" + orgPath + ", " + e.getLocalizedMessage() );
207
+                            RNFetchBlobUtils.emitWarningEvent("Failed to create form data asset :" + orgPath + ", " + e.getLocalizedMessage() );
196
                         }
208
                         }
197
                     }
209
                     }
198
                     // data from normal files
210
                     // data from normal files
203
                             pipeStreamToFileStream(fs, os);
215
                             pipeStreamToFileStream(fs, os);
204
                         }
216
                         }
205
                         else {
217
                         else {
206
-                            RNFetchBlobUtils.emitWarningEvent("RNFetchBlob Failed to create form data from path :" + orgPath + ", file not exists.");
218
+                            RNFetchBlobUtils.emitWarningEvent("Failed to create form data from path :" + orgPath + ", file not exists.");
207
                         }
219
                         }
208
                     }
220
                     }
209
                 }
221
                 }
211
                 else {
223
                 else {
212
                     byte[] b = Base64.decode(data, 0);
224
                     byte[] b = Base64.decode(data, 0);
213
                     os.write(b);
225
                     os.write(b);
214
-                    bytesWritten += b.length;
215
-                    emitUploadProgress();
216
                 }
226
                 }
217
 
227
 
218
             }
228
             }
222
                 header += "Content-Type: " + field.mime + "\r\n\r\n";
232
                 header += "Content-Type: " + field.mime + "\r\n\r\n";
223
                 os.write(header.getBytes());
233
                 os.write(header.getBytes());
224
                 byte[] fieldData = field.data.getBytes();
234
                 byte[] fieldData = field.data.getBytes();
225
-                bytesWritten += fieldData.length;
226
                 os.write(fieldData);
235
                 os.write(fieldData);
227
             }
236
             }
228
             // form end
237
             // form end
236
         return outputFile;
245
         return outputFile;
237
     }
246
     }
238
 
247
 
239
-	/**
240
-     * Write data to request body as-is
241
-     * @param sink
242
-     */
243
-	private void writeRawData(BufferedSink sink) throws IOException {
244
-        byte[] bytes = rawBody.getBytes();
245
-        contentLength = bytes.length;
246
-		sink.write(bytes);
247
-	}
248
-
249
     /**
248
     /**
250
      * Pipe input stream to request body output stream
249
      * Pipe input stream to request body output stream
251
      * @param stream    The input stream
250
      * @param stream    The input stream
252
      * @param sink      The request body buffer sink
251
      * @param sink      The request body buffer sink
253
      * @throws IOException
252
      * @throws IOException
254
      */
253
      */
255
-    private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws IOException {
254
+    private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws Exception {
255
+
256
         byte [] chunk = new byte[10240];
256
         byte [] chunk = new byte[10240];
257
+        int totalWritten = 0;
257
         int read;
258
         int read;
258
         while((read = stream.read(chunk, 0, 10240)) > 0) {
259
         while((read = stream.read(chunk, 0, 10240)) > 0) {
259
             if(read > 0) {
260
             if(read > 0) {
260
                 sink.write(chunk, 0, read);
261
                 sink.write(chunk, 0, read);
262
+                totalWritten += read;
263
+                emitUploadProgress(totalWritten);
261
             }
264
             }
262
         }
265
         }
263
         stream.close();
266
         stream.close();
356
         }
359
         }
357
     }
360
     }
358
 
361
 
359
-    private void emitUploadProgress() {
362
+    /**
363
+     * Emit progress event
364
+     * @param written
365
+     */
366
+    private void emitUploadProgress(int written) {
360
         WritableMap args = Arguments.createMap();
367
         WritableMap args = Arguments.createMap();
361
         args.putString("taskId", mTaskId);
368
         args.putString("taskId", mTaskId);
362
-        args.putString("written", String.valueOf(bytesWritten));
369
+        args.putString("written", String.valueOf(written));
363
         args.putString("total", String.valueOf(contentLength));
370
         args.putString("total", String.valueOf(contentLength));
364
 
371
 
365
         // emit event to js context
372
         // emit event to js context
367
                 .emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
374
                 .emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
368
     }
375
     }
369
 
376
 
370
-    private final class ProgressReportingSource extends ForwardingSink {
371
-
372
-        private long bytesWritten = 0;
373
-        private String mTaskId;
374
-        private Sink delegate;
375
-
376
-        public ProgressReportingSource (Sink delegate, String taskId) {
377
-            super(delegate);
378
-            this.mTaskId = taskId;
379
-            this.delegate = delegate;
380
-        }
381
-
382
-        @Override
383
-        public void write(Buffer source, long byteCount) throws IOException {
384
-            delegate.write(source, byteCount);
385
-            // on progress, emit RNFetchBlobProgress upload progress event with ticketId,
386
-            // bytesWritten, and totalSize
387
-            bytesWritten += byteCount;
388
-            WritableMap args = Arguments.createMap();
389
-            args.putString("taskId", mTaskId);
390
-            args.putString("written", String.valueOf(bytesWritten));
391
-            args.putString("total", String.valueOf(contentLength));
392
-
393
-            if(RNFetchBlobReq.isReportUploadProgress(mTaskId)) {
394
-                // emit event to js context
395
-                RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
396
-                        .emit(RNFetchBlobConst.EVENT_UPLOAD_PROGRESS, args);
397
-            }
398
-        }
399
-    }
400
 }
377
 }