环境
IDE:IntelliJ IDEA 2019.2.4 (Ultimate Edition)
操作系统:Windows 10 x64
Spring Boot 版本:2.2.2.RELEASE
JDK:1.8
示例
注意:以下示例只是将接收到文件重命名,然后存储到指定的位置,并没有执行其他操作。
项目结构:
pom.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/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.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mk</groupId>
<artifactId>spring-boot-upload</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-upload</name>
<description>文件上传</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</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.yml 文件:
spring:
servlet:
# 参考:org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
# 参考:https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/web/servlet/MultipartProperties.html
multipart:
enabled: true
# 指定每个上传的文件的最大值
max-file-size: 100MB # specifies the maximum size permitted for uploaded files. The default is 1MB
# 指定一个请求中所有上传的文件的总最大值
max-request-size: 100MB # specifies the maximum size allowed for multipart/form-data requests. The default is 10MB.
# file-size-threshold: # specifies the size threshold after which files will be written to disk. The default is 0.
# 临时存储目录
location: G:/20191212 # specifies the directory where uploaded files will be stored. When not specified, a temporary directory will be used.
server:
port: 8086
templates/index.html 文件,即上传页面,主要是第 8 ~ 12 行:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="/attachment/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<br />
<input type="submit" value="upload" />
</form>
</body>
</html>
com.mk.util.RandomUtils 随机工具类:
注意:该类中有些方法的逻辑并不严谨,请谨慎使用。
package com.mk.util;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
public class RandomUtils {
private static DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
private static Random random = new Random();
/**
* <p> 返回一个伪随机数,在最小值(包括)和最大值(不包括)之间均匀分布的整数值,该整数值来自这个随机数生成器的序列。
* @param min 最小值
* @param max 最大值
* @return 返回一个伪随机数,在最小值(包括)和最大值(不包括)之间均匀分布的整数值。
*/
public static int nextInt(int min, int max) {
return (random.nextInt(max - min) + min);
}
/**
* <p> 返回一个在最小值(包括)和最大值(不包括)之间均匀分布的伪随机整数值的字符串形式。
* @param min 最小值
* @param max 最大值
* @return 返回一个在最小值(包括)和最大值(不包括)之间均匀分布的伪随机整数值的字符串形式。
*/
public static String nextIntToString(int min, int max) {
return "" + nextInt(min, max);
}
/**
* <p> 获取文件的类型名称,即扩展名。
* <p> 示例:
* <p> 执行 <code>RandomUtils.getTypeName("file.txt", true);</code>,返回 <code>".txt"</code>
* <p> 执行 <code>RandomUtils.getTypeName("file.txt", false);</code>,返回 <code>"txt"</code>
* @param filename 文件名称
* @param dot 是否保留类型名称前面的点(.),<code>true</code> 保留,<code>false</code> 去除。
* @return 如果存在,返回文件类型名称。否则返回 <code>null</code>
*/
public static String getTypeName(String filename, boolean dot) {
if ((filename == null) || (filename.equals(""))) {
return null;
}
int lastOccurrence = filename.lastIndexOf(".");
if (lastOccurrence != -1) {
lastOccurrence = (dot == true) ? lastOccurrence : (lastOccurrence + 1);
String typeName = filename.substring(lastOccurrence);
return typeName;
}
return null; // 该文件不存在类型名称
}
/**
* <p> 使用当前日期 + 四位随机数作为文件名
* @param originalFilename 原文件名,此处需要原文件的后缀名(即文件的类型)
* @return
*/
public static String createRandomFilenameByDate(String originalFilename) {
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
String filename = df.format(new Date()) + (random.nextInt(9000) + 1000) + extension;
return filename;
}
/**
*
* @param originalFilename
* @param suffix 临时文件的后缀名
* @return
*/
public static String createRandomFilenameByDate(String originalFilename, String suffix) {
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
String filename = df.format(new Date()) + (random.nextInt(9000) + 1000) + extension;
return filename + suffix;
}
/**
* <p> 使用 UUID 作为文件名
* @param originalFilename
* @return
*/
public static String createRandomFilenameByUUID(String originalFilename) {
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
String filename = uuid + extension;
return filename;
}
/**
* <p> 使用当前系统毫秒数 + 四位随机数作为文件名
* @param originalFilename
* @return
*/
public static String createRandomFilenameByTimeMillis(String originalFilename) {
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
long t = System.currentTimeMillis();
String filename = "" + t + (random.nextInt(9000) + 1000) + extension;
return filename;
}
/**
* <p> 以 yyyy-MM-dd 格式返回当前日期的字符串
* @return
*/
public static String createDirectoryNameByDate() {
return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
}
/**
* <p> 按照指定的日期格式,返回一个指定的日期的字符串。如果没有指定日期格式,则使用 "yyyy-MM-dd" 做为默认的日期格式。
* <p> 请使用 <code>com.mk.commons.util.RandomUtils.createDateFormatStringWith(String, Date)</code> 代替。
* @param dateFormat 日期格式。例如:"yyyy-MM-dd"
* @param date 日期。如果日期为 null,则使用当前时间。
* @return 按照指定的日期格式,返回一个指定的日期的字符串。
*/
@Deprecated
public static String createStringBy(String dateFormat, Date date) {
dateFormat = ((dateFormat == null) || (dateFormat.equals(""))) ? "yyyy-MM-dd" : dateFormat;
date = (date == null)?(new Date()):date;
return new SimpleDateFormat(dateFormat).format(date);
}
/**
* <p> 按照指定的日期格式,返回一个指定的日期的字符串。如果没有指定日期格式,则使用 "yyyy-MM-dd" 做为默认的日期格式。
* <p> 具体的日期格式参见:{@link SimpleDateFormat}
* <p> 注意:不要输入非法的日期格式,因为本方法没有对日期格式进行校验。
* @param dateFormat 日期格式。如果 dateFormat 为 null 或为空字符串,则使用 "yyyy-MM-dd" 做为默认值。
* @param date 日期。如果日期为 null,则使用当前时间。
* @return 按照指定的日期格式,返回一个指定的日期的字符串。
*/
public static String createDateFormatStringWith(String dateFormat, Date date) {
dateFormat = ((dateFormat == null) || (dateFormat.equals(""))) ? "yyyy-MM-dd" : dateFormat;
date = (date == null) ? (new Date()) : date;
return new SimpleDateFormat(dateFormat).format(date);
}
/**
* <p> 按照指定的日期格式,返回一个附有前缀和后缀的指定的日期的字符串。如果没有指定日期格式,则使用 "yyyy-MM-dd" 做为默认的日期格式。
* @param prefix 前缀
* @param dateFormat 日期格式。例如:"yyyy-MM-dd"
* @param date 日期。如果日期为 null,则使用当前时间。
* @param suffix 后缀
* @return 按照指定的日期格式,返回一个附有前缀和后缀的指定的日期的字符串。
*/
public static String createDateFormatStringWith(String prefix, String dateFormat, Date date, String suffix) {
prefix = (prefix == null)?"":prefix;
suffix = (suffix == null)?"":suffix;
dateFormat = (dateFormat == null)?"yyyy-MM-dd":dateFormat;
date = (date == null)?(new Date()):date;
return prefix + (new SimpleDateFormat(dateFormat).format(date)) + suffix;
}
}
com.mk.controller.IndexController 导航控制器,用于导航到 templates/index.html 页面,见第 12 ~ 15 行:
package com.mk.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* <p>导航控制器</p>
*/
@Controller
public class IndexController {
@GetMapping(value = {"/"})
public String index() {
return "index"; // 跳转到 templates/index.html 页面
}
}
com.mk.controller.AttachmentController 附件控制器,用于处理上传文件,这里只是将接收到文件重命名,然后存储到指定的位置。
package com.mk.controller;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.mk.util.RandomUtils;
@RestController
@RequestMapping("/attachment")
public class AttachmentController {
@PostMapping("/upload")
public Map<String, Object> upload(@RequestParam(value = "file") MultipartFile file) {
Map<String, Object> data = new HashMap<String, Object>();
if ((file != null) && (!file.isEmpty())) {
// 原文件名
String originalFilename = file.getOriginalFilename();
// 构建文件的存储目录
File directory = new File("G:", RandomUtils.createDateFormatStringWith("yyyyMMdd", new Date()));
if (!directory.exists()) {
directory.mkdirs(); // 创建不存在的目录
}
// 因为上传的文件可能出现同名,所以需要创建一个新文件名,防止出现同名文件被覆盖的情况。
String newFilename = RandomUtils.createDateFormatStringWith("yyyyMMddHHmmssSSS", new Date()) // 当前日期的字符串形式
+ RandomUtils.nextIntToString(1000, 10000) // 随机数,最小 1000,最大 9999
+ RandomUtils.getTypeName(originalFilename, true); // 获取文件的类型
// 保存上传的文件到指定的位置
File destination = new File(directory, newFilename);
try {
file.transferTo(destination);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 检查文件是否保存成功
if (destination.exists()) {
data.put("location", destination.getAbsoluteFile());
return data;
}
}
return null;
}
}
效果