ThinkSSL🔒 一键申购 5分钟快速签发 30天无理由退款 购买更放心 广告
# 上载 本指南将引导您完成创建可以接收HTTP分段文件上传的服务器应用程序的过程。 ## 你会建立什么 您将创建一个接受文件上传的Spring Boot Web应用程序。 您还将构建一个简单的HTML界面来上传测试文件。 ## 你需要什么 * 约15分钟 * 最喜欢的文本编辑器或IDE * [JDK 1.8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 或更高版本 * [Gradle 4+](http://www.gradle.org/downloads) 或 [Maven 3.2+](https://maven.apache.org/download.cgi) * 您还可以将代码直接导入到IDE中: * [弹簧工具套件(STS)](https://spring.io/guides/gs/sts) * [IntelliJ IDEA](https://spring.io/guides/gs/intellij-idea/) ## 如何完成本指南 像大多数Spring 一样 [入门指南](https://spring.io/guides) ,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。 无论哪种方式,您最终都可以使用代码。 要 **从头开始** ,请继续进行“ [从Spring Initializr开始”](https://spring.io/guides/gs/uploading-files/#scratch) 。 要 **跳过基础知识** ,请执行以下操作: * [下载](https://github.com/spring-guides/gs-uploading-files/archive/master.zip) 并解压缩本指南的源存储库,或使用 对其进行克隆 [Git](https://spring.io/understanding/Git) : `git clone [https://github.com/spring-guides/gs-uploading-files.git](https://github.com/spring-guides/gs-uploading-files.git)` * 光盘进入 `gs-uploading-files/initial` * 继续 [创建应用程序类](https://spring.io/guides/gs/uploading-files/#initial) 。 **完成后** ,您可以根据中的代码检查结果 `gs-uploading-files/complete`. ## 从Spring Initializr开始 如果您使用Maven,请访问 [Spring Initializr](https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.4.4.RELEASE&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=Uploading-files&name=Uploading-files&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.Uploading-files&dependencies=web,thymeleaf) 以生成具有所需依赖项(Spring Web和Thymeleaf)的新项目。 以下清单显示了 `pom.xml` 选择Maven时创建的文件: ~~~ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>uploading-files</artifactId> <version>0.0.1-SNAPSHOT</version> <name>uploading-files</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> ~~~ 如果您使用Gradle,请访问 [Spring Initializr](https://start.spring.io/#!type=gradle-project&language=java&platformVersion=2.4.4.RELEASE&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=Uploading-files&name=Uploading-files&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.Uploading-files&dependencies=web,thymeleaf) 以生成具有所需依赖项(Spring Web和Thymeleaf)的新项目。 以下清单显示了 `build.gradle` 选择Gradle时创建的文件: ~~~ plugins { id 'org.springframework.boot' version '2.4.4' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation('org.springframework.boot:spring-boot-starter-test')\ } test { useJUnitPlatform() } ~~~ ### 手动初始化(可选) 如果要手动初始化项目而不是使用前面显示的链接,请按照以下步骤操作: 1. 导航到 [https://start.spring.io](https://start.spring.io) 。 该服务提取应用程序所需的所有依赖关系,并为您完成大部分设置。 2. 选择Gradle或Maven以及您要使用的语言。 本指南假定您选择了Java。 3. 单击 **Dependencies,** 然后选择 **Spring Web** 和 **Thymeleaf** 。 4. 点击 **生成** 。 5. 下载生成的ZIP文件,该文件是使用您的选择配置的Web应用程序的存档。 如果您的IDE集成了Spring Initializr,则可以从IDE中完成此过程。 ## 创建一个应用程序类 要启动一个Spring Boot MVC应用程序,首先需要一个启动器。 在此示例中, `spring-boot-starter-thymeleaf` 和 `spring-boot-starter-web`已经添加为依赖项。 要使用Servlet容器上传文件,您需要注册一个 `MultipartConfigElement` 类(这将是 `<multipart-config>`在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将创建一个 `MultipartConfigElement` Bean,并准备好进行文件上传。 ## 创建一个文件上传控制器 初始应用程序已经包含一些用于处理在磁盘上存储和加载上载文件的类。 它们都位于 `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 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(); } } ~~~ 这 `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); Stream<Path> loadAll(); Path load(String filename); Resource loadAsResource(String filename); void deleteAll(); } ~~~ ## 创建一个HTML模板 以下Thymeleaf模板(来自 `src/main/resources/templates/uploadForm.html`)显示了有关如何上传文件以及显示已上传内容的示例: ~~~ <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> ~~~ 该模板包括三个部分: * 顶部的可选消息,Spring MVC会在该消息中写入 [Flash范围的消息](https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-flash-attributes) 。 * 一种允许用户上传文件的表格。 * 后端提供的文件列表。 ## 调整文件上传限制 配置文件上传时,设置文件大小限制通常很有用。 想象一下要处理5GB的文件上传! 借助Spring Boot,我们可以对其自动配置进行调整 `MultipartConfigElement` 带有一些属性设置。 将以下属性添加到现有属性设置(在 `src/main/resources/application.properties`): ~~~ spring.servlet.multipart.max-file-size=128KB spring.servlet.multipart.max-request-size=128KB ~~~ 分段设置受以下约束: * `spring.http.multipart.max-file-size` 设置为128KB,表示文件总大小不能超过128KB。 * `spring.http.multipart.max-request-size` 设置为128KB,表示一个请求的总请求大小 `multipart/form-data` 不能超过128KB。 ## 运行应用程序 您需要将文件上传到的目标文件夹,因此您需要增强基本功能 `UploadingFilesApplication` Spring Initializr创建并添加Boot的类 `CommandLineRunner`在启动时删除并重新创建该文件夹。 以下清单(来自 `src/main/java/com/example/uploadingfiles/UploadingFilesApplication.java`)显示了如何执行此操作: ~~~ 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` 是一个方便注释,它添加了以下所有内容: * `@Configuration`:将类标记为应用程序上下文的Bean定义的源。 * `@EnableAutoConfiguration`:告诉Spring Boot根据类路径设置,其他bean和各种属性设置开始添加bean。 例如,如果 `spring-webmvc` 在类路径上,此注释将应用程序标记为Web应用程序并激活关键行为,例如设置 `DispatcherServlet`. * `@ComponentScan`:告诉Spring在服务器中寻找其他组件,配置和服务 `com/example` 包,让它找到控制器。 这 `main()` 方法使用Spring Boot的 `SpringApplication.run()`启动应用程序的方法。 您是否注意到没有一行XML? 没有 `web.xml`文件。 该Web应用程序是100%纯Java,因此您无需处理任何管道或基础结构。 ### 建立可执行的JAR 您可以使用Gradle或Maven从命令行运行该应用程序。 您还可以构建一个包含所有必需的依赖项,类和资源的可执行JAR文件,然后运行该文件。 生成可执行jar使得在整个开发生命周期中,跨不同环境等等的情况下,都可以轻松地将服务作为应用程序进行发布,版本控制和部署。 如果您使用Gradle,则可以通过使用以下命令运行该应用程序 `./gradlew bootRun`。 或者,您可以通过使用以下命令构建JAR文件: `./gradlew build` 然后运行JAR文件,如下所示: ~~~ java -jar build/libs/gs-uploading-files-0.1.0.jar ~~~ 如果您使用Maven,则可以通过使用以下命令运行该应用程序 `./mvnw spring-boot:run`。 或者,您可以使用以下命令构建JAR文件: `./mvnw clean package` 然后运行JAR文件,如下所示: ~~~ java -jar target/gs-uploading-files-0.1.0.jar ~~~ 此处描述的步骤将创建可运行的JAR。 您还可以 构建经典的WAR文件 。 运行服务器端接收文件上传的部分。 显示日志记录输出。 该服务应在几秒钟内启动并运行。 在服务器运行的情况下,您需要打开浏览器并访问 `[http://localhost:8080/](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应用程序。