This example shows how to use StandardServletMultipartResolver to upload a file. This resolver is based on the Servlet 3.0 javax.servlet.http.Part API.
Also check out this tutorial to understand how MultipartResolver works.
Other than registering StandardServletMultipartResolver as a bean, we also need to set a javax.servlet.MultipartConfigElement on the Servlet registration which does configuration equivalent to using javax.servlet.annotation.MultipartConfig annotation. Check out this related Servlet example.
Example
We are going to reuse our last example, we just need to modify our Java Config and WebApplicationInitializer implementation.
Java Config
package com.logicbig.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@EnableWebMvc
@Configuration
@ComponentScan
public class MyWebConfig implements WebMvcConfigurer {
@Bean
public MultipartResolver multipartResolver() {
StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
//resolver.setResolveLazily(true);
return resolver;
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
registry.viewResolver(viewResolver);
}
}
package com.logicbig.example;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.ServletRegistration;
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
// Return root configuration classes (often for service/repository layer)
// If none, return null
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
// Return Spring MVC configuration class (the equivalent of MyWebConfig)
return new Class<?>[]{MyWebConfig.class};
}
@Override
protected String[] getServletMappings() {
// Map DispatcherServlet to root context
return new String[]{"/"};
}
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
// Set multipart configuration – this is the key difference from WebApplicationInitializer
String tempDir = System.getProperty("java.io.tmpdir");
MultipartConfigElement multipartConfig = new MultipartConfigElement(
tempDir, // location
50_000_000L, // maxFileSize (bytes)
50_000_000L, // maxRequestSize (bytes)
0 // fileSizeThreshold (bytes)
);
registration.setMultipartConfig(multipartConfig);
}
}
The Controller
package com.logicbig.example;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
@Controller
@RequestMapping("/upload")
public class FileUploadController {
public static final String TARGET_FOLDER = "d:/filesUploaded/";
@GetMapping
public String handleGetRequest() {
return "file-upload";
}
@PostMapping
public String handlePostRequest(MultipartHttpServletRequest request,
Model model) throws IOException {
MultipartFile multipartFile = request.getFile("user-file");
String name = multipartFile.getOriginalFilename();
InputStream inputStream = multipartFile.getInputStream();
Files.copy(inputStream,
Paths.get(TARGET_FOLDER + name),
StandardCopyOption.REPLACE_EXISTING);
model.addAttribute("msg", "File has been uploaded. name: " + name +
", content: " + new String(multipartFile.getBytes()));
return "response";
}
}
To try examples, run embedded Jetty (configured in pom.xml of example project below):
mvn jetty:run
Output
In above form choose the file and click 'Upload File' button
Using cURL
We can use the cURL -F (or --form) flag to send raw text content as multipart/form-data. To make your backend framework treat it as a real file upload, append a dummy filename to the string:
$ curl -s -F "user-file=my file content;filename=text.txt" http://localhost:8080/upload
<html> <body>
<h3> Upload File Response </h3> File has been uploaded. name: text.txt, content: my file content </form> </body> </html>
Integration Test
package com.logicbig.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.context.WebApplicationContext;
import java.io.File;
import java.nio.file.Files;
import static org.assertj.core.api.Assertions.assertThat;
@SpringJUnitWebConfig(MyWebConfig.class)
public class UploadControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvcTester mockMvcTester;
@BeforeEach
public void setup() {
this.mockMvcTester = MockMvcTester.from(this.wac);
}
@Test
public void testController() throws Exception {
String fileName = "test.txt";
File file = new File(FileUploadController.TARGET_FOLDER + fileName);
file.delete();
MockMultipartFile mockMultipartFile =
new MockMultipartFile("user-file",
"test.txt",
null,
"test data".getBytes());
assertThat(this.mockMvcTester.post()
.uri("/upload")
.multipart()
.file(mockMultipartFile))
.hasStatusOk()
.model()
.containsEntry("msg",
"File has been uploaded."
+ " name: test.txt, "
+ "content: test data");
assertThat(file).exists();
assertThat(Files.readAllBytes(file.toPath())).asString().isEqualTo("test data");
}
}
mvn clean test -Dtest="UploadControllerTest" Output$ mvn clean test -Dtest="UploadControllerTest" [INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building the effective model for com.logicbig.example:spring-standard-servlet-multipartresolver:war:1.0-SNAPSHOT [WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-war-plugin is missing. @ line 43, column 21 [WARNING] [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. [WARNING] [WARNING] For this reason, future Maven versions might no longer support building such malformed projects. [WARNING] [INFO] [INFO] ---< com.logicbig.example:spring-standard-servlet-multipartresolver >--- [INFO] Building spring-standard-servlet-multipartresolver 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ spring-standard-servlet-multipartresolver --- [INFO] Deleting D:\example-projects\spring-mvc\file-upload\spring-standard-servlet-multipart-resolver\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-standard-servlet-multipartresolver --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\file-upload\spring-standard-servlet-multipart-resolver\src\main\resources [INFO] [INFO] --- compiler:3.5.1:compile (default-compile) @ spring-standard-servlet-multipartresolver --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to D:\example-projects\spring-mvc\file-upload\spring-standard-servlet-multipart-resolver\target\classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-standard-servlet-multipartresolver --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\file-upload\spring-standard-servlet-multipart-resolver\src\test\resources [INFO] [INFO] --- compiler:3.5.1:testCompile (default-testCompile) @ spring-standard-servlet-multipartresolver --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to D:\example-projects\spring-mvc\file-upload\spring-standard-servlet-multipart-resolver\target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-standard-servlet-multipartresolver --- [INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider [WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.UploadControllerTest [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.511 s -- in com.logicbig.example.UploadControllerTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.757 s [INFO] Finished at: 2026-06-05T10:09:14+08:00 [INFO] ------------------------------------------------------------------------
Using MultipartHttpServletRequest
Instead of using MultipartFile as the handler method parameter, we can alternatively use org.springframework.web.multipart.MultipartHttpServletRequest. Complete example here
Using javax.servlet.http.Part
When using StandardServletMultipartResolver, instead of MultipartFile/MultipartHttpServletRequest as the controller's handler method argument, we can alternatively use javax.servlet.http.Part. Complete example here
Example ProjectDependencies and Technologies Used: - spring-webmvc 7.0.6 (Spring Web MVC)
Version Compatibility: 3.2.9.RELEASE - 7.0.6 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- spring-test 7.0.6 (Spring TestContext Framework)
- jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
- jakarta.servlet.jsp.jstl 3.0.1 (Jakarta Standard Tag Library Implementation)
- junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
- hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
- assertj-core 3.26.3 (Rich and fluent assertions for testing in Java)
- JDK 25
- Maven 3.9.11
|