1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.client5.http.entity.mime;
29
30 import java.io.File;
31 import java.io.InputStream;
32 import java.nio.charset.Charset;
33 import java.nio.charset.StandardCharsets;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.UUID;
38
39 import org.apache.hc.core5.http.ContentType;
40 import org.apache.hc.core5.http.HttpEntity;
41 import org.apache.hc.core5.http.NameValuePair;
42 import org.apache.hc.core5.http.message.BasicNameValuePair;
43 import org.apache.hc.core5.util.Args;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public class MultipartEntityBuilder {
63
64 private ContentType contentType;
65 private HttpMultipartMode mode = HttpMultipartMode.STRICT;
66 private String boundary;
67 private Charset charset;
68 private List<MultipartPart> multipartParts;
69
70
71 private static final String BOUNDARY_PREFIX = "httpclient_boundary_";
72
73
74
75
76 private static final Logger LOG = LoggerFactory.getLogger(MultipartEntityBuilder.class);
77
78
79
80
81
82
83
84 private String preamble;
85
86
87
88
89
90
91 private String epilogue;
92
93
94
95
96 private static final NameValuePair[] EMPTY_NAME_VALUE_ARRAY = {};
97
98 public static MultipartEntityBuilder create() {
99 return new MultipartEntityBuilder();
100 }
101
102 MultipartEntityBuilder() {
103 }
104
105 public MultipartEntityBuilder setMode(final HttpMultipartMode mode) {
106 this.mode = mode;
107 return this;
108 }
109
110 public MultipartEntityBuilder setLaxMode() {
111 this.mode = HttpMultipartMode.LEGACY;
112 return this;
113 }
114
115 public MultipartEntityBuilder setStrictMode() {
116 this.mode = HttpMultipartMode.STRICT;
117 return this;
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public MultipartEntityBuilder setBoundary(final String boundary) {
134 this.boundary = boundary;
135 return this;
136 }
137
138
139
140
141 public MultipartEntityBuilder setMimeSubtype(final String subType) {
142 Args.notBlank(subType, "MIME subtype");
143 this.contentType = ContentType.create("multipart/" + subType);
144 return this;
145 }
146
147
148
149
150
151 public MultipartEntityBuilder setContentType(final ContentType contentType) {
152 Args.notNull(contentType, "Content type");
153 this.contentType = contentType;
154 return this;
155 }
156
157
158
159
160
161
162
163 public MultipartEntityBuilder addParameter(final BasicNameValuePair parameter) {
164 this.contentType = contentType.withParameters(parameter);
165 return this;
166 }
167
168 public MultipartEntityBuilder setCharset(final Charset charset) {
169 this.charset = charset;
170 return this;
171 }
172
173
174
175
176 public MultipartEntityBuilder addPart(final MultipartPart multipartPart) {
177 if (multipartPart == null) {
178 return this;
179 }
180 if (this.multipartParts == null) {
181 this.multipartParts = new ArrayList<>();
182 }
183 this.multipartParts.add(multipartPart);
184 return this;
185 }
186
187 public MultipartEntityBuilder addPart(final String name, final ContentBody contentBody) {
188 Args.notNull(name, "Name");
189 Args.notNull(contentBody, "Content body");
190 return addPart(FormBodyPartBuilder.create(name, contentBody).build());
191 }
192
193 public MultipartEntityBuilder addTextBody(
194 final String name, final String text, final ContentType contentType) {
195 return addPart(name, new StringBody(text, contentType));
196 }
197
198 public MultipartEntityBuilder addTextBody(
199 final String name, final String text) {
200 return addTextBody(name, text, ContentType.DEFAULT_TEXT);
201 }
202
203 public MultipartEntityBuilder addBinaryBody(
204 final String name, final byte[] b, final ContentType contentType, final String filename) {
205 return addPart(name, new ByteArrayBody(b, contentType, filename));
206 }
207
208 public MultipartEntityBuilder addBinaryBody(
209 final String name, final byte[] b) {
210 return addPart(name, new ByteArrayBody(b, ContentType.DEFAULT_BINARY));
211 }
212
213 public MultipartEntityBuilder addBinaryBody(
214 final String name, final File file, final ContentType contentType, final String filename) {
215 return addPart(name, new FileBody(file, contentType, filename));
216 }
217
218 public MultipartEntityBuilder addBinaryBody(
219 final String name, final File file) {
220 return addBinaryBody(name, file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null);
221 }
222
223 public MultipartEntityBuilder addBinaryBody(
224 final String name, final InputStream stream, final ContentType contentType,
225 final String filename) {
226 return addPart(name, new InputStreamBody(stream, contentType, filename));
227 }
228
229 public MultipartEntityBuilder addBinaryBody(final String name, final InputStream stream) {
230 return addBinaryBody(name, stream, ContentType.DEFAULT_BINARY, null);
231 }
232
233
234
235
236
237
238
239
240
241 private String getRandomBoundary() {
242 return BOUNDARY_PREFIX + UUID.randomUUID();
243 }
244
245
246
247
248
249
250
251
252
253
254 public MultipartEntityBuilder addPreamble(final String preamble) {
255 this.preamble = preamble;
256 return this;
257 }
258
259
260
261
262
263
264
265
266
267 public MultipartEntityBuilder addEpilogue(final String epilogue) {
268 this.epilogue = epilogue;
269 return this;
270 }
271
272 MultipartFormEntity buildEntity() {
273 String boundaryCopy = boundary;
274 if (boundaryCopy == null && contentType != null) {
275 boundaryCopy = contentType.getParameter("boundary");
276 }
277 if (boundaryCopy == null) {
278 boundaryCopy = getRandomBoundary();
279 }
280 Charset charsetCopy = charset;
281 if (charsetCopy == null && contentType != null) {
282 charsetCopy = contentType.getCharset();
283 }
284 final NameValuePair[] params = new NameValuePair[]{new BasicNameValuePair("boundary", boundaryCopy)};
285
286 final ContentType contentTypeCopy;
287 if (contentType != null) {
288 contentTypeCopy = contentType.withParameters(params);
289 } else {
290 boolean formData = false;
291 if (multipartParts != null) {
292 for (final MultipartPart multipartPart : multipartParts) {
293 if (multipartPart instanceof FormBodyPart) {
294 formData = true;
295 break;
296 }
297 }
298 }
299
300 if (formData) {
301 contentTypeCopy = ContentType.MULTIPART_FORM_DATA.withParameters(params);
302 } else {
303 contentTypeCopy = ContentType.create("multipart/mixed", params);
304 }
305 }
306 final List<MultipartPart> multipartPartsCopy = multipartParts != null ? new ArrayList<>(multipartParts) :
307 Collections.emptyList();
308 final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;
309 final AbstractMultipartFormat form;
310 switch (modeCopy) {
311 case LEGACY:
312 form = new LegacyMultipart(charsetCopy, boundaryCopy, multipartPartsCopy);
313 break;
314 case EXTENDED:
315 if (contentTypeCopy.isSameMimeType(ContentType.MULTIPART_FORM_DATA)) {
316 if (charsetCopy == null) {
317 charsetCopy = StandardCharsets.UTF_8;
318 }
319 form = new HttpRFC7578Multipart(charsetCopy, boundaryCopy, multipartPartsCopy, preamble, epilogue, modeCopy);
320 } else {
321 form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, multipartPartsCopy, preamble, epilogue);
322 }
323 break;
324 default:
325 form = new HttpStrictMultipart(StandardCharsets.US_ASCII, boundaryCopy, multipartPartsCopy, preamble, epilogue);
326 }
327 return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
328 }
329
330 public HttpEntity build() {
331 return buildEntity();
332 }
333
334 }