Article / 文章中心

Spring认证指南:了解如何构建一个多文件上传的 Spring 应用程序

发布时间:2022-02-08 点击数:121


image

本攻略将引导您完结创立能够接纳 HTTP 多部分文件上传的服务器应用程序的进程。

你将制作什么

您将创立一个承受文件上传的 Spring Boot Web 应用程序。您还将构建一个简单的 HTML 界面来上传测验文件。

你需求什么

  • 约15分钟
  • 最喜欢的文本编辑器或 IDE
  • JDK 1.8或更高版本
  • Gradle 4+或Maven 3.2+
  • 您还能够将代码直接导入 IDE:弹簧工具套件 (STS)IntelliJ IDEA

怎么完结本攻略

像大多数 Spring入门攻略一样,您能够从头开端并完结每个进程,也能够绕过您现已了解的基本设置进程。不管哪种办法,您终究都会得到作业代码。

从头开端,请持续从 Spring Initializr 开端。

越过基础知识,请履行以下操作:

完结后,您能够对照中的代码检查成果
gs-uploading-files/complete。

从 Spring Initializr 开端

您能够运用这个预先初始化的项目并单击 Generate 下载 ZIP 文件。此项目装备为适合本教程中的示例。

手动初始化项目:

  1. 导航到https://start.spring.io。该服务提取应用程序所需的一切依靠项,并为您完结大部分设置。
  2. 挑选 Gradle 或 Maven 以及您要运用的言语。本攻略假定您挑选了 Java。
  3. 单击Dependencies并挑选Spring WebThymeleaf
  4. 单击生成
  5. 下载生成的 ZIP 文件,该文件是依据您的挑选装备的 Web 应用程序的存档。

假如您的 IDE 具有 Spring Initializr 集成,您能够从您的 IDE 完结此进程。

你也能够从 Github 上 fork 项目并在你的 IDE 或其他编辑器中翻开它。

创立应用程序类

要发动 Spring Boot MVC 应用程序,首要需求一个发动器。在此示例中,
spring-boot-starter-thymeleaf并且spring-boot-starter-web已作为依靠项增加。要运用 Servlet 容器上传文件,您需求注册一个MultipartConfigElement类(在 web.xml 中)。感谢 Spring Boot,一切都是为您主动装备的!

开端运用此应用程序所需的只是以下UploadingFilesApplication类(来自
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);  } }

作为主动装备 Spring MVC 的一部分,Spring Boot 将创立一个MultipartConfigElementbean 并为文件上传做好准备。

创立文件上传控制器

初始应用程序现已包括一些类来处理在磁盘上存储和加载上传的文件。它们都坐落
com.example.uploadingfiles.storage包装中。您将在新的FileUploadController. 以下清单(来自
src/main/java/com/example/uploadingfiles/FileUploadController.java)显现了文件上传控制器:

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 ResponseEntityserveFile(@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();  } }

该类FileUploadController带有注释,@Controller以便 Spring MVC 能够拾取它并查找路由。每个办法都被标记@GetMapping或@PostMapping将途径和 HTTP 操作绑定到特定的控制器操作。

在这种情况下:

  • GET /:从 中查找当前上传文件的列表StorageService并将其加载到 Thymeleaf 模板中。它经过运用 计算到实际资源的链接MvcUriComponentsBuilder。
  • GET /files/{filename}:加载资源(假如存在)并运用Content-Disposition呼应头将其发送到浏览器进行下载。
  • POST /:处理多部分音讯file并将其供给给StorageService保存。

在出产场景中,您更有可能将文件存储在暂时位置、数据库或 NoSQL 存储(例如Mongo 的 GridFS)中。最好不要在应用程序的文件体系中加载内容。

您将需求供给一个StorageService以便控制器能够与存储层(例如文件体系)进行交互。以下清单(来自
src/main/java/com/example/uploadingfiles/storage/StorageService.java)显现了该界面:

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);  StreamloadAll();  Path load(String filename);  Resource loadAsResource(String filename);  void deleteAll(); }

创立 HTML 模板

以下 Thymeleaf 模板(来自
src/main/resources/templates/uploadForm.html)显现了怎么上传文件并显现已上传内容的示例:

 
				

File to upload:

该模板包括三个部分:

调整文件上传限制

装备文件上传时,设置文件巨细限制通常很有用。幻想一下尝试处理 5GB 文件上传!MultipartConfigElement运用 Spring Boot,咱们能够运用一些特点设置来调整它的主动装备。

将以下特点增加到现有特点设置(在 中
src/main/resources/application.properties):

多部分设置的束缚如下:

运转应用程序

您需求一个方针文件夹来上传文件,因而您需求增强UploadingFilesApplicationSpring Initializr 创立的基本类并增加一个 BootCommandLineRunner以在发动时删除并重新创立该文件夹。以下清单(来自
src/main/java/com/example/uploadingfiles/UploadingFilesApplication.java)显现了怎么履行此操作:

@SpringBootApplication是一个方便的注释,它增加了以下一切内容:

该main()办法运用 Spring Boot 的SpringApplication.run()办法来发动应用程序。您是否注意到没有一行 XML?也没有web.xml文件。这个 Web 应用程序是 100% 纯 Java,您不必处理任何管道或基础设施的装备。

构建一个可履行的 JAR

您能够运用 Gradle 或 Maven 从命令行运转应用程序。您还能够构建一个包括一切必要依靠项、类和资源的单个可履行 JAR 文件并运转它。构建可履行 jar 能够在整个开发生命周期、跨不同环境等中轻松地作为应用程序交付、版本化和部署服务。

假如您运用 Gradle,则能够运用./gradlew bootRun. 或者,您能够运用构建 JAR 文件./gradlew build,然后运转 JAR 文件,如下所示:

假如您运用 Maven,则能够运用./mvnw spring-boot:run. 或者,您能够运用构建 JAR 文件,./mvnw clean package然后运转该 JAR 文件,如下所示:

此处描绘的进程创立了一个可运转的 JAR。您还能够构建经典的 WAR 文件。

它运转接纳文件上传的服务器端部分。显现记录输出。该服务应在几秒钟内发动并运转。

在服务器运转的情况下,您需求翻开浏览器并访问http://localhost:8080/以查看上传表单。挑选一个(小)文件,然后按Upload。您应该会从控制器中看到成功页面。假如你挑选的文件太大,你会得到一个丑恶的错误页面。

然后,您应该会在浏览器窗口中看到类似于以下内容的行:

“您成功上传了<文件名>!”

测验您的应用程序

有多种办法能够在咱们的应用程序中测验此特定功用。以下清单(来自
src/test/java/com/example/uploadingfiles/FileUploadTests.java)显现了一个示例,MockMvc它不需求发动 servlet 容器:

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());  } }

在这些测验中,您运用各种模拟来设置与您的控制器以及StorageService与 Servlet 容器本身的交互,运用MockMultipartFile.

有关集成测验的示例,请参见
FileUploadIntegrationTests类(坐落 中
src/test/java/com/example/uploadingfiles)。

概括

恭喜!您刚刚编写了一个运用 Spring 处理文件上传的 Web 应用程序。