Support multipart filename with charset
StandardMultipartHttpServletRequest now supports filenames with charset information. Issue: SPR-13319
This commit is contained in:
@@ -19,9 +19,9 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.servlet.MultipartConfigElement;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
@@ -29,7 +29,6 @@ import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
@@ -43,6 +42,7 @@ import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
@@ -52,9 +52,9 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
|
||||
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
@@ -66,7 +66,8 @@ import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
/**
|
||||
* Test access to parts of a multipart request with {@link RequestPart}.
|
||||
@@ -117,7 +118,7 @@ public class RequestPartIntegrationTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
ByteArrayHttpMessageConverter emptyBodyConverter = new ByteArrayHttpMessageConverter();
|
||||
emptyBodyConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON));
|
||||
emptyBodyConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>(3);
|
||||
converters.add(emptyBodyConverter);
|
||||
@@ -129,7 +130,7 @@ public class RequestPartIntegrationTests {
|
||||
converter.setPartConverters(converters);
|
||||
|
||||
restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
|
||||
restTemplate.setMessageConverters(Arrays.<HttpMessageConverter<?>>asList(converter));
|
||||
restTemplate.setMessageConverters(Collections.singletonList(converter));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@@ -150,6 +151,37 @@ public class RequestPartIntegrationTests {
|
||||
testCreate(baseUrl + "/standard-resolver/test");
|
||||
}
|
||||
|
||||
// SPR-13319
|
||||
|
||||
@Test
|
||||
public void standardMultipartResolverWithEncodedFileName() throws Exception {
|
||||
|
||||
byte[] boundary = MimeTypeUtils.generateMultipartBoundary();
|
||||
String boundaryText = new String(boundary, "US-ASCII");
|
||||
Map<String, String> params = Collections.singletonMap("boundary", boundaryText);
|
||||
|
||||
String content =
|
||||
"--" + boundaryText + "\n" +
|
||||
"Content-Disposition: form-data; name=\"file\"; filename*=\"utf-8''%C3%A9l%C3%A8ve.txt\"\n" +
|
||||
"Content-Type: text/plain\n" +
|
||||
"Content-Length: 7\n" +
|
||||
"\n" +
|
||||
"content\n" +
|
||||
"--" + boundaryText + "--";
|
||||
|
||||
RequestEntity<byte[]> requestEntity =
|
||||
RequestEntity.post(new URI(baseUrl + "/standard-resolver/spr13319"))
|
||||
.contentType(new MediaType(MediaType.MULTIPART_FORM_DATA, params))
|
||||
.body(content.getBytes(Charset.forName("us-ascii")));
|
||||
|
||||
ByteArrayHttpMessageConverter converter = new ByteArrayHttpMessageConverter();
|
||||
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.MULTIPART_FORM_DATA));
|
||||
this.restTemplate.setMessageConverters(Collections.singletonList(converter));
|
||||
|
||||
ResponseEntity<Void> responseEntity = restTemplate.exchange(requestEntity, Void.class);
|
||||
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
|
||||
}
|
||||
|
||||
private void testCreate(String url) {
|
||||
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
|
||||
parts.add("json-data", new HttpEntity<TestData>(new TestData("Jason")));
|
||||
@@ -176,6 +208,7 @@ public class RequestPartIntegrationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@SuppressWarnings("unused")
|
||||
static class CommonsMultipartResolverTestConfig extends RequestPartTestConfig {
|
||||
|
||||
@Bean
|
||||
@@ -185,6 +218,7 @@ public class RequestPartIntegrationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@SuppressWarnings("unused")
|
||||
static class StandardMultipartResolverTestConfig extends RequestPartTestConfig {
|
||||
|
||||
@Bean
|
||||
@@ -194,9 +228,10 @@ public class RequestPartIntegrationTests {
|
||||
}
|
||||
|
||||
@Controller
|
||||
@SuppressWarnings("unused")
|
||||
private static class RequestPartTestController {
|
||||
|
||||
@RequestMapping(value = "/test", method = RequestMethod.POST, consumes = { "multipart/mixed", "multipart/form-data" })
|
||||
@RequestMapping(value = "/test", method = POST, consumes = { "multipart/mixed", "multipart/form-data" })
|
||||
public ResponseEntity<Object> create(@RequestPart(name = "json-data") TestData testData,
|
||||
@RequestPart("file-data") MultipartFile file,
|
||||
@RequestPart(name = "empty-data", required = false) TestData emptyData,
|
||||
@@ -209,6 +244,12 @@ public class RequestPartIntegrationTests {
|
||||
headers.setLocation(URI.create(url));
|
||||
return new ResponseEntity<Object>(headers, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/spr13319", method = POST, consumes = "multipart/form-data")
|
||||
public ResponseEntity<Void> create(@RequestPart("file") MultipartFile multipartFile) {
|
||||
assertEquals("%C3%A9l%C3%A8ve.txt", multipartFile.getOriginalFilename());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
Reference in New Issue
Block a user