Skip to content

Commit 1fd0307

Browse files
author
Stephane Landelle
committed
Don't create byte arrays for computing multipart Content-Length
1 parent e5540e5 commit 1fd0307

File tree

5 files changed

+263
-193
lines changed

5 files changed

+263
-193
lines changed

api/src/main/java/org/asynchttpclient/multipart/FilePart.java

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,14 @@ public class FilePart extends PartBase {
6161
/**
6262
* FilePart Constructor.
6363
*
64-
* @param name the name for this part
65-
* @param partSource the source for this part
66-
* @param contentType the content type for this part, if <code>null</code> the {@link #DEFAULT_CONTENT_TYPE default} is used
67-
* @param charset the charset encoding for this part, if <code>null</code> the {@link #DEFAULT_CHARSET default} is used
64+
* @param name
65+
* the name for this part
66+
* @param partSource
67+
* the source for this part
68+
* @param contentType
69+
* the content type for this part, if <code>null</code> the {@link #DEFAULT_CONTENT_TYPE default} is used
70+
* @param charset
71+
* the charset encoding for this part, if <code>null</code> the {@link #DEFAULT_CHARSET default} is used
6872
* @param contentId
6973
*/
7074
public FilePart(String name, PartSource partSource, String contentType, String charset, String contentId) {
@@ -76,16 +80,18 @@ public FilePart(String name, PartSource partSource, String contentType, String c
7680
}
7781
this.source = partSource;
7882
}
79-
83+
8084
public FilePart(String name, PartSource partSource, String contentType, String charset) {
8185
this(name, partSource, contentType, charset, null);
8286
}
8387

8488
/**
8589
* FilePart Constructor.
8690
*
87-
* @param name the name for this part
88-
* @param partSource the source for this part
91+
* @param name
92+
* the name for this part
93+
* @param partSource
94+
* the source for this part
8995
*/
9096
public FilePart(String name, PartSource partSource) {
9197
this(name, partSource, null, null);
@@ -94,9 +100,12 @@ public FilePart(String name, PartSource partSource) {
94100
/**
95101
* FilePart Constructor.
96102
*
97-
* @param name the name of the file part
98-
* @param file the file to post
99-
* @throws java.io.FileNotFoundException if the <i>file</i> is not a normal file or if it is not readable.
103+
* @param name
104+
* the name of the file part
105+
* @param file
106+
* the file to post
107+
* @throws java.io.FileNotFoundException
108+
* if the <i>file</i> is not a normal file or if it is not readable.
100109
*/
101110
public FilePart(String name, File file) throws FileNotFoundException {
102111
this(name, new FilePartSource(file), null, null);
@@ -105,11 +114,16 @@ public FilePart(String name, File file) throws FileNotFoundException {
105114
/**
106115
* FilePart Constructor.
107116
*
108-
* @param name the name of the file part
109-
* @param file the file to post
110-
* @param contentType the content type for this part, if <code>null</code> the {@link #DEFAULT_CONTENT_TYPE default} is used
111-
* @param charset the charset encoding for this part, if <code>null</code> the {@link #DEFAULT_CHARSET default} is used
112-
* @throws FileNotFoundException if the <i>file</i> is not a normal file or if it is not readable.
117+
* @param name
118+
* the name of the file part
119+
* @param file
120+
* the file to post
121+
* @param contentType
122+
* the content type for this part, if <code>null</code> the {@link #DEFAULT_CONTENT_TYPE default} is used
123+
* @param charset
124+
* the charset encoding for this part, if <code>null</code> the {@link #DEFAULT_CHARSET default} is used
125+
* @throws FileNotFoundException
126+
* if the <i>file</i> is not a normal file or if it is not readable.
113127
*/
114128
public FilePart(String name, File file, String contentType, String charset) throws FileNotFoundException {
115129
this(name, new FilePartSource(file), contentType, charset);
@@ -118,10 +132,14 @@ public FilePart(String name, File file, String contentType, String charset) thro
118132
/**
119133
* FilePart Constructor.
120134
*
121-
* @param name the name of the file part
122-
* @param fileName the file name
123-
* @param file the file to post
124-
* @throws FileNotFoundException if the <i>file</i> is not a normal file or if it is not readable.
135+
* @param name
136+
* the name of the file part
137+
* @param fileName
138+
* the file name
139+
* @param file
140+
* the file to post
141+
* @throws FileNotFoundException
142+
* if the <i>file</i> is not a normal file or if it is not readable.
125143
*/
126144
public FilePart(String name, String fileName, File file) throws FileNotFoundException {
127145
this(name, new FilePartSource(fileName, file), null, null);
@@ -130,12 +148,18 @@ public FilePart(String name, String fileName, File file) throws FileNotFoundExce
130148
/**
131149
* FilePart Constructor.
132150
*
133-
* @param name the name of the file part
134-
* @param fileName the file name
135-
* @param file the file to post
136-
* @param contentType the content type for this part, if <code>null</code> the {@link #DEFAULT_CONTENT_TYPE default} is used
137-
* @param charset the charset encoding for this part, if <code>null</code> the {@link #DEFAULT_CHARSET default} is used
138-
* @throws FileNotFoundException if the <i>file</i> is not a normal file or if it is not readable.
151+
* @param name
152+
* the name of the file part
153+
* @param fileName
154+
* the file name
155+
* @param file
156+
* the file to post
157+
* @param contentType
158+
* the content type for this part, if <code>null</code> the {@link #DEFAULT_CONTENT_TYPE default} is used
159+
* @param charset
160+
* the charset encoding for this part, if <code>null</code> the {@link #DEFAULT_CHARSET default} is used
161+
* @throws FileNotFoundException
162+
* if the <i>file</i> is not a normal file or if it is not readable.
139163
*/
140164
public FilePart(String name, String fileName, File file, String contentType, String charset) throws FileNotFoundException {
141165
this(name, new FilePartSource(fileName, file), contentType, charset);
@@ -144,8 +168,10 @@ public FilePart(String name, String fileName, File file, String contentType, Str
144168
/**
145169
* Write the disposition header to the output stream
146170
*
147-
* @param out The output stream
148-
* @throws java.io.IOException If an IO problem occurs
171+
* @param out
172+
* The output stream
173+
* @throws java.io.IOException
174+
* If an IO problem occurs
149175
*/
150176
protected void sendDispositionHeader(OutputStream out) throws IOException {
151177
super.sendDispositionHeader(out);
@@ -158,11 +184,25 @@ protected void sendDispositionHeader(OutputStream out) throws IOException {
158184
}
159185
}
160186

187+
protected long dispositionHeaderLength() {
188+
String filename = this.source.getFileName();
189+
long length = super.dispositionHeaderLength();
190+
if (filename != null) {
191+
length += FILE_NAME_BYTES.length;
192+
length += QUOTE_BYTES.length;
193+
length += MultipartEncodingUtil.getAsciiBytes(filename).length;
194+
length += QUOTE_BYTES.length;
195+
}
196+
return length;
197+
}
198+
161199
/**
162200
* Write the data in "source" to the specified stream.
163201
*
164-
* @param out The output stream.
165-
* @throws IOException if an IO problem occurs.
202+
* @param out
203+
* The output stream.
204+
* @throws IOException
205+
* if an IO problem occurs.
166206
*/
167207
protected void sendData(OutputStream out) throws IOException {
168208
if (lengthOfData() == 0) {
@@ -207,9 +247,8 @@ protected PartSource getSource() {
207247
* Return the length of the data.
208248
*
209249
* @return The length.
210-
* @throws IOException if an IO problem occurs
211250
*/
212-
protected long lengthOfData() throws IOException {
251+
protected long lengthOfData() {
213252
return source.getLength();
214253
}
215254

api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -154,16 +154,6 @@ public long read(ByteBuffer buffer) throws IOException {
154154
}
155155
}
156156

157-
private void initializeByteArrayBody(FilePart filePart) throws IOException {
158-
159-
ByteArrayOutputStream output = new ByteArrayOutputStream();
160-
filePart.sendData(output);
161-
162-
initializeBuffer(output);
163-
164-
fileLocation = FileLocation.MIDDLE;
165-
}
166-
167157
private void initializeFileEnd(FilePart currentPart) throws IOException {
168158

169159
ByteArrayOutputStream output = generateFileEnd(currentPart);
@@ -206,22 +196,15 @@ private void initializeFileBody(FilePart currentPart) throws IOException {
206196

207197
private void initializeFilePart(FilePart filePart) throws IOException {
208198

209-
filePart.setPartBoundary(boundary);
210-
211199
ByteArrayOutputStream output = generateFileStart(filePart);
212-
213200
initializeBuffer(output);
214-
215201
fileLocation = FileLocation.START;
216202
}
217203

218204
private void initializeStringPart(StringPart currentPart) throws IOException {
219-
currentPart.setPartBoundary(boundary);
220205

221206
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
222-
223207
Part.sendPart(outputStream, currentPart, boundary);
224-
225208
initializeBuffer(outputStream);
226209
}
227210

@@ -281,33 +264,25 @@ public long transferTo(long position, long count, WritableByteChannel target) th
281264
}
282265

283266
private long handleFileEnd(WritableByteChannel target, FilePart filePart) throws IOException {
284-
285267
ByteArrayOutputStream endOverhead = generateFileEnd(filePart);
286-
287268
return this.writeToTarget(target, endOverhead);
288269
}
289270

290271
private ByteArrayOutputStream generateFileEnd(FilePart filePart) throws IOException {
291272
ByteArrayOutputStream endOverhead = new ByteArrayOutputStream();
292-
293273
filePart.sendEnd(endOverhead);
294274
return endOverhead;
295275
}
296276

297277
private long handleFileHeaders(WritableByteChannel target, FilePart filePart) throws IOException {
298-
filePart.setPartBoundary(boundary);
299-
300278
ByteArrayOutputStream overhead = generateFileStart(filePart);
301-
302279
return writeToTarget(target, overhead);
303280
}
304281

305282
private ByteArrayOutputStream generateFileStart(FilePart filePart) throws IOException {
306283
ByteArrayOutputStream overhead = new ByteArrayOutputStream();
307284

308-
filePart.setPartBoundary(boundary);
309-
310-
filePart.sendStart(overhead);
285+
filePart.sendStart(overhead, boundary);
311286
filePart.sendDispositionHeader(overhead);
312287
filePart.sendContentTypeHeader(overhead);
313288
filePart.sendTransferEncodingHeader(overhead);
@@ -418,20 +393,13 @@ private long handlePartSource(WritableByteChannel target, FilePart filePart) thr
418393
}
419394

420395
private long handleStringPart(WritableByteChannel target, StringPart currentPart) throws IOException {
421-
422-
currentPart.setPartBoundary(boundary);
423-
424396
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
425-
426397
Part.sendPart(outputStream, currentPart, boundary);
427-
428398
return writeToTarget(target, outputStream);
429399
}
430400

431401
private long handleMultiPart(WritableByteChannel target, Part currentPart) throws IOException {
432402

433-
currentPart.setPartBoundary(boundary);
434-
435403
if (currentPart.getClass().equals(StringPart.class)) {
436404
return handleStringPart(target, (StringPart) currentPart);
437405
} else if (currentPart.getClass().equals(FilePart.class)) {

api/src/main/java/org/asynchttpclient/multipart/MultipartRequestEntity.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,15 @@
1515
*/
1616
package org.asynchttpclient.multipart;
1717

18-
import static org.asynchttpclient.util.MiscUtil.isNonEmpty;
19-
20-
import org.asynchttpclient.FluentCaseInsensitiveStringsMap;
21-
import org.slf4j.Logger;
22-
import org.slf4j.LoggerFactory;
18+
import static org.asynchttpclient.util.MiscUtil.*;
2319

2420
import java.io.IOException;
2521
import java.io.OutputStream;
2622
import java.util.List;
2723
import java.util.Random;
2824

25+
import org.asynchttpclient.FluentCaseInsensitiveStringsMap;
26+
2927
/**
3028
* This class is an adaptation of the Apache HttpClient implementation
3129
*
@@ -57,21 +55,22 @@ private static byte[] generateMultipartBoundary() {
5755
return bytes;
5856
}
5957

60-
private final Logger log = LoggerFactory.getLogger(MultipartRequestEntity.class);
61-
6258
/**
6359
* The MIME parts as set by the constructor
6460
*/
65-
protected final List<Part>parts;
61+
private final List<Part> parts;
6662

6763
private final byte[] multipartBoundary;
6864

6965
private final String contentType;
7066

67+
private final long contentLength;
68+
7169
/**
7270
* Creates a new multipart entity containing the given parts.
7371
*
74-
* @param parts The parts to include.
72+
* @param parts
73+
* The parts to include.
7574
*/
7675
public MultipartRequestEntity(List<Part> parts, FluentCaseInsensitiveStringsMap requestHeaders) {
7776
if (parts == null) {
@@ -94,6 +93,8 @@ public MultipartRequestEntity(List<Part> parts, FluentCaseInsensitiveStringsMap
9493
multipartBoundary = generateMultipartBoundary();
9594
contentType = computeContentType(MULTIPART_FORM_CONTENT_TYPE);
9695
}
96+
97+
contentLength = Part.getLengthOfParts(parts, multipartBoundary);
9798
}
9899

99100
private String computeContentType(String base) {
@@ -104,8 +105,8 @@ private String computeContentType(String base) {
104105
}
105106

106107
/**
107-
* Returns the MIME boundary string that is used to demarcate boundaries of this part. The first call to this method will implicitly create a new boundary string. To create a boundary string first the HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise a
108-
* random one is generated.
108+
* Returns the MIME boundary string that is used to demarcate boundaries of this part. The first call to this method will implicitly create a new boundary string. To create a
109+
* boundary string first the HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise a random one is generated.
109110
*
110111
* @return The boundary string of this entity in ASCII encoding.
111112
*/
@@ -117,7 +118,7 @@ protected byte[] getMultipartBoundary() {
117118
* Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise.
118119
*/
119120
public boolean isRepeatable() {
120-
for (Part part: parts) {
121+
for (Part part : parts) {
121122
if (!part.isRepeatable()) {
122123
return false;
123124
}
@@ -130,16 +131,10 @@ public void writeRequest(OutputStream out) throws IOException {
130131
}
131132

132133
public long getContentLength() {
133-
try {
134-
return Part.getLengthOfParts(parts, multipartBoundary);
135-
} catch (Exception e) {
136-
log.error("An exception occurred while getting the length of the parts", e);
137-
return 0;
138-
}
134+
return contentLength;
139135
}
140136

141137
public String getContentType() {
142138
return contentType;
143139
}
144140
}
145-

0 commit comments

Comments
 (0)