使用 JPA、Thymeleaf、Multipart 在 Spring Boot 中上传多个文件

Spring Boot构建在spring之上,包含了spring的所有特性。如今,它正成为开发人员的最爱,因为它的快速生产就绪环境使开发人员能够直接专注于逻辑,而不是费力地进行配置和设置。

Spring Boot 是一个基于微服务的框架,在其中创建一个可用于生产的应用程序只需很少的时间。在本文中,我们将学习如何借助 Spring boot 将多个文件上传到服务器。

因此,为了使其成功,我们将使用 MySQL 作为数据库,Thymeleaf 作为模板引擎,并使用 JPA 将数据保存在数据库中。

application.properties file:

# It means server will run on port 8080
server.port=8080    

# connection url
spring.datasource.url=jdbc:mysql://localhost:3306/filedb 
   
# DB user  
spring.datasource.username=root   

# DB password
spring.datasource.password=[Your Password]  
  
# update the schema
spring.jpa.hibernate.ddl-auto=update  

# 将其通用 SQL 语句转换为供应商特定的 DDL、DML
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect   
   
# off to show SQL query
spring.jpa.show-sql=false 

FileModal.java

让我们编写一个简单的 POJO 类,作为网络服务方法的输入和输出。

package com.example.user.modal;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;

// Entity annotation defines that a 
// class can be mapped to a table
@Entity

// @Table annotation defines name of the table
@Table(name =
"filemodal"
public class FileModal {
    
// @Id annotation specifies the
    
// primary key of an entity
    @Id

    
// @GeneratedValue annotation Provides for the 
    
// specification of generation strategies
    
// for the values of primary keys
    @GeneratedValue(strategy = GenerationType.IDENTITY) 

    
// @Column annotation specifies
    
// the name of the column
    @Column(name =
"id"
    long id;
    @Column(name =
"name")
    String fileName;
    @Lob
    @Column(name =
"content")
    String content;
    @Column(name =
"filetype")
    private String fileType;

    public FileModal() {
        super();
    }
    public FileModal(String fileName, String content, String fileType) {
        super();
        this.fileName = fileName;
        this.content = content;
        this.fileType = fileType;
    }
    public String getFileName() {
        return fileName;
    }
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public String getFileType() {
        return fileType;
    }
    public void setFileType(String fileType) {
        this.fileType = fileType;
    }
}

FileRepository.java

FileRepository.java 扩展了用于 DB 操作的 JpaRepository 接口。

package com.example.user.repoasitory;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.user.modal.FileModal;

// @Repository 注解用于表明该类提供了对对象进行存储、检索、搜索、更新和删除操作的机制。
@Repository
public interface FileRepository extends JpaRepository<FileModal, Long> {

}

FileService.java

FileService.java 接口包含两个抽象方法 getAllFiles() 和 saveAllFilesList(List<FileModal> fileList)。

package com.example.user.service;

import java.util.List;
import com.example.user.modal.FileModal;

public interface FileService {
    List<FileModal> getAllFiles();
    void saveAllFilesList(List<FileModal> fileList);
}

FileServiceImplementation.java

FileServiceImplementation.java 类实现了 FileService.java 接口并提供了抽象方法的定义。

package com.example.user.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.user.modal.FileModal;
import com.example.user.repoasitory.FileRepository;

//@Service 注解用于提供某些业务功能的类
//
@Service
public class FileServiceImplementation implements FileService {

   
// @Autowired 注解用于注入 
   
// FileRepository 的对象依赖关系
    @Autowired
    FileRepository fileRepository;

    @Override
    public List<FileModal> getAllFiles() {
        
// fetch all the files form database
        return fileRepository.findAll();
    }
    public void saveAllFilesList(List<FileModal> fileList) {
        
// Save all the files into database
        for (FileModal fileModal : fileList)
            fileRepository.save(fileModal);
    }
}

FileController.java

FileController.java 类通过视图接收用户输入,然后在模型的帮助下处理用户数据,并将结果传回视图。因此,用户将从用户界面上传文件,并在相应的方法中以多部分数组的形式接收这些文件。

package com.example.user.controller;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import com.example.user.modal.FileModal;
import com.example.user.service.FileServiceImplementation;

// @Controller 注解用于将任何 java 类标记为控制器类
@Controller
public class FileController {

    @Autowired
    FileServiceImplementation fileServiceImplementation;

    
// @GetMapping 注解,用于将 HTTP GET 请求映射到
    
// 特定的处理程序方法上。*/
    @GetMapping(
"/"
    public String getData() {
        return
"File";
    }

   
// @PostMapping 注解将 HTTP POST 
   
// 请求映射到特定的处理程序方法上
    @PostMapping(
"/"
    public String uploadMultipartFile(@RequestParam(
"files") MultipartFile[] files, Model modal) {
    try {
       
// 声明用于收集文件数据的空列表 
      
// 这些数据将来自用户界面
        List<FileModal> fileList = new ArrayList<FileModal>();
        for (MultipartFile file : files) {
            String fileContentType = file.getContentType();
            String sourceFileContent = new String(file.getBytes(), StandardCharsets.UTF_8);
            String fileName = file.getOriginalFilename();
            FileModal fileModal = new FileModal(fileName, sourceFileContent, fileContentType);
            
            
// Adding file into fileList
            fileList.add(fileModal);
            }
    
            
// Saving all the list item into database
            fileServiceImplementation.saveAllFilesList(fileList);

        } catch (Exception e) {
            e.printStackTrace();
        }
    
       
// 使用模态类向 View 发送文件列表 
     
// fileServiceImplementation.getAllFiles() 用于 
      
// 从 DB 获取所有文件列表
        modal.addAttribute(
"allFiles", fileServiceImplementation.getAllFiles());
    
        return
"FileList";
    }
}

FileApplication.java

这是一个主类,弹簧应用程序就是从这里开始运行的。

package com.example.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Spring Boot @SpringBootApplication 注解用于标记一个配置 
//类,该类声明了一个或多个 @Bean 方法,还触发了自动配置
//和组件扫描。这与使用 @Configuration、
// @EnableAutoConfiguration 和 @ComponentScan 注解声明一个类是一样的。
@SpringBootApplication
public class FileApplication {

    public static void main(String[] args) {
        SpringApplication.run(FileApplication.class, args);
    }

}

file.html
在这个 HTML 文件中,<input type="file"> 接受单个文件。您可以通过 <input type="file" multiple> 允许输入多个文件,webkitdirectory 会将浏览器的文件选择器切换为选择一个目录。该目录内的所有文件以及任何嵌套子目录内的文件都将被选中作为文件输入。 enctype='multipart/form-data 是一种编码类型,允许通过 POST 发送文件。简单地说,没有这种编码,文件就不能通过 POST 发送。如果要允许用户通过表单上传文件,就必须使用这种 enctype。

<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>File Upload</title>
</head>

<style>
input[type=file], select {
    width: 100%;
    padding: 12px 20px;
    margin: 8px 0;
    display: inline-block;
    border: 1px solid ccc;
    border-radius: 4px;
    box-sizing: border-box;
    background-color: #1affff;
}

.button {
    background-color: #4CAF50;
/* Green */
    border: none;
    color: white;
    padding: 10px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px;
    margin-left: 45%; 
    cursor: pointer;
}

.button4 {
    border-radius: 10px;
}

div {
    border-radius: 5px;
    background-color: a6a6a6;
    padding: 10px;
    width: 50%;
    margin: auto;
}
</style>
<body>
    <form method=
"POST" enctype="multipart/form-data">
        <div><div>
            <input type=
"file" name="files"
            webkitdirectory multiple></div>
            <button class=
"button button4" type="submit">
            Upload</button> </div>
    </form>
</body>
</html>

FileList.html

因此,在这个 HTML 文件中,我们只需使用 thymeleaf 打印与文件相关的数据即可

<tr th:each="file: ${allFiles}">
       <td th:text=
"${file.fileName}"></td>
       <td th:text=
"${file.fileType}"></td>
</tr>

这是一个thymeleaf 循环,用于遍历 allFiles 列表中的所有项目,这些项目以键值对的形式通过 modal.addAttribute("allFiles", fileServiceImplementation.getAllFiles()); 方法来自 Java 控制器。

  • th:each="file:${allFiles}" -> 逐个将 FileModal 赋值到文件名变量。
  • th:text="${file.fileName}" -> 访问 FileModal Pojo 中写入的 fileName 字段。
  • th:text="${file.fileType}" -> 访问您在 FileModal Pojo 中编写的 fileType 字段。

完整html:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>All files</title>
<meta charset=
"utf-8" />
<meta name=
"viewport" content="width=device-width, initial-scale=1" />
<link rel=
"stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/
    4.1.0/css/bootstrap.min.css
">
<script src=
"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/
        jquery.min.js
"></script>
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/
        umd/popper.min.js
"></script>
<script src=
"https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/js/
        bootstrap.min.js
"></script>
</head>

<body>
    <div class=
"container h-100">
    <div class=
"row h-100 justify-content-center align-items-center">
    <div class=
"col-md-7 table-responsive">
    <h2>Uploaded Files</h2>
    <table id=
"customerTable" class="table">
    <thead>
            <tr>
                <th>File Name</th>
                <th>File Type</th>
            </tr>
    </thead>
    <tbody>
            <tr th:each=
"file: ${allFiles}">
                <td th:text=
"${file.fileName}"></td>
                <td th:text=
"${file.fileType}"></td>
            </tr>
    </tbody>
    </table>
    <hr>
    <div>
        <a href=
"/" class="btn btn-light btn-block"
        role=
"button">Back to Upload Form</a>
    </div>
    </div>
    </div>
    </div>
</body>
</html>

完成上述所有步骤后,只需以 Spring Boot 的方式运行应用程序即可。

打开浏览器,在 URL 中输入 localhost:8080/ 并点击回车。