前言

文件上传和下载是很多网站都会有的功能,这次我们来实践下用 Spring Boot 实现这个功能。为了方便演示,没有实现将文件存储到数据库中,只是用到了一个 List 将上传文件的相关信息保存在内存中,文件本身放在本地磁盘。

创建项目

项目结构图如下:

springboot 接收MultipartFile文件 是bolb_java

pom 依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http:///POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http:///POM/4.0.0 https:///xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>top.yekongle</groupId>
	<artifactId>springboot-fileupload-sample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboot-fileupload-sample</name>
	<description>file upload sample for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

代码编写

application.properties, 全局配置,文件上传配置根据自身需求进行调整。

# Thymeleaf 配置
# 模板文件存放路径
spring.thymeleaf.prefix=classpath:/templates/
# 文件后缀
spring.thymeleaf.suffix=.html
# 使用 html5
spring.thymeleaf.mode=HTML5
# 编码
spring.thymeleaf.encoding=UTF-8
# 是否启动缓存
spring.thymeleaf.cache=false

# 文件上传配置
# 是否支持多文件上传
spring.servlet.multipart.enabled=true
# 上传文件目录
spring.servlet.multipart.location=F:/upload
# 最大上传文件大小,默认1MB
spring.servlet.multipart.max-file-size=5MB
# 最大请求大小, 默认10M
spring.servlet.multipart.max-request-size=50MB
#文件大小阈值,当大于这个阈值时将写入到磁盘,否则存在内存中(默认值0 一般情况下不用特意修改)
spring.servlet.multipart.file-size-threshold=0

FileInfo.java,实体类,代表文件信息

package top.yekongle.fileupload.entity;

import lombok.AllArgsConstructor;
import lombok.Data;

/** 
* @Author: Yekongle 
* @Date: 2020年4月28日
*/

@Data
@AllArgsConstructor
public class FileInfo {
	private String name;
	private String contentType;
	private long size;
    private String uploadDate;
}

UploadController.java

package top.yekongle.fileupload.controller;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
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.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;


import lombok.extern.slf4j.Slf4j;
import top.yekongle.fileupload.entity.FileInfo;

/**
 * @Description: 文件上传
 * @Author: Yekongle
 */

@Controller
@Slf4j
public class UploadController {

	@Value("${spring.servlet.multipart.location}")
	private String path;

	private static final int DEFAULT_MAX_FILE_SIZE = 5 * 1000 * 1000;
	private static final int DEFAULT_REQUEST_MAX_SIZE = 50 * 1000 * 1000;

	private SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	private List<FileInfo> fileInfoList = new ArrayList<FileInfo>();

	@GetMapping("/singleFileUpload")
	public String getSingleFileUploadPage(Model model) {
		model.addAttribute("files", fileInfoList);
		return "single_file_upload";
	}

	@GetMapping("/multiFileUpload")
	public String getMultiFileUploadPage(Model model) {
		model.addAttribute("files", fileInfoList);
		return "multi_file_upload";
	}

	@PostMapping("/singleFileUpload")
	public String singleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes)
			throws IOException {
		File filePath = new File(path);
		log.info("文件保存的路径为: {}", filePath.getAbsolutePath());
		if (!filePath.exists() && !filePath.isDirectory()) {
			// 该文件目录不存在则创建
			filePath.mkdir();
		}
		// 判断文件是否为空
		if (file.isEmpty()) {
			redirectAttributes.addFlashAttribute("message", "文件为空!");
			return "redirect:/singleFileUpload";
		}
		// 判断文件是否为空文件
		if (file.getSize() <= 0) {
			redirectAttributes.addFlashAttribute("message", "文件大小为空,上传失败!");
			return "redirect:/singleFileUpload";
		}
		// 判断文件大小不能大于5MB
		if (file.getSize() > DEFAULT_MAX_FILE_SIZE) {
			redirectAttributes.addFlashAttribute("message", "上传的文件不能大于5M!");
			return "redirect:/singleFileUpload";
		}
		// 获取文件名
		String fileName = file.getOriginalFilename();

		log.info("上传的文件名为:{}", fileName);
		log.info("上传的文件大小为:{}", file.getSize());
		log.info("上传的文件类型为:{}", file.getContentType());
		// 在指定目录下创建该文件
		File newfile = new File(path, fileName);
		if (newfile.exists()) {
			redirectAttributes.addFlashAttribute("message", "该文件已经存在!");
			return "redirect:/singleFileUpload";
		} else {
			FileInfo fileInfo = new FileInfo(fileName, file.getContentType(), file.getSize(), sf.format(new Date()));
			fileInfoList.add(fileInfo);
		}

		// 将文件保存到该目录
		file.transferTo(newfile);
		return "redirect:/singleFileUpload";
	}

	@PostMapping("/multiFileUpload")
	public String multiFileUpload(@RequestParam("files") MultipartFile[] files, RedirectAttributes redirectAttributes)
			throws IOException {
		if (null == files) {
			redirectAttributes.addFlashAttribute("message", "参数为空!");
			return "redirect:/multiFileUpload";
		}
		File filePath = new File(path);
		log.info("文件保存的路径为: {}", filePath.getAbsolutePath());
		if (!filePath.exists() && !filePath.isDirectory()) {
			// 该文件目录不存在则创建
			filePath.mkdir();
		}
		for (MultipartFile uploadFile : files) {
			if (uploadFile.isEmpty()) {
				redirectAttributes.addFlashAttribute("message", "文件为空!");
				return "redirect:/multiFileUpload";
			}
			// 判断文件是否为空文件
			if (uploadFile.getSize() <= 0) {
				redirectAttributes.addFlashAttribute("message", "文件大小为空,上传失败!");
				return "redirect:/multiFileUpload";
			}
			// 判断文件大小不能大于50MB
			if (uploadFile.getSize() > DEFAULT_REQUEST_MAX_SIZE) {
				redirectAttributes.addFlashAttribute("message", "上传的文件不能大于5M!");
				return "redirect:/multiFileUpload";
			}
			// 获取文件名
			String fileName = uploadFile.getOriginalFilename();

			log.info("上传的文件名为:{}", fileName);
			log.info("上传的文件大小为:{}", uploadFile.getSize());
			log.info("上传的文件类型为:{}", uploadFile.getContentType());
			// 在指定目录下创建该文件
			File newfile = new File(path, fileName);
			if (!newfile.exists()) {
				FileInfo fileInfo = new FileInfo(fileName, uploadFile.getContentType(), uploadFile.getSize(),
						sf.format(new Date()));
				fileInfoList.add(fileInfo);
			}

			// 将文件保存到该目录
			uploadFile.transferTo(newfile);
		}

		return "redirect:/multiFileUpload";
	}
}
package top.yekongle.fileupload.controller;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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.ResponseBody;

import lombok.extern.slf4j.Slf4j;

/**
 * @Description: 文件下载
 * @Author: Yekongle
 */

@Controller
@Slf4j
public class DownloadController {

	private static final String FILE_STORE_PATH = "F:/upload";

	@GetMapping("/downloadFile")
	public String getUploadPage() {
		return "file_download";
	}

	/**
	 * 文件下载
	 *
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	@PostMapping("/downloadFile")
	@ResponseBody
	public String downLoadFile(HttpServletRequest request, HttpServletResponse response, Model model)
			throws UnsupportedEncodingException {
		log.info("要下载的文件:{}", request.getParameter("fileName"));
		String fileName = request.getParameter("fileName");
		log.info("File name:{}", fileName);

		File file = new File(FILE_STORE_PATH, fileName);
		log.info("File path:{}", file.getAbsolutePath());

		if (file.exists()) {
			// 配置文件下载
			response.setHeader("content-type", "application/octet-stream");
			response.setContentType("application/octet-stream");
			// 下载文件能正常显示中文
			response.setHeader("Content-Disposition",
					"attachment;filename=" + new String(fileName.getBytes(), "utf-8"));
			// 文件内容长度
			response.setHeader("Content-Length", "" + file.length());
			// 读取文件
			BufferedInputStream bi = null;
			try {
				byte[] buffer = new byte[1024];
				bi = new BufferedInputStream(new FileInputStream(file));
				ServletOutputStream outputStream = response.getOutputStream();
				int i = -1;
				while (-1 != (i = bi.read(buffer))) {
					outputStream.write(buffer, 0, i);
				}
				return "下载成功";
			} catch (Exception e) {
				e.printStackTrace();
				return "下载失败";
			} finally {
				if (bi != null) {
					try {
						bi.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}

		return "文件不存在";
	}

}

前端页面
single_file_upload.html, 只可以上传单个文件,上传成功会返回文件信息

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>单个文件上传</title>
<script src="../static/js/jquery-1.10.2.min.js"
	th:src="@{/js/jquery-1.10.2.min.js}"></script>
</head>
<body>
	<div th:if="${message}">
		<h2 style="color: red;" th:text="${message}" />
	</div>

	<div>
		<h2>单个文件上传</h2>
		<form action="singleFileUpload" method="POST"
			enctype="multipart/form-data">
			<p>
				文件:<input type="file" name="file" />
			</p>
			<p>
				<input type="submit" value="上传" />
			</p>
		</form>
	</div>

	<div>
		<table border="1">
			<thead>
				<tr>
					<td>Name</td>
					<td>contentType</td>
					<td>size</td>
					<td>uploadDate</td>
				</tr>
			</thead>
			<tbody>
				<tr th:if="${files.size()} eq 0">
					<td colspan="4">没有文件信息!!</td>
				</tr>
				<tr th:each="file : ${files}">
					<td th:text="${}" /></td>
					<td th:text="${file.contentType}"></td>
					<td th:text="${file.size}"></td>
					<td th:text="${file.uploadDate}"></td>
				</tr>
			</tbody>
		</table>
	</div>

</body>

</html>

multi_file_upload.html, 可以多个文件一起上传,同样会返回文件信息列表

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>多个文件上传</title>
<script src="../static/js/jquery-1.10.2.min.js"
	th:src="@{/js/jquery-1.10.2.min.js}"></script>
</head>
<body>
	<div th:if="${message}">
		<h2 style="color: red;" th:text="${message}" />
	</div>

	<div>
		<h2>多文件上传</h2>
		<form action="/multiFileUpload" method="POST"
			enctype="multipart/form-data">
			<p>
				多文件上传:<input type="file" name="files" multiple="multiple" />
			</p>
			<p>
				<input type="submit" value="上传" />
			</p>
		</form>
	</div>

	<div>
		<table border="1">
			<thead>
				<tr>
					<td>Name</td>
					<td>contentType</td>
					<td>size</td>
					<td>uploadDate</td>
				</tr>
			</thead>
			<tbody>
				<tr th:if="${files.size()} eq 0">
					<td colspan="4">没有文件信息!</td>
				</tr>
				<tr th:each="file : ${files}">
					<td th:text="${}" /></td>
					<td th:text="${file.contentType}"></td>
					<td th:text="${file.size}"></td>
					<td th:text="${file.uploadDate}"></td>
				</tr>
			</tbody>
		</table>
	</div>
</body>
</body>
</html>

file_download.html, 输入上传的文件名即可下载

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文件下载</title>
<script src="../static/js/jquery-1.10.2.min.js"
	th:src="@{/js/jquery-1.10.2.min.js}"></script>
</head>
<body>
	<div>
	<h2>文件下载</h2>

	<form action="downloadFile" method="POST" enctype="multipart/form-data">
		<p>
			文件名:<input type="text" name="fileName" />
		</p>
		<p>
			<input type="submit" value="下载文件" />
		</p>
	</form>
	</div>
</body>
</html>

运行演示

启动项目

单个文件上传,访问 http://localhost:8080/singleFileUpload

springboot 接收MultipartFile文件 是bolb_spring_02

文件上传判错,比如不选择文件直接上传

springboot 接收MultipartFile文件 是bolb_spring_03

多个文件上传,访问 http://localhost:8080/multiFileUpload

springboot 接收MultipartFile文件 是bolb_java_04

下载上传的文件,访问 http://localhost:8080/downloadFile

输入文件名点击下载

springboot 接收MultipartFile文件 是bolb_download_05

项目已上传至 Github: https:///yekongle/springboot-code-samples/tree/master/springboot-fileupload-sample , 希望对小伙伴们有帮助哦。