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 应用程序中上传文件。