Commit 1ae91a13 authored by Phillip Webb's avatar Phillip Webb

Polish MultipartConfigElement support

Polish MultipartConfigElement changes introduced in commit e8e59ea6
as follows:

- Fix javadoc formatting
- Fix tab/spaces formatting
- Fix asciidoc formatting
- Move creation of MultipartConfigElement into MultipartProperties
- Add @Since tags
- Restore random port in tests
parent 3e3d1e83
...@@ -25,11 +25,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; ...@@ -25,11 +25,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.MultipartConfigFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.support.StandardServletMultipartResolver; import org.springframework.web.multipart.support.StandardServletMultipartResolver;
/** /**
...@@ -41,7 +39,7 @@ import org.springframework.web.multipart.support.StandardServletMultipartResolve ...@@ -41,7 +39,7 @@ import org.springframework.web.multipart.support.StandardServletMultipartResolve
* <p/> * <p/>
* The {@link javax.servlet.MultipartConfigElement} is a Servlet API that's used to * The {@link javax.servlet.MultipartConfigElement} is a Servlet API that's used to
* configure how the container handles file uploads. By default * configure how the container handles file uploads. By default
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Josh Long * @author Josh Long
*/ */
...@@ -57,25 +55,7 @@ public class MultipartAutoConfiguration { ...@@ -57,25 +55,7 @@ public class MultipartAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public MultipartConfigElement multipartConfigElement() { public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory(); return this.multipartProperties.createMultipartConfig();
if (StringUtils.hasText(this.multipartProperties.getFileSizeThreshold())) {
factory.setFileSizeThreshold(this.multipartProperties.getFileSizeThreshold());
}
if (StringUtils.hasText(this.multipartProperties.getLocation())) {
factory.setLocation(this.multipartProperties.getLocation());
}
if (StringUtils.hasText(this.multipartProperties.getMaxRequestSize())) {
factory.setMaxRequestSize(this.multipartProperties.getMaxRequestSize());
}
if (StringUtils.hasText(this.multipartProperties.getMaxFileSize())) {
factory.setMaxFileSize(this.multipartProperties.getMaxFileSize());
}
return factory.createMultipartConfig();
} }
@Bean @Bean
...@@ -83,4 +63,5 @@ public class MultipartAutoConfiguration { ...@@ -83,4 +63,5 @@ public class MultipartAutoConfiguration {
public StandardServletMultipartResolver multipartResolver() { public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver(); return new StandardServletMultipartResolver();
} }
} }
...@@ -16,65 +16,99 @@ ...@@ -16,65 +16,99 @@
package org.springframework.boot.autoconfigure.web; package org.springframework.boot.autoconfigure.web;
import javax.servlet.MultipartConfigElement;
import org.springframework.boot.context.embedded.MultipartConfigFactory;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;
/** /**
* Properties to be used in configuring * Properties to be used in configuring a {@link MultipartConfigElement}.
* a <A href="http://docs.oracle.com/javaee/6/api/javax/servlet/MultipartConfigElement.html">javax.servlet.MultipartConfigElement</a>. * <ul>
* <p/> * <li>{@literal multipart.location} specifies the directory where files will be stored.
* {@literal multipart.fileSizeThreshold} specifies the size threshold after which files will be written to disk. Default is 0, which means that the file will be written to disk immediately. * The default is "". A common value is to use the system's temporary directory, which can
* {@literal multipart.location} specifies the directory where files will be stored. The default is "". A common value is to use the system's temporary directory, which can be obtained * be obtained.</li>
* {@literal multipart.maxFileSize} specifies the maximum size permitted for uploaded files. The default is unlimited. * <li>{@literal multipart.maxFileSize} specifies the maximum size permitted for uploaded
* {@literal multipart.maxRequestSize} specifies the maximum size allowed for {@literal multipart/form-data} requests. * files. The default is 1Mb.</li>
* <p/> * <li>
* These properties are ultimately passed through {@link org.springframework.boot.context.embedded.MultipartConfigFactory} * {@literal multipart.maxRequestSize} specifies the maximum size allowed for
* which means you may specify the values using {@literal long} values or using more readable {@literal String} * {@literal multipart/form-data} requests. The default is 10Mb</li>
* variants that accept {@literal KB} or {@literal MB} suffixes. * <li>
* * {@literal multipart.fileSizeThreshold} specifies the size threshold after which files
* will be written to disk. Default is 0, which means that the file will be written to
* disk immediately.</li>
* </ul>
* <p>
* These properties are ultimately passed through
* {@link org.springframework.boot.context.embedded.MultipartConfigFactory} which means
* you may specify the values using {@literal long} values or using more readable
* {@literal String} variants that accept {@literal Kb} or {@literal Mb} suffixes.
*
* @author Josh Long * @author Josh Long
* @since 1.1.0
*/ */
@ConfigurationProperties(prefix = "multipart", ignoreUnknownFields = false) @ConfigurationProperties(prefix = "multipart", ignoreUnknownFields = false)
public class MultipartProperties { public class MultipartProperties {
private String maxFileSize = "1Mb"; private String location;
private String maxRequestSize = "10Mb"; private String maxFileSize = "1Mb";
private String fileSizeThreshold = null; private String maxRequestSize = "10Mb";
private String location = null; private String fileSizeThreshold = "0";
public String getMaxFileSize() { public String getMaxFileSize() {
return maxFileSize; return this.maxFileSize;
} }
public String getMaxRequestSize() { public String getMaxRequestSize() {
return maxRequestSize; return this.maxRequestSize;
} }
public String getFileSizeThreshold() { public String getFileSizeThreshold() {
return fileSizeThreshold; return this.fileSizeThreshold;
} }
public String getLocation() { public String getLocation() {
return location; return this.location;
} }
public void setMaxFileSize(String maxFileSize) { public void setMaxFileSize(String maxFileSize) {
this.maxFileSize = maxFileSize; this.maxFileSize = maxFileSize;
} }
public void setMaxRequestSize(String maxRequestSize) { public void setMaxRequestSize(String maxRequestSize) {
this.maxRequestSize = maxRequestSize; this.maxRequestSize = maxRequestSize;
} }
public void setLocation(String location) { public void setLocation(String location) {
this.location = location; this.location = location;
} }
public void setFileSizeThreshold(String fileSizeThreshold) { public void setFileSizeThreshold(String fileSizeThreshold) {
this.fileSizeThreshold = fileSizeThreshold; this.fileSizeThreshold = fileSizeThreshold;
} }
/**
* Create a new {@link MultipartConfigElement} using the
* @return a new {@link MultipartConfigElement} configured using there properties
*/
public MultipartConfigElement createMultipartConfig() {
MultipartConfigFactory factory = new MultipartConfigFactory();
if (StringUtils.hasText(this.fileSizeThreshold)) {
factory.setFileSizeThreshold(this.fileSizeThreshold);
}
if (StringUtils.hasText(this.location)) {
factory.setLocation(this.location);
}
if (StringUtils.hasText(this.maxRequestSize)) {
factory.setMaxRequestSize(this.maxRequestSize);
}
if (StringUtils.hasText(this.maxFileSize)) {
factory.setMaxFileSize(this.maxFileSize);
}
return factory.createMultipartConfig();
}
} }
...@@ -38,15 +38,17 @@ import org.springframework.web.multipart.support.StandardServletMultipartResolve ...@@ -38,15 +38,17 @@ import org.springframework.web.multipart.support.StandardServletMultipartResolve
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
/** /**
* Tests for {@link MultipartAutoConfiguration}. Tests an empty configuration, no * Tests for {@link MultipartAutoConfiguration}. Tests an empty configuration, no
* multipart configuration, and a multipart configuration (with both Jetty and Tomcat). * multipart configuration, and a multipart configuration (with both Jetty and Tomcat).
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Dave Syer * @author Dave Syer
* @author Josh Long * @author Josh Long
...@@ -71,10 +73,10 @@ public class MultipartAutoConfigurationTests { ...@@ -71,10 +73,10 @@ public class MultipartAutoConfigurationTests {
ContainerWithNothing.class, BaseConfiguration.class); ContainerWithNothing.class, BaseConfiguration.class);
DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class); DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class);
assertNotNull(servlet.getMultipartResolver()); assertNotNull(servlet.getMultipartResolver());
assertEquals(1, assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class)
this.context.getBeansOfType(StandardServletMultipartResolver.class) .size(), equalTo(1));
.size()); assertThat(this.context.getBeansOfType(MultipartResolver.class).size(),
assertEquals(1, this.context.getBeansOfType(MultipartResolver.class).size()); equalTo(1));
} }
@Configuration @Configuration
...@@ -87,10 +89,10 @@ public class MultipartAutoConfigurationTests { ...@@ -87,10 +89,10 @@ public class MultipartAutoConfigurationTests {
ContainerWithNoMultipartJetty.class, BaseConfiguration.class); ContainerWithNoMultipartJetty.class, BaseConfiguration.class);
DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class); DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class);
assertNotNull(servlet.getMultipartResolver()); assertNotNull(servlet.getMultipartResolver());
assertEquals(1, assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class)
this.context.getBeansOfType(StandardServletMultipartResolver.class) .size(), equalTo(1));
.size()); assertThat(this.context.getBeansOfType(MultipartResolver.class).size(),
assertEquals(1, this.context.getBeansOfType(MultipartResolver.class).size()); equalTo(1));
verifyServletWorks(); verifyServletWorks();
} }
...@@ -113,10 +115,10 @@ public class MultipartAutoConfigurationTests { ...@@ -113,10 +115,10 @@ public class MultipartAutoConfigurationTests {
ContainerWithNoMultipartTomcat.class, BaseConfiguration.class); ContainerWithNoMultipartTomcat.class, BaseConfiguration.class);
DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class); DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class);
assertNull(servlet.getMultipartResolver()); assertNull(servlet.getMultipartResolver());
assertEquals(1, assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class)
this.context.getBeansOfType(StandardServletMultipartResolver.class) .size(), equalTo(1));
.size()); assertThat(this.context.getBeansOfType(MultipartResolver.class).size(),
assertEquals(1, this.context.getBeansOfType(MultipartResolver.class).size()); equalTo(1));
verifyServletWorks(); verifyServletWorks();
} }
...@@ -134,7 +136,9 @@ public class MultipartAutoConfigurationTests { ...@@ -134,7 +136,9 @@ public class MultipartAutoConfigurationTests {
public void containerWithAutomatedMultipartTomcatConfiguration() throws Exception { public void containerWithAutomatedMultipartTomcatConfiguration() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext( this.context = new AnnotationConfigEmbeddedWebApplicationContext(
ContainerWithEverythingTomcat.class, BaseConfiguration.class); ContainerWithEverythingTomcat.class, BaseConfiguration.class);
new RestTemplate().getForObject("http://localhost:8080/", String.class); new RestTemplate().getForObject("http://localhost:"
+ this.context.getEmbeddedServletContainer().getPort() + "/",
String.class);
this.context.getBean(MultipartConfigElement.class); this.context.getBean(MultipartConfigElement.class);
assertSame(this.context.getBean(DispatcherServlet.class).getMultipartResolver(), assertSame(this.context.getBean(DispatcherServlet.class).getMultipartResolver(),
this.context.getBean(StandardServletMultipartResolver.class)); this.context.getBean(StandardServletMultipartResolver.class));
...@@ -149,8 +153,9 @@ public class MultipartAutoConfigurationTests { ...@@ -149,8 +153,9 @@ public class MultipartAutoConfigurationTests {
.addFirst(new PropertySource<Object>("test") { .addFirst(new PropertySource<Object>("test") {
@Override @Override
public Object getProperty(String name) { public Object getProperty(String name) {
if (name.toLowerCase().contains("multipart.enabled")) if (name.toLowerCase().contains("multipart.enabled")) {
return "false"; return "false";
}
return null; return null;
} }
}); });
...@@ -162,8 +167,9 @@ public class MultipartAutoConfigurationTests { ...@@ -162,8 +167,9 @@ public class MultipartAutoConfigurationTests {
private void verifyServletWorks() { private void verifyServletWorks() {
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
assertEquals(restTemplate.getForObject("http://localhost:8080/", String.class), assertEquals(restTemplate.getForObject("http://localhost:"
"Hello"); + this.context.getEmbeddedServletContainer().getPort() + "/",
String.class), "Hello");
} }
@Configuration @Configuration
...@@ -172,6 +178,13 @@ public class MultipartAutoConfigurationTests { ...@@ -172,6 +178,13 @@ public class MultipartAutoConfigurationTests {
ServerPropertiesAutoConfiguration.class }) ServerPropertiesAutoConfiguration.class })
protected static class BaseConfiguration { protected static class BaseConfiguration {
@Bean
public ServerProperties serverProperties() {
ServerProperties properties = new ServerProperties();
properties.setPort(0);
return properties;
}
} }
@Configuration @Configuration
...@@ -186,10 +199,12 @@ public class MultipartAutoConfigurationTests { ...@@ -186,10 +199,12 @@ public class MultipartAutoConfigurationTests {
WebController controller() { WebController controller() {
return new WebController(); return new WebController();
} }
} }
@Configuration @Configuration
public static class ContainerWithEverythingJetty { public static class ContainerWithEverythingJetty {
@Bean @Bean
MultipartConfigElement multipartConfigElement() { MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement(""); return new MultipartConfigElement("");
...@@ -204,11 +219,13 @@ public class MultipartAutoConfigurationTests { ...@@ -204,11 +219,13 @@ public class MultipartAutoConfigurationTests {
WebController webController() { WebController webController() {
return new WebController(); return new WebController();
} }
} }
@Configuration @Configuration
@EnableWebMvc @EnableWebMvc
public static class ContainerWithEverythingTomcat { public static class ContainerWithEverythingTomcat {
@Bean @Bean
MultipartConfigElement multipartConfigElement() { MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement(""); return new MultipartConfigElement("");
...@@ -223,14 +240,18 @@ public class MultipartAutoConfigurationTests { ...@@ -223,14 +240,18 @@ public class MultipartAutoConfigurationTests {
WebController webController() { WebController webController() {
return new WebController(); return new WebController();
} }
} }
@Controller @Controller
public static class WebController { public static class WebController {
@RequestMapping("/") @RequestMapping("/")
public @ResponseBody String index() { @ResponseBody
public String index() {
return "Hello"; return "Hello";
} }
} }
} }
...@@ -726,22 +726,24 @@ then you can take control completely and do everything manually using ...@@ -726,22 +726,24 @@ then you can take control completely and do everything manually using
See the {sc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{sc-ext}[`WebMvcAutoConfiguration`] See the {sc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{sc-ext}[`WebMvcAutoConfiguration`]
source code for more details. source code for more details.
[[howto-multipart-file-upload-configuration]]
=== Handling Multipart File Uploads
Spring Boot embraces the Servlet 3 `javax.servlet.http.Part` API to support uploading files. By default
Spring Boot configures Spring MVC with a maximum file of 1Mb per
file and a maximum of 10Mb of file data in a single request. You may override these values, as well as the location
to which intermediate data is stored (e.g., to the `/tmp` directory) and the threshold past which data is flushed to
disk by using the properties exposed in the `MultipartAutoConfiguration` class. If you want to specify that files be unlimited,
for example, set the `multipart.maxFileSize` property to `-1`.
The multipart support is helpful when you want to receive multipart encoded file data as a `@RequestParam`-annotated
parameter of type `MultipartFile` in a Spring MVC controller handler method.
See the {sc-spring-boot-autoconfigure}/web/MultipartAutoConfiguration.{sc-ext}[`MultipartAutoConfiguration`] source for more details.
[[howto-multipart-file-upload-configuration]]
=== Handling Multipart File Uploads
Spring Boot embraces the Servlet 3 `javax.servlet.http.Part` API to support uploading
files. By default Spring Boot configures Spring MVC with a maximum file of 1Mb per
file and a maximum of 10Mb of file data in a single request. You may override these
values, as well as the location to which intermediate data is stored (e.g., to the `/tmp`
directory) and the threshold past which data is flushed to disk by using the properties
exposed in the `MultipartProperties` class. If you want to specify that files be
unlimited, for example, set the `multipart.maxFileSize` property to `-1`.
The multipart support is helpful when you want to receive multipart encoded file data as
a `@RequestParam`-annotated parameter of type `MultipartFile` in a Spring MVC controller
handler method.
See the {sc-spring-boot-autoconfigure}/web/MultipartAutoConfiguration.{sc-ext}[`MultipartAutoConfiguration`] s
ource for more details.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment