春季启动百里角文件上传示例
#网络开发人员 #编程 #java #springboot

在本教程中,我将向您展示如何在Spring Boot项目中使用Thymeleaf和Bootstrap实现文件上传。我们还使用Spring Web MultipartFile接口来处理HTTP multi-part请求。

相关文章:

fulstack:

关联:

我们的Spring Boot + ThyMeleaf文件上传示例将具有以下功能:

  • 将文件上传到服务器中的静态文件夹
  • 使用链接从服务器下载文件
  • 获取文件的信息列表(文件名和URL)
  • 这是文件上传表格:

thymeleaf-file-upload-example

  • 如果文件超过特定的最大大小:

thymeleaf-file-upload-exception

  • 这是存储所有上传文件的静态文件夹:

thymeleaf-file-upload-folder

  • 您可以看到带有下载链接的上传文件列表:

thymeleaf-file-upload-spring-boot

在本教程中,我没有解释删除文件的方式。如果您想知道这一点,只需访问:
Spring Boot Delete File example with Thymeleaf

或添加分页以下教程:
Spring Boot Thymeleaf Pagination example

项目结构

thymeleaf-file-upload-spring-boot-project

让我简要解释。

  • 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请求映射到特定的处理程序方法并返回适当的模板文件。
  • 我们使用@AutowiredFilesStorageService bean注入本地变量。

设置模板

in src / main /资源文件夹,创建文件夹和文件如下结构:

thymeleaf-file-upload-example-template

标题和页脚

我们将使用百里叶片段(th:fragment)重复使用一些常见部分,例如标头和页脚。
让我们为他们编写HTML代码。

片段/ footer.html

<footer class="text-center">
  Copyright &copy; 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">&times;</span>
    </button>
  </div>
</div>

显示文件列表

首先,我们需要创建具有字段的FileInfo模型:nameurl

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 并实现了CommandLineRunnerrun()方法:

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-files-to-database-table-files

您可以在:
上找到指令 Spring Boot Upload/Download File to/from Database example

快乐学习!再次见。

进一步阅读

例外处理:

单元测试:

部署:

关联: