25
25
import java .nio .charset .StandardCharsets ;
26
26
import java .util .ArrayList ;
27
27
import java .util .Collections ;
28
+ import java .util .HashMap ;
28
29
import java .util .Iterator ;
29
30
import java .util .List ;
30
31
import java .util .Map ;
@@ -148,10 +149,17 @@ public void addPartConverter(HttpMessageConverter<?> partConverter) {
148
149
/**
149
150
* Set the default character set to use for reading and writing form data when
150
151
* the request or response Content-Type header does not explicitly specify it.
151
- * <p>By default this is set to "UTF-8". As of 4.3, it will also be used as
152
- * the default charset for the conversion of text bodies in a multipart request.
153
- * In contrast to this, {@link #setMultipartCharset} only affects the encoding of
154
- * <i>file names</i> in a multipart request according to the encoded-word syntax.
152
+ *
153
+ * <p>As of 4.3, this is also used as the default charset for the conversion
154
+ * of text bodies in a multipart request.
155
+ *
156
+ * <p>As of 5.0 this is also used for part headers including
157
+ * "Content-Disposition" (and its filename parameter) unless (the mutually
158
+ * exclusive) {@link #setMultipartCharset} is also set, in which case part
159
+ * headers are encoded as ASCII and <i>filename</i> is encoded with the
160
+ * "encoded-word" syntax from RFC 2047.
161
+ *
162
+ * <p>By default this is set to "UTF-8".
155
163
*/
156
164
public void setCharset (Charset charset ) {
157
165
if (charset != this .charset ) {
@@ -177,9 +185,13 @@ private void applyDefaultCharset() {
177
185
178
186
/**
179
187
* Set the character set to use when writing multipart data to encode file
180
- * names. Encoding is based on the encoded-word syntax defined in RFC 2047
188
+ * names. Encoding is based on the " encoded-word" syntax defined in RFC 2047
181
189
* and relies on {@code MimeUtility} from "javax.mail".
182
- * <p>If not set file names will be encoded as US-ASCII.
190
+ *
191
+ * <p>As of 5.0 by default part headers, including Content-Disposition (and
192
+ * its filename parameter) will be encoded based on the setting of
193
+ * {@link #setCharset(Charset)} or {@code UTF-8} by default.
194
+ *
183
195
* @since 4.1.1
184
196
* @see <a href="http://en.wikipedia.org/wiki/MIME#Encoded-Word">Encoded-Word</a>
185
197
*/
@@ -322,7 +334,11 @@ public void writeTo(OutputStream outputStream) throws IOException {
322
334
323
335
private void writeMultipart (final MultiValueMap <String , Object > parts , HttpOutputMessage outputMessage ) throws IOException {
324
336
final byte [] boundary = generateMultipartBoundary ();
325
- Map <String , String > parameters = Collections .singletonMap ("boundary" , new String (boundary , "US-ASCII" ));
337
+ Map <String , String > parameters = new HashMap <>(2 );
338
+ parameters .put ("boundary" , new String (boundary , "US-ASCII" ));
339
+ if (!isFilenameCharsetSet ()) {
340
+ parameters .put ("charset" , this .charset .name ());
341
+ }
326
342
327
343
MediaType contentType = new MediaType (MediaType .MULTIPART_FORM_DATA , parameters );
328
344
HttpHeaders headers = outputMessage .getHeaders ();
@@ -344,6 +360,15 @@ public void writeTo(OutputStream outputStream) throws IOException {
344
360
}
345
361
}
346
362
363
+ /**
364
+ * When {@link #setMultipartCharset(Charset)} is configured (i.e. RFC 2047,
365
+ * "encoded-word" syntax) we need to use ASCII for part headers or otherwise
366
+ * we encode directly using the configured {@link #setCharset(Charset)}.
367
+ */
368
+ private boolean isFilenameCharsetSet () {
369
+ return this .multipartCharset != null ;
370
+ }
371
+
347
372
private void writeParts (OutputStream os , MultiValueMap <String , Object > parts , byte [] boundary ) throws IOException {
348
373
for (Map .Entry <String , List <Object >> entry : parts .entrySet ()) {
349
374
String name = entry .getKey ();
@@ -365,7 +390,8 @@ private void writePart(String name, HttpEntity<?> partEntity, OutputStream os) t
365
390
MediaType partContentType = partHeaders .getContentType ();
366
391
for (HttpMessageConverter <?> messageConverter : this .partConverters ) {
367
392
if (messageConverter .canWrite (partType , partContentType )) {
368
- HttpOutputMessage multipartMessage = new MultipartHttpOutputMessage (os );
393
+ Charset charset = isFilenameCharsetSet () ? StandardCharsets .US_ASCII : this .charset ;
394
+ HttpOutputMessage multipartMessage = new MultipartHttpOutputMessage (os , charset );
369
395
multipartMessage .getHeaders ().setContentDispositionFormData (name , getFilename (partBody ));
370
396
if (!partHeaders .isEmpty ()) {
371
397
multipartMessage .getHeaders ().putAll (partHeaders );
@@ -378,7 +404,6 @@ private void writePart(String name, HttpEntity<?> partEntity, OutputStream os) t
378
404
"found for request type [" + partType .getName () + "]" );
379
405
}
380
406
381
-
382
407
/**
383
408
* Generate a multipart boundary.
384
409
* <p>This implementation delegates to
@@ -451,12 +476,15 @@ private static class MultipartHttpOutputMessage implements HttpOutputMessage {
451
476
452
477
private final OutputStream outputStream ;
453
478
479
+ private final Charset charset ;
480
+
454
481
private final HttpHeaders headers = new HttpHeaders ();
455
482
456
483
private boolean headersWritten = false ;
457
484
458
- public MultipartHttpOutputMessage (OutputStream outputStream ) {
485
+ public MultipartHttpOutputMessage (OutputStream outputStream , Charset charset ) {
459
486
this .outputStream = outputStream ;
487
+ this .charset = charset ;
460
488
}
461
489
462
490
@ Override
@@ -473,9 +501,9 @@ public OutputStream getBody() throws IOException {
473
501
private void writeHeaders () throws IOException {
474
502
if (!this .headersWritten ) {
475
503
for (Map .Entry <String , List <String >> entry : this .headers .entrySet ()) {
476
- byte [] headerName = getAsciiBytes (entry .getKey ());
504
+ byte [] headerName = getBytes (entry .getKey ());
477
505
for (String headerValueString : entry .getValue ()) {
478
- byte [] headerValue = getAsciiBytes (headerValueString );
506
+ byte [] headerValue = getBytes (headerValueString );
479
507
this .outputStream .write (headerName );
480
508
this .outputStream .write (':' );
481
509
this .outputStream .write (' ' );
@@ -488,8 +516,8 @@ private void writeHeaders() throws IOException {
488
516
}
489
517
}
490
518
491
- private byte [] getAsciiBytes (String name ) {
492
- return name .getBytes (StandardCharsets . US_ASCII );
519
+ private byte [] getBytes (String name ) {
520
+ return name .getBytes (this . charset );
493
521
}
494
522
}
495
523
0 commit comments