Spring Boot 上传文件
最后修改于 2023 年 7 月 29 日
在本文中,我们将展示如何使用 Spring Boot 框架上传单个文件。
Spring 是一个流行的 Java 应用程序框架,而 Spring Boot 是 Spring 的一个演进,它有助于轻松创建独立的、生产级别的基于 Spring 的应用程序。
HTML 表单编码类型
对于 POST 请求,有三种 HTML 表单编码类型
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
application/x-www-form-urlencoded
是默认编码,其中值以 key-value 元组的形式编码,用 &
分隔。 =
字符用于分隔 key 和 value。非字母数字字符会被百分比编码。 这种编码类型不适用于二进制文件。
multipart/form-data
用于非 ascii 数据和二进制文件。 input
元素的 type
属性设置为 file
。
text/plain
用于调试。
Spring 上传文件示例
在下面的示例中,我们有一个 web 表单,用于选择要上传到服务器的文件。 该文件被上传到 /var/www/upload/
目录。
上传目录
/var/www/
目录是 Debian Linux 中用于 web 内容的标准目录。
$ ls -ld /var/www/upload/ drwxrwxr-x 2 www-data www-data 4096 Dec 3 14:29 /var/www/upload/
我们将文件上传到 /var/www/upload/
目录。 www-data
组中的用户可以修改该目录中的文件。 因此,运行 web 服务器的用户必须在此组中。
应用程序
以下是 Spring Boot web 应用程序的源代码。
build.gradle ... src ├───main │ ├───java │ │ └───com │ │ └───zetcode │ │ │ Application.java │ │ ├───controller │ │ │ MyController.java │ │ ├───exception │ │ │ StorageException.java │ │ └───service │ │ StorageService.java │ └───resources │ │ application.properties │ └───static │ failure.html │ index.html │ success.html └───test └───java
这是 Spring 应用程序的项目结构。
plugins { id 'org.springframework.boot' version '3.1.1' id 'io.spring.dependency-management' version '1.1.0' id 'java' } group = 'com.zetcode' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' } test { useJUnitPlatform() }
这是 Gradle 构建文件。
package com.zetcode.controller; import com.zetcode.exception.StorageException; import com.zetcode.service.StorageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; @Controller public class MyController { private final StorageService storageService; @Autowired public MyController(StorageService storageService) { this.storageService = storageService; } @RequestMapping(value = "/doUpload", method = RequestMethod.POST, consumes = {"multipart/form-data"}) public String upload(@RequestParam MultipartFile file) { storageService.uploadFile(file); return "redirect:/success.html"; } @ExceptionHandler(StorageException.class) public String handleStorageFileNotFound(StorageException e) { return "redirect:/failure.html"; } }
MyController
从请求中读取文件并将其保存到选定的目录中。
private final StorageService storageService; @Autowired public MyController(StorageService storageService) { this.storageService = storageService; }
StoreageService
将文件存储在磁盘上。
@RequestMapping(value = "/doUpload", method = RequestMethod.POST, consumes = {"multipart/form-data"}) public String upload(@RequestParam MultipartFile file) {
upload
方法映射到 doUpload
URL 模式。 由于我们将数据发送到服务器,因此我们使用 POST 请求。 请求参数具有 MultipartFile
类型。
return "redirect:/success.html";
我们在成功上传文件后显示一条消息。
@ExceptionHandler(StorageException.class) public String handleStorageFileNotFound(StorageException e) { return "redirect:/failure.html"; }
我们有一个针对 StorageException
的处理程序。
package com.zetcode.exception; public class StorageException extends RuntimeException { public StorageException(String message) { super(message); } public StorageException(String message, Throwable cause) { super(message, cause); } }
这是我们的自定义 StorageException
。 当文件无法存储在文件系统上时,会抛出它。
package com.zetcode.service; import com.zetcode.exception.StorageException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @Service public class StorageService { @Value("${upload.path}") private String path; public void uploadFile(MultipartFile file) { if (file.isEmpty()) { throw new StorageException("Failed to store empty file"); } try { var fileName = file.getOriginalFilename(); var is = file.getInputStream(); Files.copy(is, Paths.get(String.format("%s/%s", path, fileName)), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { var msg = String.format("Failed to store file %s", file.getName()); throw new StorageException(msg, e); } } }
StorageService
从输入流复制数据并将其保存在磁盘上。
@Value("${upload.path}") private String path;
我们使用 @Value
注解从 application.properties
文件中读取上传目录。
if (file.isEmpty()) { throw new StorageException("Failed to store empty file"); }
我们使用 isEmpty
方法确保选择了文件。
var fileName = file.getOriginalFilename();
我们使用 getOriginalFilename
方法获取文件名。
var is = file.getInputStream();
我们使用 getInputStream
方法获取输入流。
Files.copy(is, Paths.get(String.format("%s/%s", path, fileName)), StandardCopyOption.REPLACE_EXISTING);
该文件从输入流源复制到目标目录,使用 Files.copy
。
upload.path=/var/www/upload/
在 application.properties
中,我们有一个 upload.path
属性,用于指定上传目录。
<!DOCTYPE html> <html lang="en"> <head> <title>Uploading file</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <h1>Uploading file</h1> <form action="/doUpload" method="post" enctype="multipart/form-data"> <label>Enter file</label> <input type="file" name="file"> <button type="submit">Upload</button> </form> </body> </html>
这是主页。 它是一个静态文件,位于 src/main/resources/static
目录中。 它包含一个表单,用于选择一个文件并将其发送到 Spring 应用程序。
<form action="/doUpload" method="post" enctype="multipart/form-data">
我们选择了 doUpload
URL 模式。 由此表单创建的请求将由 Spring 控制器处理。 enctype
属性指定 multipart/form-data
编码类型,这是使用 HTML 表单上传文件所必需的。
<input type="file" name="file">
input
标签的 type
属性允许用户选择一个文件。
<button type="submit">Upload</button>
最后,这是一个提交按钮。
<!DOCTYPE html> <html lang="en"> <head> <title>Success</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <p>File successfully uploaded</p> </body> </html>
当文件成功上传到服务器时,将显示 success.html
。
<!DOCTYPE html> <htm lang="en"l> <head> <title>Failure</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <p>Failed to upload file</p> </body> </html>
当文件上传失败时,将显示 failure.html
。
package com.zetcode; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
此代码设置了 Spring Boot 应用程序。
在本文中,我们学习了如何在 Spring 应用程序中上传文件。