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