更新时间:2023年5月25日 08点52分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="uploadFile" value="请选择文件">
<input type="submit" value="上传">
</form>
</body>
</html>
package com.zibo.api.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@RestController
public class FileUploadController {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
@PostMapping("upload")
public String upload(MultipartFile uploadFile, HttpServletRequest request){
String realPath = request.getSession().getServletContext().getRealPath("/uploadFile/");
String format = sdf.format(new Date());
File folder = new File(realPath + format);
if(!folder.isDirectory()){
folder.mkdirs();
}
String oldName = uploadFile.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
try {
uploadFile.transferTo(new File(folder,newName));
return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/uploadFile/" + format + "/" + newName;
}catch (IOException e){
e.printStackTrace();
}
return "上传失败!";
}
}
如果配置了拦截器可能无法直接访问静态文件,需要进行响应的调整(见参考文章),我这里为了方便直接把拦截器注释了!
参考文章:
【Spring Boot】007-Spring Boot Web开发:静态资源导入、Thymeleaf使用
(其中的“自定义策略”部分)
https://blog.csdn.net/qq_29689343/article/details/108552420
# 是否开启文件上传支持,默认为true
spring.servlet.multipart.enabled=true
# 文件写入磁盘的阈值,默认为0
spring.servlet.multipart.file-size-threshold=0
# 文件上传临时保存的位置
spring.servlet.multipart.location=E:\\temp
# 单个文件最大大小
spring.servlet.multipart.max-file-size=1MB
# 多文件上传时总最大大小
spring.servlet.multipart.max-request-size=10MB
# 文件是否延迟解析,默认false
spring.servlet.multipart.resolve-lazily=false
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<form action="/uploads" method="post" enctype="multipart/form-data">
<input type="file" name="uploadFiles" value="请选择文件">
<input type="submit" value="上传">
</form>
</body>
</html>
@PostMapping("uploads")
public String upload(MultipartFile[] uploadFiles, HttpServletRequest request){
// 遍历 uploadFiles 数组,分别存储即可!
}
package com.wq.floodserver.controller.system;
import com.wq.floodserver.common.pojo.ApiException;
import com.wq.floodserver.common.pojo.Pair;
import com.wq.floodserver.common.util.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* 文件 Controller
*
* @author dev
* @date 2023/3/30 16:55
* @slogan 真正的大师永远怀着一颗学徒的心。——易大师
*/
@RestController
@RequestMapping("/file")
@Slf4j
public class UploadFileController {
/**
* 接收前端上传的文件
*
* @param file 文件
* @return 上传后的文件路径 (如:\\uploads\2023\03\30\) + 文件名.后缀)
*/
@PostMapping("/upload")
public Pair<String, String> upload(MultipartFile file) {
// 文件上传后的路径
return FileUtil.saveFile(file);
}
@GetMapping("/download")
public void download(String path, HttpServletResponse response) {
// 读取文件
File file = new File(FileUtil.USER_DIR, path);
// 从文件路径中获取文件名
String fileName = FileUtil.getFileName(path);
// 获取文件输入流
try (
FileInputStream is = new FileInputStream(file)
) {
// 解决文件名中文乱码的问题
response.setHeader("content-disposition",
"attachment;fileName=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
// 设置强制下载不打开
response.setContentType("application/force-download");
// 获取响应输出流
ServletOutputStream os = response.getOutputStream();
// 文件拷贝
IOUtils.copy(is, os);
// 关流方式(优雅)
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
} catch (IOException e) {
throw new ApiException(e);
}
}
/**
* 删除文件
*/
@GetMapping("/delete")
public Boolean delete(String path) {
return FileUtil.deleteFile(path);
}
}
package com.wq.floodserver.common.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;
import java.util.regex.Pattern;
import com.wq.floodserver.common.pojo.Pair;
import com.wq.floodserver.common.pojo.ApiException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* 文件工具类
*
* @author dev
* @date 2022/7/13 0013 11:57
*/
public class FileUtil {
private FileUtil() {
}
public static final String USER_DIR = System.getProperty("user.dir");
public static final String UPLOADS_PATH = StringUtils.join(USER_DIR, File.separator, "uploads", File.separator);
private static final Integer BUFFER_SIZE = 8192;
/**
* 保存文件
* 文件的相对地址加了多余的反斜杠,为了防止 idea 报错
*
* @param file 文件
* @return 记录原始文件名 + 相对的文件路径(数据库保存的文件地址:时间文件夹(如:\\uploads\2023\03\30\) + 文件名.后缀)
*/
public static Pair<String, String> saveFile(File file) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(StringUtils.join("yyyy", File.separator, "MM", File.separator, "dd"));
// 文件夹路径
String finalPath = StringUtils.join(UPLOADS_PATH, simpleDateFormat.format(new Date()), File.separator);
// file
File dir = new File(finalPath);
// 如果文件夹不存在,则创建
if (!dir.exists()) {
boolean mkdirs = dir.mkdirs();
if (!mkdirs) {
throw new ApiException(50000, "创建文件夹失败");
}
}
// 记录原始文件名
String originalName = file.getName();
// 文件重命名
String fileName =
StringUtils.join(finalPath, UUID.randomUUID().toString(), file.getName().substring(file.getName().lastIndexOf(".")));
// 保存文件
boolean renameTo = file.renameTo(new File(fileName));
if (!renameTo) {
throw new ApiException(50000, "保存文件失败");
}
file.deleteOnExit();
LogUtility.uploadFile(StringUtils.join("上传文件:", originalName, ",保存路径:", fileName.substring(FileUtil.USER_DIR.length())));
// 直接返回相对路径,直接存入数据库即可
return Pair.of(originalName, fileName.substring(FileUtil.USER_DIR.length()));
}
/**
* 保存文件
*
* @param multipartFile 文件
* @return 保存后的相对路径
*/
public static Pair<String, String> saveFile(MultipartFile multipartFile) {
try {
return saveFile(multipartFileToFile(multipartFile));
} catch (NullPointerException e) {
throw new ApiException(50000, "保存文件失败:" + e.getMessage());
}
}
/**
* 根据相对路径删除文件,示例地址加了多余的斜杠,实际使用时注意
*
* @param path 数据库保存的文件地址:时间文件夹(如:\\uploads\2023\03\30\) + 文件名.后缀
* 文件上传得到的完整地址:D:\MyFile\GitHub\flood-server\\uploads\2023\03\30\269889b4-359d-48b5-93fb-049e621062d5.xmind
* UPLOADS_PATH:D:\MyFile\GitHub\flood-server\\uploads\
* 数据库保存的文件地址:\\uploads\2023\03\30\269889b4-359d-48b5-93fb-049e621062d5.xmind
* @return 是否删除成功
*/
public static Boolean deleteFile(String path) {
String originalPath = path;
// 替换掉“\\uploads\”
path = path.replace("\\uploads\\", "");
// 验证路径的合法性:(策略)验证是否是以四位数年份开头即可
String pattern = "^\\d{4}";
if (Pattern.matches(pattern, path)) {
throw new ApiException("无效的文件路径:" + originalPath);
}
// 创建 File
File file = new File(StringUtils.join(UPLOADS_PATH, path));
// 文件删除日志
LogUtility.deleteFile(path);
// 删除文件
return file.delete();
}
/**
* 将 multipartFile 转换成 临时 file 文件.(该临时文件使用完记得 delete掉.)
*
* @param multipartFile multipartFile
* @return 临时的 file 文件.
*/
public static File multipartFileToFile(MultipartFile multipartFile) {
if (multipartFile == null) {
return null;
}
File file = new File(Objects.requireNonNull(multipartFile.getOriginalFilename()));
try (
OutputStream os = new FileOutputStream(file);
InputStream inputStream = multipartFile.getInputStream()
) {
int bytesRead;
byte[] buffer = new byte[8192];
while ((bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE)) != -1) {
os.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
e.printStackTrace();
}
return file;
}
/**
* 从文件路径中获取文件名
*
* @param path 文件路径
* @return 文件名
*/
public static String getFileName(String path) {
return path.substring(path.lastIndexOf(File.separator) + 1);
}
}