ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

上传文件

2022-04-30 10:32:40  阅读:199  来源: 互联网

标签:文件 web com springframework import org 上传 example


此篇指南带你遍历创建一个可以接受HTTP多部分文件(multi-part file)上传服务器程序的步骤。

What You Will Build

你将创建一个Spring Boot web应用用来接受上传的文件。你也将构建一个简单的HTML界面用来上传测试文件。

What You Need

  • 大约15分钟
  • 你喜欢的文本编辑器或IDE
  • JDK 1.8 以上
  • Graddle 4+ 或 Maven 3.2+
  • 你也可以直接将代码导入进你的IDE
    • Spring Tool Suite (STS)
    • InteelliJ IDEA

How to complete this guide

正如Spring大多数指南一样,你可以从头开始并且完成每一个步骤或者你可以绕开你已经熟悉的基本设置。无论哪种方式,你最终都是完成代码。

为了从头开始,移步到Starting with Spring Initializr

为了跳过基础,做如下步骤:

  • 下载并解压这篇指南的原仓库,或者使用GIt克隆git clone https://github.com/spring-guides/gs-uploading-files.git
  • 进入(cd)gs-uploading-files/initial
  • 移步到Create an Application Class

当你完成以后,你可以再次检查你的gs-uploading-files/complete的代码。

Starting with Spring Initializr

你可以使用预初始化项目点击Generate来下载ZIP文件。这一项目被配置为适合本篇指南。

为了手动初始化项目:

  1. 浏览https://start.spring.io/。这个服务拉取了你的应用所有需要的依赖并且已经为了做了多数配置。
  2. 选择Gradle或Maven与你想使用的语言。本文假设你选择Java。
  3. 点击Dependencies选择Spring WebThymeleaf
  4. 点击Generate
  5. 下载ZIP文件,这是一个为你的选择配置好的网络应用文件。

如果你的IDE整合了Spring Initializr,你可以从你的IDE完成此步骤

你也可以从GitHub中fork此项目使用你的IDE或者编辑器打开。

Create an Application Class

为了启动Spring Boot MVC应用,你首先需要一个启动器。在本篇示例,spring-boot-starter-thymeleafspring-boot-starter-web都作为依赖被添加。为了使用Servlet容器上传文件,你需要注册一个MultipartConfigElement类(将是在web.xml中的<multipart-config>)。由于使用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并且使其准备接受文件上传。

Create a File Upload Controller

initial中的应用已经包含一少部分的类用来处理存储和加载在硬盘中上传的文件。它们全部在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 /:处理multi-part消息file以及发送它到StorageService进行保存。

在生产环境中,你很有可能将文件存储到临时位置、数据库、NoSQL存储(例如Mongo的[GridFS](https://docs.mongodb.org/manual/core/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();

}

Creating an HTML Template

如下所示的Thymeleaf模板用来演示如何上传文件以及显示被上传的文件:

<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="上传" /></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-scoped meesage可选消息
  • 用户上传文件的表单
  • 从后端提供的文件列表

Tuning File Upload Limits

当配置文件上传的时候,一个常用的做法是设置文件的大小。想象一下尝试处理5GB的文件上传!在Spring Boot,我们可以微调它的自动配置MultipartContentElement的某些属性配置。

添加如下所示的属性到你存在的属性配置中:

src/main/resources/application.properties

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

multipart设置被限制为:

  • spring.servlet.multipart.max-file-size设置为128KB,代表着全部文件大小不可以超过128KB。
  • spring.servlet.multipart.max-request-size设置为128KB,代表全部请求multipart/form-data大小不能超过128KB。

Run the Application

你想要目标目录用来上传文件,所以你需要启用由Spring Initializr创建的基本UploadingFilesApplication类,并且添加一个BootCommandLineRunner在启动时来删除和重新创建这个目录。如下所示:

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:对于应用上下文(context),将类标记为bean定义的来源。
  • @EnableAutoConfiguration:告诉Spring Boot开始根据类路径设置添加bean,其它bean,以及各种属性设置。例如,如果spring-webmvc在类路径中,这一注解将应用程序标记为web应用以及激活关键行为,例如设置DispatcherServlet
  • @ComponentScan:告诉Spring寻找com.example包下其它组件、配置以及服务,使其找到控制器。

main()方法使用Spring Boot的SpringApplication.run()方法来启动一个应用程序。你是否注意到没有一行的XML?也同样没有web.xml文件。这一个应用程序是100%纯Java并且你并没有必须处理配置任何一个plumbing或infrastructure。

Build an executable JAR

你可以在命令行以Gradle或Maven运行应用程序。你也可以构建一个单一可执行的JAR包,其中包含所有必须的依赖、类以及资源并且运行。构建一个可执行jar可以易于传输、版本和部署服务作为应用,贯穿至整个开发周期,穿插于不同环境等等。

如果你使用Gradle,你可以通过使用./gradlew bootRun来运行应用程序。或者说,你可以使用./gradlew build来构建JAR包,然后运行JAR包,如下命令:

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

如果你使用Maven,你可以通过./mvnw spring-boot:run来运行应用程序。或者说,你可以使用./mvnw clean package来构建JAR包,通过如下命令执行JAR包。

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

此处步骤描述了创建一个可执行JAR包。你也可以构造一个经典的WAR文件

它运行于服务端用于接受文件上传的部分。日志输出被打印。服务应该启动运行在几秒之内。

当服务器运行,你需要打开浏览器访问http://localhost:8080/可以看到文件上传表单。选择一个小文件之后点击上传按钮。你应该看到来自于控制器的成功页面。如果你选择一个文件太大了,你将会得到一个丑陋的错误页面。

在之后,你应该在你的浏览器窗口看到一行类似于如下的输出:

You successfully uploaded <name of your file>!

Testing Your Application

在我们的应用中,有多种方法来测试特定功能。如下所示,使用MockMvc以便于不需要从servlet容器开始:

src/test/java/com/example/uploadingfiles/FileUploadTests.java

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

}

在这些测试,你使用各种mock来设置你的控制器和StorageService的交互,以及使用MockMultipartFile本身与Servlet容器交互。

对于一个整合测试的例子,查看FileUploadIntegrationTests类(在src/test/java/com/example/uploadingfiles)。

标签:文件,web,com,springframework,import,org,上传,example
来源: https://www.cnblogs.com/geekfx/p/16209548.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有