11package com .RNFetchBlob ;
22
3- import android .net .Uri ;
43import android .util .Base64 ;
54import android .util .Log ;
65
76import com .facebook .react .bridge .Arguments ;
87import com .facebook .react .bridge .ReactApplicationContext ;
98import com .facebook .react .bridge .ReadableArray ;
109import com .facebook .react .bridge .ReadableMap ;
11- import com .facebook .react .bridge .WritableArray ;
1210import com .facebook .react .bridge .WritableMap ;
1311import com .facebook .react .modules .core .DeviceEventManagerModule ;
1412
13+ import java .io .ByteArrayInputStream ;
1514import java .io .File ;
1615import java .io .FileInputStream ;
17- import java .io .FileNotFoundException ;
1816import java .io .IOException ;
1917import java .io .InputStream ;
2018import java .util .ArrayList ;
2422import okhttp3 .RequestBody ;
2523import okio .Buffer ;
2624import okio .BufferedSink ;
27- import okio .ByteString ;
2825import okio .ForwardingSink ;
2926import okio .Okio ;
3027import okio .Sink ;
@@ -39,18 +36,24 @@ public class RNFetchBlobBody extends RequestBody{
3936 long bytesWritten = 0 ;
4037 ReadableArray form ;
4138 String mTaskId ;
39+ String rawBody ;
4240 RNFetchBlobReq .RequestType requestType ;
4341 MediaType mime ;
4442
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 ) {
4644 this .mTaskId = taskId ;
4745 this .form = form ;
48- requestStream = stream ;
49- contentLength = size ;
5046 requestType = type ;
5147 mime = contentType ;
5248 }
5349
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+
5457 @ Override
5558 public MediaType contentType () {
5659 return mime ;
@@ -59,85 +62,134 @@ public MediaType contentType() {
5962 @ Override
6063 public void writeTo (BufferedSink sink ) throws IOException {
6164
62- ProgressReportingSource source = new ProgressReportingSource (sink , mTaskId , contentLength () );
65+ ProgressReportingSource source = new ProgressReportingSource (sink , mTaskId );
6366 BufferedSink buffer = Okio .buffer (source );
6467 switch (requestType ) {
6568 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+ }
11677
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+ }
117109 }
118- // data field
110+ // data from normal files
119111 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+ }
126120 }
127- // form end
128- sink .write ("\r \n " .getBytes ());
129121 }
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 ());
137142 }
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+
139185 }
140186
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+ */
141193 private void pipeStreamToSink (InputStream stream , BufferedSink sink ) throws IOException {
142194 byte [] chunk = new byte [10240 ];
143195 int read = stream .read (chunk , 0 , 10240 );
@@ -150,28 +202,21 @@ private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws IOEx
150202 if (read > 0 ) {
151203 sink .write (chunk , 0 , read );
152204 bytesWritten += read ;
153- emitUploadProgress (bytesWritten , contentLength );
205+ emitUploadProgress ();
154206 }
155207
156208 }
157209 stream .close ();
158210 }
159211
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 ();
169216 }
170217
171-
172-
173218 /**
174- * Compute a proximate content length for form data
219+ * Compute approximate content length for form data
175220 * @return
176221 */
177222 private ArrayList <FormField > countFormDataLength () {
@@ -218,6 +263,10 @@ private ArrayList<FormField> countFormDataLength() {
218263 return list ;
219264 }
220265
266+ /**
267+ * Since ReadableMap could only be access once, we have to store the field into a map for
268+ * repeatedly access.
269+ */
221270 private class FormField {
222271 public String name ;
223272 public String filename ;
@@ -239,17 +288,26 @@ public FormField(ReadableMap rawData) {
239288 }
240289 }
241290
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+
242302 private final class ProgressReportingSource extends ForwardingSink {
243303
244304 private long bytesWritten = 0 ;
245305 private String mTaskId ;
246- private long mContentLength ;
247306 private Sink delegate ;
248307
249- public ProgressReportingSource (Sink delegate , String taskId , long contentLength ) {
308+ public ProgressReportingSource (Sink delegate , String taskId ) {
250309 super (delegate );
251310 this .mTaskId = taskId ;
252- this .mContentLength = contentLength ;
253311 this .delegate = delegate ;
254312 }
255313
@@ -262,7 +320,7 @@ public void write(Buffer source, long byteCount) throws IOException {
262320 WritableMap args = Arguments .createMap ();
263321 args .putString ("taskId" , mTaskId );
264322 args .putString ("written" , String .valueOf (bytesWritten ));
265- args .putString ("total" , String .valueOf (mContentLength ));
323+ args .putString ("total" , String .valueOf (contentLength ));
266324
267325 // emit event to js context
268326 RNFetchBlob .RCTContext .getJSModule (DeviceEventManagerModule .RCTDeviceEventEmitter .class )
0 commit comments