|
@@ -13,8 +13,12 @@ import com.facebook.react.modules.core.DeviceEventManagerModule;
|
13
|
13
|
import java.io.ByteArrayInputStream;
|
14
|
14
|
import java.io.File;
|
15
|
15
|
import java.io.FileInputStream;
|
|
16
|
+import java.io.FileNotFoundException;
|
|
17
|
+import java.io.FileOutputStream;
|
16
|
18
|
import java.io.IOException;
|
17
|
19
|
import java.io.InputStream;
|
|
20
|
+import java.nio.ByteBuffer;
|
|
21
|
+import java.nio.MappedByteBuffer;
|
18
|
22
|
import java.util.ArrayList;
|
19
|
23
|
import java.util.HashMap;
|
20
|
24
|
|
|
@@ -33,19 +37,27 @@ import okio.Sink;
|
33
|
37
|
public class RNFetchBlobBody extends RequestBody{
|
34
|
38
|
|
35
|
39
|
InputStream requestStream;
|
36
|
|
- long contentLength;
|
|
40
|
+ long contentLength = 0;
|
37
|
41
|
long bytesWritten = 0;
|
38
|
42
|
ReadableArray form;
|
39
|
43
|
String mTaskId;
|
40
|
44
|
String rawBody;
|
41
|
45
|
RNFetchBlobReq.RequestType requestType;
|
42
|
46
|
MediaType mime;
|
|
47
|
+ File bodyCache;
|
|
48
|
+
|
43
|
49
|
|
44
|
50
|
public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, ReadableArray form, MediaType contentType) {
|
45
|
51
|
this.mTaskId = taskId;
|
46
|
52
|
this.form = form;
|
47
|
53
|
requestType = type;
|
48
|
54
|
mime = contentType;
|
|
55
|
+ try {
|
|
56
|
+ bodyCache = createMultipartBodyCache();
|
|
57
|
+ contentLength = bodyCache.length();
|
|
58
|
+ } catch (IOException e) {
|
|
59
|
+ e.printStackTrace();
|
|
60
|
+ }
|
49
|
61
|
}
|
50
|
62
|
|
51
|
63
|
public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, String rawBody, MediaType contentType) {
|
|
@@ -55,6 +67,10 @@ public class RNFetchBlobBody extends RequestBody{
|
55
|
67
|
mime = contentType;
|
56
|
68
|
}
|
57
|
69
|
|
|
70
|
+ @Override
|
|
71
|
+ public long contentLength() {
|
|
72
|
+ return contentLength;
|
|
73
|
+ }
|
58
|
74
|
@Override
|
59
|
75
|
public MediaType contentType() {
|
60
|
76
|
return mime;
|
|
@@ -67,10 +83,11 @@ public class RNFetchBlobBody extends RequestBody{
|
67
|
83
|
BufferedSink buffer = Okio.buffer(source);
|
68
|
84
|
switch (requestType) {
|
69
|
85
|
case Form:
|
70
|
|
- writeFormData(sink);
|
|
86
|
+ pipeStreamToSink(new FileInputStream(bodyCache), sink);
|
71
|
87
|
break;
|
72
|
88
|
case SingleFile:
|
73
|
|
- writeOctetData(sink);
|
|
89
|
+ if(requestStream != null)
|
|
90
|
+ pipeStreamToSink(requestStream, sink);
|
74
|
91
|
break;
|
75
|
92
|
case AsIs:
|
76
|
93
|
writeRawData(sink);
|
|
@@ -79,10 +96,58 @@ public class RNFetchBlobBody extends RequestBody{
|
79
|
96
|
buffer.flush();
|
80
|
97
|
}
|
81
|
98
|
|
82
|
|
- private void writeFormData(BufferedSink sink) throws IOException {
|
|
99
|
+ private void caculateOctetContentLength() {
|
|
100
|
+ long total = 0;
|
|
101
|
+ // upload from storage
|
|
102
|
+ if (rawBody.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
|
|
103
|
+ String orgPath = rawBody.substring(RNFetchBlobConst.FILE_PREFIX.length());
|
|
104
|
+ orgPath = RNFetchBlobFS.normalizePath(orgPath);
|
|
105
|
+ // upload file from assets
|
|
106
|
+ if (RNFetchBlobFS.isAsset(orgPath)) {
|
|
107
|
+ try {
|
|
108
|
+ String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
|
|
109
|
+ contentLength = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
|
|
110
|
+ requestStream = RNFetchBlob.RCTContext.getAssets().open(assetName);
|
|
111
|
+ } catch (IOException e) {
|
|
112
|
+// e.printStackTrace();
|
|
113
|
+ }
|
|
114
|
+ } else {
|
|
115
|
+ File f = new File(RNFetchBlobFS.normalizePath(orgPath));
|
|
116
|
+ try {
|
|
117
|
+ if(!f.exists())
|
|
118
|
+ f.createNewFile();
|
|
119
|
+ contentLength = f.length();
|
|
120
|
+ requestStream = new FileInputStream(f);
|
|
121
|
+ } catch (Exception e) {
|
|
122
|
+// callback.invoke(e.getLocalizedMessage(), null);
|
|
123
|
+ }
|
|
124
|
+ }
|
|
125
|
+ } else {
|
|
126
|
+ try {
|
|
127
|
+ byte[] bytes = Base64.decode(rawBody, 0);
|
|
128
|
+ contentLength = bytes.length;
|
|
129
|
+ requestStream = new ByteArrayInputStream(bytes);
|
|
130
|
+ } catch(Exception ex) {
|
|
131
|
+ Log.e("error", ex.getLocalizedMessage());
|
|
132
|
+ }
|
|
133
|
+ }
|
|
134
|
+ }
|
|
135
|
+
|
|
136
|
+ /**
|
|
137
|
+ * Create a temp file that contains content of multipart form data content
|
|
138
|
+ * @return The cache file object
|
|
139
|
+ * @throws IOException
|
|
140
|
+ */
|
|
141
|
+ private File createMultipartBodyCache() throws IOException {
|
83
|
142
|
String boundary = "RNFetchBlob-" + mTaskId;
|
|
143
|
+
|
|
144
|
+ File outputDir = RNFetchBlob.RCTContext.getCacheDir(); // context being the Activity pointer
|
|
145
|
+ File outputFile = File.createTempFile("rnfb-form-tmp", "", outputDir);
|
|
146
|
+ FileOutputStream os = new FileOutputStream(outputFile);
|
|
147
|
+
|
84
|
148
|
ArrayList<FormField> fields = countFormDataLength();
|
85
|
149
|
ReactApplicationContext ctx = RNFetchBlob.RCTContext;
|
|
150
|
+
|
86
|
151
|
for(int i = 0;i < fields.size(); i++) {
|
87
|
152
|
FormField field = fields.get(i);
|
88
|
153
|
String data = field.data;
|
|
@@ -95,7 +160,7 @@ public class RNFetchBlobBody extends RequestBody{
|
95
|
160
|
if (field.filename != null) {
|
96
|
161
|
header += "Content-Disposition: form-data; name=" + name + "; filename=" + field.filename + "\r\n";
|
97
|
162
|
header += "Content-Type: " + field.mime + "\r\n\r\n";
|
98
|
|
- sink.write(header.getBytes());
|
|
163
|
+ os.write(header.getBytes());
|
99
|
164
|
// file field header end
|
100
|
165
|
// upload from storage
|
101
|
166
|
if (data.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
|
|
@@ -106,7 +171,7 @@ public class RNFetchBlobBody extends RequestBody{
|
106
|
171
|
try {
|
107
|
172
|
String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
|
108
|
173
|
InputStream in = ctx.getAssets().open(assetName);
|
109
|
|
- pipeStreamToSink(in, sink);
|
|
174
|
+ pipeStreamToFileStream(in, os);
|
110
|
175
|
} catch (IOException e) {
|
111
|
176
|
Log.e("RNFetchBlob", "Failed to create form data asset :" + orgPath + ", " + e.getLocalizedMessage() );
|
112
|
177
|
}
|
|
@@ -116,7 +181,7 @@ public class RNFetchBlobBody extends RequestBody{
|
116
|
181
|
File file = new File(RNFetchBlobFS.normalizePath(orgPath));
|
117
|
182
|
if(file.exists()) {
|
118
|
183
|
FileInputStream fs = new FileInputStream(file);
|
119
|
|
- pipeStreamToSink(fs, sink);
|
|
184
|
+ pipeStreamToFileStream(fs, os);
|
120
|
185
|
}
|
121
|
186
|
else {
|
122
|
187
|
Log.e("RNFetchBlob", "Failed to create form data from path :" + orgPath + "file not exists.");
|
|
@@ -126,7 +191,7 @@ public class RNFetchBlobBody extends RequestBody{
|
126
|
191
|
// base64 embedded file content
|
127
|
192
|
else {
|
128
|
193
|
byte[] b = Base64.decode(data, 0);
|
129
|
|
- sink.write(b);
|
|
194
|
+ os.write(b);
|
130
|
195
|
bytesWritten += b.length;
|
131
|
196
|
emitUploadProgress();
|
132
|
197
|
}
|
|
@@ -136,70 +201,30 @@ public class RNFetchBlobBody extends RequestBody{
|
136
|
201
|
else {
|
137
|
202
|
header += "Content-Disposition: form-data; name=" + name + "\r\n";
|
138
|
203
|
header += "Content-Type: " + field.mime + "\r\n\r\n";
|
139
|
|
- sink.write(header.getBytes());
|
|
204
|
+ os.write(header.getBytes());
|
140
|
205
|
byte[] fieldData = field.data.getBytes();
|
141
|
206
|
bytesWritten += fieldData.length;
|
142
|
|
- sink.write(fieldData);
|
|
207
|
+ os.write(fieldData);
|
143
|
208
|
}
|
144
|
209
|
// form end
|
145
|
|
- sink.write("\r\n".getBytes());
|
|
210
|
+ os.write("\r\n".getBytes());
|
146
|
211
|
}
|
147
|
212
|
// close the form
|
148
|
213
|
byte[] end = ("--" + boundary + "--\r\n").getBytes();
|
149
|
|
- sink.write(end);
|
150
|
|
- }
|
151
|
|
-
|
152
|
|
- /**
|
153
|
|
- * Write octet stream data to request body
|
154
|
|
- * @param sink
|
155
|
|
- */
|
156
|
|
- private void writeOctetData(BufferedSink sink) throws IOException {
|
157
|
|
- // upload from storage
|
158
|
|
- if (rawBody.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
|
159
|
|
- String orgPath = rawBody.substring(RNFetchBlobConst.FILE_PREFIX.length());
|
160
|
|
- orgPath = RNFetchBlobFS.normalizePath(orgPath);
|
161
|
|
- // upload file from assets
|
162
|
|
- if (RNFetchBlobFS.isAsset(orgPath)) {
|
163
|
|
- try {
|
164
|
|
- String assetName = orgPath.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "");
|
165
|
|
- contentLength = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
|
166
|
|
- requestStream = RNFetchBlob.RCTContext.getAssets().open(assetName);
|
167
|
|
- } catch (IOException e) {
|
168
|
|
-// e.printStackTrace();
|
169
|
|
- }
|
170
|
|
- } else {
|
171
|
|
- File f = new File(RNFetchBlobFS.normalizePath(orgPath));
|
172
|
|
- try {
|
173
|
|
- if(!f.exists())
|
174
|
|
- f.createNewFile();
|
175
|
|
- contentLength = f.length();
|
176
|
|
- requestStream = new FileInputStream(f);
|
177
|
|
- } catch (Exception e) {
|
178
|
|
-// callback.invoke(e.getLocalizedMessage(), null);
|
179
|
|
- }
|
180
|
|
- }
|
181
|
|
- } else {
|
182
|
|
- try {
|
183
|
|
- byte[] bytes = Base64.decode(rawBody, 0);
|
184
|
|
- contentLength = bytes.length;
|
185
|
|
- requestStream = new ByteArrayInputStream(bytes);
|
186
|
|
- } catch(Exception ex) {
|
187
|
|
-
|
188
|
|
- Log.e("error", ex.getLocalizedMessage());
|
189
|
|
- }
|
190
|
|
- }
|
191
|
|
- if(requestStream != null)
|
192
|
|
- pipeStreamToSink(requestStream, sink);
|
193
|
|
-
|
|
214
|
+ os.write(end);
|
|
215
|
+ os.flush();
|
|
216
|
+ os.close();
|
|
217
|
+ return outputFile;
|
194
|
218
|
}
|
195
|
219
|
|
196
|
|
-
|
197
|
220
|
/**
|
198
|
221
|
* Write data to request body as-is
|
199
|
222
|
* @param sink
|
200
|
223
|
*/
|
201
|
224
|
private void writeRawData(BufferedSink sink) throws IOException {
|
202
|
|
- sink.write(rawBody.getBytes());
|
|
225
|
+ byte[] bytes = rawBody.getBytes();
|
|
226
|
+ contentLength = bytes.length;
|
|
227
|
+ sink.write(bytes);
|
203
|
228
|
}
|
204
|
229
|
|
205
|
230
|
/**
|
|
@@ -210,27 +235,29 @@ public class RNFetchBlobBody extends RequestBody{
|
210
|
235
|
*/
|
211
|
236
|
private void pipeStreamToSink(InputStream stream, BufferedSink sink) throws IOException {
|
212
|
237
|
byte [] chunk = new byte[10240];
|
213
|
|
- int read = stream.read(chunk, 0, 10240);
|
214
|
|
- if(read > 0) {
|
215
|
|
- sink.write(chunk, 0, read);
|
216
|
|
- }
|
217
|
|
- bytesWritten += read;
|
218
|
|
- while(read > 0) {
|
219
|
|
- read = stream.read(chunk, 0, 10240);
|
|
238
|
+ int read;
|
|
239
|
+ while((read = stream.read(chunk, 0, 10240)) > 0) {
|
220
|
240
|
if(read > 0) {
|
221
|
241
|
sink.write(chunk, 0, read);
|
222
|
|
- bytesWritten += read;
|
223
|
|
- emitUploadProgress();
|
224
|
242
|
}
|
225
|
|
-
|
226
|
243
|
}
|
227
|
244
|
stream.close();
|
228
|
245
|
}
|
229
|
246
|
|
230
|
|
- private void writeBufferToSink(byte [] bytes, BufferedSink sink) throws IOException {
|
231
|
|
- bytesWritten += bytes.length;
|
232
|
|
- sink.write(bytes);
|
233
|
|
- emitUploadProgress();
|
|
247
|
+ /**
|
|
248
|
+ * Pipe input stream to a file
|
|
249
|
+ * @param is The input stream
|
|
250
|
+ * @param os The output stream to a file
|
|
251
|
+ * @throws IOException
|
|
252
|
+ */
|
|
253
|
+ private void pipeStreamToFileStream(InputStream is, FileOutputStream os) throws IOException {
|
|
254
|
+
|
|
255
|
+ byte[] buf = new byte[10240];
|
|
256
|
+ int len;
|
|
257
|
+ while ((len = is.read(buf)) > 0) {
|
|
258
|
+ os.write(buf, 0, len);
|
|
259
|
+ }
|
|
260
|
+ is.close();
|
234
|
261
|
}
|
235
|
262
|
|
236
|
263
|
/**
|
|
@@ -274,7 +301,7 @@ public class RNFetchBlobBody extends RequestBody{
|
274
|
301
|
}
|
275
|
302
|
// data field
|
276
|
303
|
else {
|
277
|
|
- total += field.data != null ? field.data.length() : 0;
|
|
304
|
+ total += field.data != null ? field.data.getBytes().length : 0;
|
278
|
305
|
}
|
279
|
306
|
}
|
280
|
307
|
contentLength = total;
|