在本教程中,我将向您展示如何在Spring Boot项目中使用Thymeleaf和Bootstrap实现文件上传。我们还使用Spring Web MultipartFile
接口来处理HTTP multi-part请求。
相关文章:
- Spring Boot upload Image and Display with Thymeleaf
- Spring Boot Thymeleaf CRUD example
- Spring Boot Thymeleaf Pagination and Sorting example
- Spring Boot Thymeleaf Multiple File upload
- Spring Boot @ControllerAdvice & @ExceptionHandler example
- @RestControllerAdvice example in Spring Boot
- Spring Boot Unit Test for JPA Repository
- Spring Boot Unit Test for Rest Controller
- Deploy Spring Boot App on AWS – Elastic Beanstalk
fulstack:
关联:
- Spring Boot One To One example with JPA, Hibernate
- Spring Boot One To Many example with JPA, Hibernate
- Spring Boot Many to Many example with JPA, Hibernate
我们的Spring Boot + ThyMeleaf文件上传示例将具有以下功能:
- 将文件上传到服务器中的静态文件夹
- 使用链接从服务器下载文件
- 获取文件的信息列表(文件名和URL)
- 这是文件上传表格:
- 如果文件超过特定的最大大小:
- 这是存储所有上传文件的静态文件夹:
- 您可以看到带有下载链接的上传文件列表:
在本教程中,我没有解释删除文件的方式。如果您想知道这一点,只需访问:
Spring Boot Delete File example with Thymeleaf
或添加分页以下教程:
Spring Boot Thymeleaf Pagination example
项目结构
让我简要解释。
-
FileInfo
包含上传文件的信息。 -
FilesStorageService
帮助我们初始化存储,保存新文件,加载文件,获取文件的信息,删除文件。 -
FileController
使用FilesStorageService
处理文件上传/下载和模板请求。 -
当控制器处理文件上传时,
FileUploadExceptionAdvice
处理异常。 -
template
存储项目的HTML模板文件。 - application.properties 包含Servlet Multipart的配置。
- 上传是存储文件的静态文件夹。
- pom.xml 用于春季启动依赖。
创建&设置Spring Boot Project
使用Spring web tool或您的开发工具(Spring Tool Suite,Eclipse,Intellij)创建一个Spring Boot项目。
然后打开 pom.xml ,并为春季网络,百里叶,bootstrap,jQuery添加依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.6.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
为文件存储
创建服务首先,我们需要一个将在控制器中自动进行的接口。
在 service 文件夹中,创建FilesStorageService
接口如下:
服务/filesstorageservice.java
package com.bezkoder.spring.thymeleaf.file.upload.service;
import java.nio.file.Path;
import java.util.stream.Stream;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
public interface FilesStorageService {
public void init();
public void save(MultipartFile file);
public Resource load(String filename);
public void deleteAll();
public Stream<Path> loadAll();
}
现在我们创建接口的实现。
服务/filesstorageserviceimpl.java
package com.bezkoder.spring.thymeleaf.file.upload.service;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;
@Service
public class FilesStorageServiceImpl implements FilesStorageService {
private final Path root = Paths.get("./uploads");
@Override
public void init() {
try {
Files.createDirectories(root);
} catch (IOException e) {
throw new RuntimeException("Could not initialize folder for upload!");
}
}
@Override
public void save(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.root.resolve(file.getOriginalFilename()));
} catch (Exception e) {
if (e instanceof FileAlreadyExistsException) {
throw new RuntimeException("A file of that name already exists.");
}
throw new RuntimeException(e.getMessage());
}
}
@Override
public Resource load(String filename) {
try {
Path file = root.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("Could not read the file!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("Error: " + e.getMessage());
}
}
@Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(root.toFile());
}
@Override
public Stream<Path> loadAll() {
try {
return Files.walk(this.root, 1).filter(path -> !path.equals(this.root)).map(this.root::relativize);
} catch (IOException e) {
throw new RuntimeException("Could not load the files!");
}
}
}
for文件上传
创建控制器在控制器软件包中,我们创建FileController
。
控制器/filecontroller.java
package com.bezkoder.spring.thymeleaf.file.upload.controller;
// ...
import com.bezkoder.spring.thymeleaf.file.upload.service.FilesStorageService;
@Controller
public class FileController {
@Autowired
FilesStorageService storageService;
@GetMapping("/")
public String homepage() {
return "redirect:/files";
}
@GetMapping("/files/new")
public String newFile(Model model) {
return "upload_form";
}
@PostMapping("/files/upload")
public String uploadFile(Model model, @RequestParam("file") MultipartFile file) {
...
return "upload_form";
}
@GetMapping("/files")
public String getListFiles(Model model) {
...
return "files";
}
@GetMapping("/files/{filename:.+}")
public ResponseEntity<Resource> getFile(@PathVariable String filename) {
...
// return File
}
}
-
@Controller
注释用于定义控制器。 -
@GetMapping
和@PostMapping
注释用于将http get&post请求映射到特定的处理程序方法并返回适当的模板文件。 - 我们使用
@Autowired
将FilesStorageService
bean注入本地变量。
设置模板
in src / main /资源文件夹,创建文件夹和文件如下结构:
标题和页脚
我们将使用百里叶片段(th:fragment
)重复使用一些常见部分,例如标头和页脚。
让我们为他们编写HTML代码。
片段/ footer.html
<footer class="text-center">
Copyright © BezKoder
</footer>
和包含导航栏的标题:
片段/ header.html
<header th:fragment="header">
<nav class="navbar navbar-expand-md bg-dark navbar-dark mb-3">
<a class="navbar-brand" th:href="@{/files}">
BezKoder
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#topNavbar">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="topNavbar">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" th:href="@{/files/new}">Upload</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/files}">Files</a>
</li>
</ul>
</div>
</nav>
</header>
现在我们需要创建HTML文件,然后导入百里叶片段,bootstrap,jQuery和字体很棒。
文件上传表格
upload_form.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0" />
<title>BezKoder - Thymeleaf File Upload example</title>
<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
<script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>
<body>
<div th:replace="fragments/header :: header"></div>
-- file upload form --
<div th:replace="fragments/footer :: footer"></div>
</body>
</html>
文件列表
files.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0" />
<title>BezKoder - Thymeleaf File Upload example</title>
<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>
<body>
<div th:replace="fragments/header :: header"></div>
-- list of files display --
<div th:replace="fragments/footer :: footer"></div>
</body>
</html>
实现文件上传功能
我们使用@GetMapping
和@PostMapping
注释用于将http get&post请求映射到特定的处理程序方法:
- get/files/new:
newFile()
-返回 upload_form.html 模板 - 发布/文件/上传:
uploadFile()
-上传文件
filecontroller.java
import com.bezkoder.spring.thymeleaf.file.upload.service.FilesStorageService;
@Controller
public class FileController {
@Autowired
FilesStorageService storageService;
@GetMapping("/files/new")
public String newFile(Model model) {
return "upload_form";
}
@PostMapping("/files/upload")
public String uploadFile(Model model, @RequestParam("file") MultipartFile file) {
String message = "";
try {
storageService.save(file);
message = "Uploaded the file successfully: " + file.getOriginalFilename();
model.addAttribute("message", message);
} catch (Exception e) {
message = "Could not upload the file: " + file.getOriginalFilename() + ". Error: " + e.getMessage();
model.addAttribute("message", message);
}
return "upload_form";
}
}
upload_form.html
<div class="container" style="max-width: 500px">
<h3 class="mb-3">Thymeleaf File Upload example</h3>
<form
id="uploadForm"
method="post"
th:action="@{/files/upload}"
enctype="multipart/form-data">
<input id="input-file" type="file" name="file" />
<button class="btn btn-sm btn-outline-success float-right" type="submit">
Upload
</button>
</form>
<div
th:if="${message != null}"
class="alert alert-secondary alert-dismissible fade show text-center message mt-3"
role="alert">
[[${message}]]
<button type="button" class="close btn-sm" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
显示文件列表
首先,我们需要创建具有字段的FileInfo
模型:name
&url
。
model/fileinfo.java
package com.bezkoder.spring.thymeleaf.file.upload.model;
public class FileInfo {
private String name;
private String url;
public FileInfo(String name, String url) {
this.name = name;
this.url = url;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
}
在控制器中,我们将返回FileInfo
对象的列表作为模型属性。
@GetMapping
和@PostMapping
注释用于映射http get&post请求到特定处理程序方法:
- get/files:
getListFiles()
-返回 files.html 模板 - get /files /[文件名]:
getFile()
-下载filename
的文件
filecontroller.java
import com.bezkoder.spring.thymeleaf.file.upload.model.FileInfo;
import com.bezkoder.spring.thymeleaf.file.upload.service.FilesStorageService;
@Controller
public class FileController {
@Autowired
FilesStorageService storageService;
// ...
@GetMapping("/")
public String homepage() {
return "redirect:/files";
}
@GetMapping("/files")
public String getListFiles(Model model) {
List<FileInfo> fileInfos = storageService.loadAll().map(path -> {
String filename = path.getFileName().toString();
String url = MvcUriComponentsBuilder
.fromMethodName(FileController.class, "getFile", path.getFileName().toString()).build().toString();
return new FileInfo(filename, url);
}).collect(Collectors.toList());
model.addAttribute("files", fileInfos);
return "files";
}
@GetMapping("/files/{filename:.+}")
public ResponseEntity<Resource> getFile(@PathVariable String filename) {
Resource file = storageService.load(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
}
files.html
<div class="container-fluid" style="max-width: 600px; margin: 0 auto;">
<h2 class="text-center">List of Files</h2>
<div th:if="${files.size() > 0}">
<table class="table table-hover">
<thead class="thead-light">
<tr>
<th scope="col">File Name</th>
<th scope="col">Link</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<tr th:each="file : ${files}">
<td>[[${file.name}]]</td>
<td><a th:href="@{${file.url}}">Download</a></td>
<td>
<a th:href="@{'/files/delete/' + ${file.name}}" th:fileName="${file.name}" id="btnDelete"
title="Delete this file" class="fa-regular fa-trash-can icon-dark btn-delete"></a>
</td>
</tr>
</tbody>
</table>
</div>
<div th:unless="${files.size() > 0}">
<span>No files found!</span>
</div>
</div>
有关删除文件,请在教程之后访问:
Spring Boot Delete File example with Thymeleaf
配置servlet
让我们定义可以在 application.properties 中上传的最大文件大小如下:
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=1MB
spring.servlet.multipart.max-file-size
:每个请求的最大文件大小。
spring.servlet.multipart.max-request-size
:Multipart/form-data的最大请求大小。
句柄文件上传异常
这是我们处理请求超过最大上传大小的情况。该系统将抛出MaxUploadSizeExceededException
,我们将使用@ControllerAdvice
与@ExceptionHandler
Annotation处理例外。
exception/fileuploadexceptionadvice.java
package com.bezkoder.spring.thymeleaf.file.upload.exception;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class FileUploadExceptionAdvice {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public String handleMaxSizeException(Model model, MaxUploadSizeExceededException e) {
model.addAttribute("message", "File is too large!");
return "upload_form";
}
}
我们需要运行init()
FilesStorageService
的方法(必要时也需要deleteAll()
)。因此,开放 thymeleafileuploadapplication.java 并实现了CommandLineRunner
的run()
方法:
package com.bezkoder.spring.thymeleaf.file.upload;
import javax.annotation.Resource;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.bezkoder.spring.thymeleaf.file.upload.service.FilesStorageService;
@SpringBootApplication
public class ThymeleafFileUploadApplication implements CommandLineRunner {
@Resource
FilesStorageService storageService;
public static void main(String[] args) {
SpringApplication.run(ThymeleafFileUploadApplication.class, args);
}
@Override
public void run(String... arg) throws Exception {
// storageService.deleteAll();
storageService.init();
}
}
使用命令运行春季启动应用程序:mvn spring-boot:run
。
源代码
您可以在Github上找到本教程的完整源代码。
用于删除文件:
Spring Boot Delete File example with Thymeleaf
结论
今天,我们已经学会了如何创建带有Multipart文件的Spring Boot Thymeleaf文件上传应用程序,并使用静态文件夹获取文件的信息。
一次一次上传多个文件:
Spring Boot Multiple File upload with Thymeleaf
或fullstack带有前端:
您还可以知道上传excel/csv文件并使用帖子存储在mySQL数据库中的方法:
- Spring Boot: Upload/Import Excel file data into MySQL Database
- Spring Boot: Upload/Import CSV file data into MySQL Database
您可以在:
上找到指令
Spring Boot Upload/Download File to/from Database example
快乐学习!再次见。
进一步阅读
- Spring Boot upload Image and Display with Thymeleaf
- Spring Boot upload multiple files Rest API
- Spring Boot Thymeleaf CRUD example
- Spring Boot Thymeleaf Pagination and Sorting example
例外处理:
- Spring Boot @ControllerAdvice & @ExceptionHandler example
- @RestControllerAdvice example in Spring Boot
单元测试:
部署:
关联: