1919import java .net .URI ;
2020import java .nio .charset .Charset ;
2121import java .util .ArrayList ;
22- import java .util .Arrays ;
22+ import java .util .Collections ;
2323import java .util .List ;
24-
24+ import java . util . Map ;
2525import javax .servlet .MultipartConfigElement ;
2626
2727import org .eclipse .jetty .server .Connector ;
2828import org .eclipse .jetty .server .NetworkConnector ;
2929import org .eclipse .jetty .server .Server ;
3030import org .eclipse .jetty .servlet .ServletContextHandler ;
3131import org .eclipse .jetty .servlet .ServletHolder ;
32-
3332import org .junit .AfterClass ;
3433import org .junit .Assert ;
3534import org .junit .Before ;
4342import org .springframework .http .HttpHeaders ;
4443import org .springframework .http .HttpStatus ;
4544import org .springframework .http .MediaType ;
45+ import org .springframework .http .RequestEntity ;
4646import org .springframework .http .ResponseEntity ;
4747import org .springframework .http .client .HttpComponentsClientHttpRequestFactory ;
4848import org .springframework .http .converter .ByteArrayHttpMessageConverter ;
5252import org .springframework .http .converter .support .AllEncompassingFormHttpMessageConverter ;
5353import org .springframework .stereotype .Controller ;
5454import org .springframework .util .LinkedMultiValueMap ;
55+ import org .springframework .util .MimeTypeUtils ;
5556import org .springframework .util .MultiValueMap ;
5657import org .springframework .web .bind .annotation .RequestMapping ;
57- import org .springframework .web .bind .annotation .RequestMethod ;
5858import org .springframework .web .bind .annotation .RequestPart ;
5959import org .springframework .web .client .RestTemplate ;
6060import org .springframework .web .context .support .AnnotationConfigWebApplicationContext ;
6666import org .springframework .web .servlet .config .annotation .EnableWebMvc ;
6767import org .springframework .web .servlet .config .annotation .WebMvcConfigurerAdapter ;
6868
69- import static org .junit .Assert .*;
69+ import static org .junit .Assert .assertEquals ;
70+ import static org .springframework .web .bind .annotation .RequestMethod .POST ;
7071
7172/**
7273 * Test access to parts of a multipart request with {@link RequestPart}.
@@ -117,7 +118,7 @@ public static void startServer() throws Exception {
117118 @ Before
118119 public void setUp () {
119120 ByteArrayHttpMessageConverter emptyBodyConverter = new ByteArrayHttpMessageConverter ();
120- emptyBodyConverter .setSupportedMediaTypes (Arrays . asList (MediaType .APPLICATION_JSON ));
121+ emptyBodyConverter .setSupportedMediaTypes (Collections . singletonList (MediaType .APPLICATION_JSON ));
121122
122123 List <HttpMessageConverter <?>> converters = new ArrayList <>(3 );
123124 converters .add (emptyBodyConverter );
@@ -129,7 +130,7 @@ public void setUp() {
129130 converter .setPartConverters (converters );
130131
131132 restTemplate = new RestTemplate (new HttpComponentsClientHttpRequestFactory ());
132- restTemplate .setMessageConverters (Arrays .< HttpMessageConverter <?>> asList (converter ));
133+ restTemplate .setMessageConverters (Collections . singletonList (converter ));
133134 }
134135
135136 @ AfterClass
@@ -150,6 +151,37 @@ public void standardMultipartResolver() throws Exception {
150151 testCreate (baseUrl + "/standard-resolver/test" );
151152 }
152153
154+ // SPR-13319
155+
156+ @ Test
157+ public void standardMultipartResolverWithEncodedFileName () throws Exception {
158+
159+ byte [] boundary = MimeTypeUtils .generateMultipartBoundary ();
160+ String boundaryText = new String (boundary , "US-ASCII" );
161+ Map <String , String > params = Collections .singletonMap ("boundary" , boundaryText );
162+
163+ String content =
164+ "--" + boundaryText + "\n " +
165+ "Content-Disposition: form-data; name=\" file\" ; filename*=\" utf-8''%C3%A9l%C3%A8ve.txt\" \n " +
166+ "Content-Type: text/plain\n " +
167+ "Content-Length: 7\n " +
168+ "\n " +
169+ "content\n " +
170+ "--" + boundaryText + "--" ;
171+
172+ RequestEntity <byte []> requestEntity =
173+ RequestEntity .post (new URI (baseUrl + "/standard-resolver/spr13319" ))
174+ .contentType (new MediaType (MediaType .MULTIPART_FORM_DATA , params ))
175+ .body (content .getBytes (Charset .forName ("us-ascii" )));
176+
177+ ByteArrayHttpMessageConverter converter = new ByteArrayHttpMessageConverter ();
178+ converter .setSupportedMediaTypes (Collections .singletonList (MediaType .MULTIPART_FORM_DATA ));
179+ this .restTemplate .setMessageConverters (Collections .singletonList (converter ));
180+
181+ ResponseEntity <Void > responseEntity = restTemplate .exchange (requestEntity , Void .class );
182+ assertEquals (HttpStatus .OK , responseEntity .getStatusCode ());
183+ }
184+
153185 private void testCreate (String url ) {
154186 MultiValueMap <String , Object > parts = new LinkedMultiValueMap <String , Object >();
155187 parts .add ("json-data" , new HttpEntity <TestData >(new TestData ("Jason" )));
@@ -176,6 +208,7 @@ public RequestPartTestController controller() {
176208 }
177209
178210 @ Configuration
211+ @ SuppressWarnings ("unused" )
179212 static class CommonsMultipartResolverTestConfig extends RequestPartTestConfig {
180213
181214 @ Bean
@@ -185,6 +218,7 @@ public MultipartResolver multipartResolver() {
185218 }
186219
187220 @ Configuration
221+ @ SuppressWarnings ("unused" )
188222 static class StandardMultipartResolverTestConfig extends RequestPartTestConfig {
189223
190224 @ Bean
@@ -194,9 +228,10 @@ public MultipartResolver multipartResolver() {
194228 }
195229
196230 @ Controller
231+ @ SuppressWarnings ("unused" )
197232 private static class RequestPartTestController {
198233
199- @ RequestMapping (value = "/test" , method = RequestMethod . POST , consumes = { "multipart/mixed" , "multipart/form-data" })
234+ @ RequestMapping (value = "/test" , method = POST , consumes = { "multipart/mixed" , "multipart/form-data" })
200235 public ResponseEntity <Object > create (@ RequestPart (name = "json-data" ) TestData testData ,
201236 @ RequestPart ("file-data" ) MultipartFile file ,
202237 @ RequestPart (name = "empty-data" , required = false ) TestData emptyData ,
@@ -209,6 +244,12 @@ public ResponseEntity<Object> create(@RequestPart(name = "json-data") TestData t
209244 headers .setLocation (URI .create (url ));
210245 return new ResponseEntity <Object >(headers , HttpStatus .CREATED );
211246 }
247+
248+ @ RequestMapping (value = "/spr13319" , method = POST , consumes = "multipart/form-data" )
249+ public ResponseEntity <Void > create (@ RequestPart ("file" ) MultipartFile multipartFile ) {
250+ assertEquals ("%C3%A9l%C3%A8ve.txt" , multipartFile .getOriginalFilename ());
251+ return ResponseEntity .ok ().build ();
252+ }
212253 }
213254
214255 @ SuppressWarnings ("unused" )
0 commit comments