diff --git a/build.gradle b/build.gradle index 0f6389e4d67..8047b1cfd9e 100644 --- a/build.gradle +++ b/build.gradle @@ -838,7 +838,6 @@ project("spring-webflux") { testRuntime("org.python:jython-standalone:2.5.3") testRuntime("org.webjars:underscorejs:1.8.3") testRuntime("org.glassfish:javax.el:3.0.1-b08") - testRuntime("com.sun.mail:javax.mail:${javamailVersion}") testRuntime("com.sun.xml.bind:jaxb-core:${jaxbVersion}") testRuntime("com.sun.xml.bind:jaxb-impl:${jaxbVersion}") testRuntime("org.synchronoss.cloud:nio-multipart-parser:${niomultipartVersion}") diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java index 1d9fb69d8a9..c08f7df83bc 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java @@ -16,18 +16,17 @@ package org.springframework.http.codec.multipart; -import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.mail.internet.MimeUtility; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -69,7 +68,7 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter> partWriters; - private Charset filenameCharset = DEFAULT_CHARSET; + private Charset charset = DEFAULT_CHARSET; private final DataBufferFactory bufferFactory = new DefaultDataBufferFactory(); @@ -86,19 +85,20 @@ public class MultipartHttpMessageWriter implements HttpMessageWriterBy default this is set to "UTF-8". */ - public void setFilenameCharset(Charset charset) { + public void setCharset(Charset charset) { Assert.notNull(charset, "'charset' must not be null"); - this.filenameCharset = charset; + this.charset = charset; } /** - * Return the configured filename charset. + * Return the configured charset for part headers. */ - public Charset getFilenameCharset() { - return this.filenameCharset; + public Charset getCharset() { + return this.charset; } @@ -120,8 +120,10 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter params = new HashMap<>(2); + params.put("boundary", new String(boundary, StandardCharsets.US_ASCII)); + params.put("charset", getCharset().name()); + outputMessage.getHeaders().setContentType(new MediaType(MediaType.MULTIPART_FORM_DATA, params)); return Mono.from(inputStream).flatMap(map -> { @@ -149,7 +151,8 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter Flux encodePart(byte[] boundary, String name, T value) { - MultipartHttpOutputMessage outputMessage = new MultipartHttpOutputMessage(this.bufferFactory); + MultipartHttpOutputMessage outputMessage = + new MultipartHttpOutputMessage(this.bufferFactory, getCharset()); T body; if (value instanceof HttpEntity) { @@ -160,9 +163,10 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter> writer = this.partWriters.stream() @@ -189,26 +193,6 @@ public class MultipartHttpMessageWriter implements HttpMessageWriterThe default implementation returns {@link Resource#getFilename()} if - * the part is a {@code Resource}, and {@code null} in other cases. - * @param part the part for which return a file name - * @return the filename or {@code null} - */ - protected String getFilename(Object part) { - if (part instanceof Resource) { - Resource resource = (Resource) part; - String filename = resource.getFilename(); - filename = MimeDelegate.encode(filename, this.filenameCharset.name()); - return filename; - } - else { - return null; - } - } - private DataBuffer generateBoundaryLine(byte[] boundary) { DataBuffer buffer = this.bufferFactory.allocateBuffer(boundary.length + 4); buffer.write((byte)'-'); @@ -243,6 +227,8 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter body; - public MultipartHttpOutputMessage(DataBufferFactory bufferFactory) { + public MultipartHttpOutputMessage(DataBufferFactory bufferFactory, Charset charset) { this.bufferFactory = bufferFactory; + this.charset = charset; } @@ -287,9 +274,9 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter> entry : headers.entrySet()) { - byte[] headerName = entry.getKey().getBytes(StandardCharsets.US_ASCII); + byte[] headerName = entry.getKey().getBytes(this.charset); for (String headerValueString : entry.getValue()) { - byte[] headerValue = headerValueString.getBytes(StandardCharsets.US_ASCII); + byte[] headerValue = headerValueString.getBytes(this.charset); buffer.write(headerName); buffer.write((byte)':'); buffer.write((byte)' '); @@ -321,19 +308,4 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter private static class SynchronossFilePart extends DefaultSynchronossPart implements FilePart { - public SynchronossFilePart(HttpHeaders headers, StreamStorage storage, String fileName, DataBufferFactory factory) { + public SynchronossFilePart(HttpHeaders headers, StreamStorage storage, + String fileName, DataBufferFactory factory) { + super(headers, storage, factory); } diff --git a/spring-web/src/test/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriterTests.java b/spring-web/src/test/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriterTests.java index b99870e4a2c..a0eb71bc14b 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriterTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriterTests.java @@ -101,7 +101,7 @@ public class MultipartHttpMessageWriterTests { Map hints = Collections.emptyMap(); this.writer.write(Mono.just(map), null, MediaType.MULTIPART_FORM_DATA, response, hints).block(); - final MediaType contentType = response.getHeaders().getContentType(); + MediaType contentType = response.getHeaders().getContentType(); assertNotNull("No boundary found", contentType.getParameter("boundary")); // see if Synchronoss NIO Multipart can read what we wrote @@ -109,7 +109,7 @@ public class MultipartHttpMessageWriterTests { MultipartHttpMessageReader reader = new MultipartHttpMessageReader(synchronossReader); MockServerHttpRequest request = MockServerHttpRequest.post("/foo") - .header(HttpHeaders.CONTENT_TYPE, contentType.toString()) + .contentType(MediaType.parseMediaType(contentType.toString())) .body(response.getBody()); ResolvableType elementType = ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, Part.class);