Spring Certification Guide: learn how to build a spring application with multiple file uploads

Original title: Spring certified China Education Management Center - learn how to build a spring application that accepts multi part file upload (spring China Education Management Center)

This guide will guide you through the process of creating a server application that can receive HTTP multipart file uploads.

What will you build

You will create a Spring Boot Web application that accepts file uploads. You will also build a simple HTML interface to upload test files.

What do you need?

  • About 15 minutes
  • Favorite text editor or IDE
  • JDK 1.8 or later
  • Gradle 4 + or Maven 3.2+
  • You can also import the code directly into the IDE: spring tool kit (STS)IntelliJ IDEA

How to complete this guide

Like most Spring getting started guides, you can start from scratch and complete each step, or you can bypass the basic setup steps you are already familiar with. Either way, you'll end up with working code.

To start from scratch, continue with Spring Initializr.

To skip the basics:

  • Download and unzip the source code base of this guide, or clone it with Git: git clone https://github.com/spring-guides/gs-uploading-files.git
  • The CD goes to GS uploading files / initial
  • Continue creating application classes.

When finished, you can check the results against the code in gs-uploading-files/complete.

Start with Spring Initializr

You can use this pre initialized project and click Generate to download the ZIP file. This project is configured to fit the examples in this tutorial.

Manually initialize the project:

  1. Navigate to https://start.spring.io . The service extracts all the dependencies required by the application and completes most of the settings for you.
  2. Select Gradle or Maven and the language you want to use. This guide assumes that you have chosen Java.
  3. Click Dependencies and select Spring Web and Thymeleaf.
  4. Click generate.
  5. Download the generated ZIP file, which is an archive of the Web application configured according to your choice.

If your IDE has spring initializer integration, you can complete this process from your IDE.

You can also fork the project from Github and open it in your IDE or other editor.

Create application class

To start the Spring Boot MVC application, you first need a launcher. In this example, Spring Boot starter thymeleaf and Spring Boot starter web have been added as dependencies. To upload files using the Servlet container, you need to register a multipart configlelement class (< multipart config > in web.xml). Thanks to Spring Boot, everything is automatically configured for you!

All you need to start using this application is the following UploadingFilesApplication class (from src/main/java/com/example/uploadingfiles/UploadingFilesApplication.java):

package com.example.uploadingfiles;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class UploadingFilesApplication {

	public static void main(String[] args) {
		SpringApplication.run(UploadingFilesApplication.class, args);
	}

}

As part of the automatic configuration of Spring MVC, Spring Boot will create a multipartconfiglelement bean and prepare for file upload.

Create file upload controller

The initial application already contains classes to handle storing and loading uploaded files on disk. They are all located in com.example.uploadingfiles.storage package. You will be in the new FileUploadController The following list (from src/main/java/com/example/uploadingfiles/FileUploadController.java) shows the file upload controller:

package com.example.uploadingfiles;

import java.io.IOException;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.example.uploadingfiles.storage.StorageFileNotFoundException;
import com.example.uploadingfiles.storage.StorageService;

@Controller
public class FileUploadController {

	private final StorageService storageService;

	@Autowired
	public FileUploadController(StorageService storageService) {
		this.storageService = storageService;
	}

	@GetMapping("/")
	public String listUploadedFiles(Model model) throws IOException {

		model.addAttribute("files", storageService.loadAll().map(
				path -> MvcUriComponentsBuilder.fromMethodName(FileUploadController.class,
						"serveFile", path.getFileName().toString()).build().toUri().toString())
				.collect(Collectors.toList()));

		return "uploadForm";
	}

	@GetMapping("/files/{filename:.+}")
	@ResponseBody
	public ResponseEntity<Resource> serveFile(@PathVariable String filename) {

		Resource file = storageService.loadAsResource(filename);
		return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
				"attachment; filename=\"" + file.getFilename() + "\"").body(file);
	}

	@PostMapping("/")
	public String handleFileUpload(@RequestParam("file") MultipartFile file,
			RedirectAttributes redirectAttributes) {

		storageService.store(file);
		redirectAttributes.addFlashAttribute("message",
				"You successfully uploaded " + file.getOriginalFilename() + "!");

		return "redirect:/";
	}

	@ExceptionHandler(StorageFileNotFoundException.class)
	public ResponseEntity<?> handleStorageFileNotFound(StorageFileNotFoundException exc) {
		return ResponseEntity.notFound().build();
	}

}

This class of FileUploadController is annotated with @ Controller so that Spring MVC can pick it up and find routes. Each method is marked @ GetMapping or @ PostMapping to bind the path and HTTP operation to a specific Controller operation.

under these circumstances:

  • GET /: find the list of currently uploaded files from StorageService and load it into the Thymeleaf template. It is calculated by linking it to the actual resource of the mvcomponentbuilder.
  • GET /files/{filename}: load the resource (if it exists) and send it to the browser for download using the content disposition response header.
  • POST /: process multi part message file and provide it to StorageService for saving.

In a production scenario, you are more likely to store files in a temporary location, database, or NoSQL storage (such as Mongo's GridFS). It is best not to load content in the application's file system.

You will need to provide a StorageService so that the controller can interact with storage tiers, such as file systems. The following list (from src/main/java/com/example/uploadingfiles/storage/StorageService.java) shows the interface:

package com.example.uploadingfiles.storage;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

import java.nio.file.Path;
import java.util.stream.Stream;

public interface StorageService {

	void init();

	void store(MultipartFile file);

	Stream<Path> loadAll();

	Path load(String filename);

	Resource loadAsResource(String filename);

	void deleteAll();

}

Create HTML template

The following Thymeleaf templates (from src/main/resources/templates/uploadForm.html) shows an example of how to upload a file and display the uploaded content:

<html xmlns:th="https://www.thymeleaf.org">
<body>

	<div th:if="${message}">
		<h2 th:text="${message}"/>
	</div>

	<div>
		<form method="POST" enctype="multipart/form-data" action="/">
			<table>
				<tr><td>File to upload:</td><td><input type="file" name="file" /></td></tr>
				<tr><td></td><td><input type="submit" value="Upload" /></td></tr>
			</table>
		</form>
	</div>

	<div>
		<ul>
			<li th:each="file : ${files}">
				<a th:href="${file}" th:text="${file}" />
			</li>
		</ul>
	</div>

</body>
</html>

The template consists of three parts:

  • Spring MVC writes the optional message at the top of the flash scoped message in it.
  • A form that allows users to upload files.
  • List of files provided from the backend.

Adjust file upload restrictions

Setting file size limits is often useful when uploading configuration files. Imagine trying to handle 5GB file uploads! Multipartconfiglelement uses Spring Boot. We can use some property settings to adjust its automatic configuration.

Add the following properties to the existing property settings in src/main/resources/application.properties):

spring.servlet.multipart.max-file-size=128KB
spring.servlet.multipart.max-request-size=128KB

The constraints for multipart settings are as follows:

  • spring. servlet. multipart. Max file size is set to 128KB, which means that the total file size cannot exceed 128KB.
  • spring. servlet. multipart. Max request size is set to 128KB, which means that the total request size of a multipart / form data cannot exceed 128KB.

Run the application

You need a target folder to upload files, so you need to enhance the basic class created by UploadingFilesApplicationSpring Initializr and add a BootCommandLineRunner to delete and recreate the folder at startup. The following list (from src/main/java/com/example/uploadingfiles/UploadingFilesApplication.java) shows how to do this:

package com.example.uploadingfiles;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import com.example.uploadingfiles.storage.StorageProperties;
import com.example.uploadingfiles.storage.StorageService;

@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class UploadingFilesApplication {

	public static void main(String[] args) {
		SpringApplication.run(UploadingFilesApplication.class, args);
	}

	@Bean
	CommandLineRunner init(StorageService storageService) {
		return (args) -> {
			storageService.deleteAll();
			storageService.init();
		};
	}
}

@SpringBootApplication adds the following notes:

  • @Configuration: marks the class as the bean definition source of the application context.
  • @EnableAutoConfiguration: Tell Spring Boot to start adding beans according to classpath settings, other beans and various property settings. For example, if spring webmvc is on the classpath, this annotation marks the application as a Web application and activates key behaviors, such as setting DispatcherServlet
  • @ComponentScan: Tell Spring to find other components, configurations and services com/example in the package and let it find the controller.

The main() method uses springapplication. Of Spring Boot Run () method to start the application. Have you noticed that there is no single line of XML? There is no Web XML file. This Web application is 100% pure Java, and you don't have to deal with any pipeline or infrastructure configuration.

Build an executable JAR

You can run the application from the command line using Gradle or Maven. You can also build and run a single executable jar file that contains all the necessary dependencies, classes, and resources. Building executable jars can be easily used as application delivery, versioning and deployment services throughout the development life cycle, across different environments, and so on.

If you use Gradle, you can use/ gradlew bootRun. Alternatively, you can use to build JAR files/ gradlew build, and then run the} JAR file as follows:

java -jar build/libs/gs-uploading-files-0.1.0.jar

If you use Maven, you can use/ mvnw spring-boot:run. Alternatively, you can use the build JAR file/ mvnw clean package and run the JAR file as follows:

java -jar target/gs-uploading-files-0.1.0.jar

The steps described here create a runnable JAR. You can also build classic WAR files.

It runs the server-side part that receives file uploads. Display record output. The service should start and run in a few seconds.

When the server is running, you need to open the browser and access http://localhost:8080/ To view the Upload form. Select a (small) file and press Upload. You should see the success page from the controller. If the file you choose is too large, you will get an ugly error page.

Then, you should see a line similar to the following in the browser window:

"You have successfully uploaded < file name >!"

Test your application

There are several ways to test this particular feature in our application. The following list (from src/test/java/com/example/uploadingfiles/FileUploadTests.java) shows an example, MockMvc, which does not need to start the servlet container:

package com.example.uploadingfiles;

import java.nio.file.Paths;
import java.util.stream.Stream;

import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.example.uploadingfiles.storage.StorageFileNotFoundException;
import com.example.uploadingfiles.storage.StorageService;

@AutoConfigureMockMvc
@SpringBootTest
public class FileUploadTests {

	@Autowired
	private MockMvc mvc;

	@MockBean
	private StorageService storageService;

	@Test
	public void shouldListAllFiles() throws Exception {
		given(this.storageService.loadAll())
				.willReturn(Stream.of(Paths.get("first.txt"), Paths.get("second.txt")));

		this.mvc.perform(get("/")).andExpect(status().isOk())
				.andExpect(model().attribute("files",
						Matchers.contains("http://localhost/files/first.txt",
								"http://localhost/files/second.txt")));
	}

	@Test
	public void shouldSaveUploadedFile() throws Exception {
		MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt",
				"text/plain", "Spring Framework".getBytes());
		this.mvc.perform(multipart("/").file(multipartFile))
				.andExpect(status().isFound())
				.andExpect(header().string("Location", "/"));

		then(this.storageService).should().store(multipartFile);
	}

	@SuppressWarnings("unchecked")
	@Test
	public void should404WhenMissingFile() throws Exception {
		given(this.storageService.loadAsResource("test.txt"))
				.willThrow(StorageFileNotFoundException.class);

		this.mvc.perform(get("/files/test.txt")).andExpect(status().isNotFound());
	}

}

In these tests, you use various simulations to set up interaction with your controller and StorageService with the Servlet container itself, using MockMultipartFile

For an example of integration testing, see FileUploadIntegrationTests class in src/test/java/com/example/uploadingfiles).

generalization

congratulations! You have just written a Web application that uses Spring to handle file uploads.

Added by Iceman512 on Mon, 07 Feb 2022 10:51:25 +0200