目录
spring.servlet.multipart.location 临时目录
嵌入式 Tomcat 文件上传
url 映射虚拟路径需求分析
1、Java web 应用开发完成后如果是导入外置的 Tomcat 的 webapps 目录的话,那么上传的文件可以直接的放在应用的 web 目录下去就好了,浏览器可以很方便的进行访问。
2、Spring Boot 默认使用嵌入式 Tomcat ,将来打包成可执行 Jar 文件进行部署,显然打成 jar 包后,总不可能再将上传的文件放在 resources 目录下去了,肯定是放在其它的磁盘目录下。
3、Spring Boot 于是提供了 url 地址匹配本地虚拟路径的功能:
1)上传文件到服务器,服务器将文件保存到了本地,如:E:\wmx\uploadFiles\222.png 2)用户访问应该是服务器地址,如:http://localhost:9393/fileServer/uploadFiles/222.png 3)于是通过配置资源映射,使 url 中的 /uploadFiles/ 映射到本地的 E:\wmx\uploadFiles\ 即可,E:\wmx\uploadFiles 则为虚拟路径。 |
4、url 映射虚拟路径主要是针对网络访问和下载,与文件上传关系不大。
url 映射虚拟路径配置
1、使用资源映射虚拟路径很简单,使用 WebMvcConfigurer 扩展配置即可,可参考自定义资源映射虚拟路径
2、全局配置文件自定义配置:
uploadFile:
resourceHandler: /uploadFiles/** #请求 url 中的资源映射
location: E:/wmx/uploadFiles/ #自定义上传文件本地保存路径
src/main/resources/application.yml · 汪少棠/fileServer - Gitee.com
3、WebMvcConfigurer 扩展 webMvc 配置,设置资源映射
/**
* WebMvc 扩展配置类,应用一启动,本类就会执行
* url 映射虚拟路径配置
*
* @author wangMaoXiong
* Created by Administrator on 2019/3/17 0017.
*/
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
private static final Logger logger = LoggerFactory.getLogger(MyWebMvcConfigurer.class);
/**
* 请求 url 中的资源映射,不推荐写死在代码中,最好提供可配置,如 /uploadFiles/**
*/
@Value("${uploadFile.resourceHandler}")
private String resourceHandler;
/**
* 上传文件保存的本地目录,使用@Value获取全局配置文件中配置的属性值,如 E:/wmx/uploadFiles/
*/
@Value("${uploadFile.location}")
private String location;
/**
* 如果上传文件保存的本地目录不存在,则创建,否则后期保存文件时,容易出现找不到路径的错误
*/
@PostConstruct
public void init() {
File file = new File(location);
if (!file.exists()) {
file.mkdirs();
logger.debug("服务器文件存在目录={},已经不存在,进行新建。", location);
} else {
logger.debug("服务器存储目录已经存在={}", location);
}
}
/**
* 配置静态资源映射
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//就是说 url 中出现 resourceHandler 匹配时,则映射到 location 中去,location 相当于虚拟路径
//映射本地文件时,开头必须是 file:/// 开头,表示协议
registry.addResourceHandler(resourceHandler).addResourceLocations("file:///" + location);
}
}
src/main/java/com/wmx/www/config/MyWebMvcConfigurer.java · 汪少棠/fileServer - Gitee.com
文件上传
1、Java JDK 1.8 + Spring Boot 2.3.5 新建 web 项目,spring boot 版本不同,可能略有差异。
2、spring boot 的 web 组件中的 org.springframework:spring-web 已经集成了文件上传功能,无需再导入以前的 commons-io、commons-fileupload 了,使用方式和之前的 commons-fileupload 完全一样。
一:pom.xml 内容如下,为了页面写起来方便,使用了 Thymeleaf 模板引擎,java web 应用,自然需要导入 web 组件。
<!-- thymeleaf 模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- web 组件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
二:application.yml 全局配置文件内容如下:
#配置服务器
server:
port: 8080 #服务器端口
servlet:
context-path: /fileServer #应用上下文路径
spring:
servlet:
multipart: #配置文件上传
max-file-size: 1000MB #设置上传的单个文件最大值,单位可以是 MB、KB,默认为 1MB
max-request-size: 1024MB #设置多文件上传时,单次内多个文件的总量的最大值,单位可以是 MB、KB,默认为 10 M
#文件上传的临时存储目录(1、默认为系统临时目录;2、目录不存在时会自动创建)
location: E:/wmx/uploadTemps/
uploadFile:
resourceHandler: /uploadFiles/** #请求 url 中的资源映射
location: E:/wmx/uploadFiles/ #自定义上传文件本地保存路径
src/main/resources/application.yml · 汪少棠/fileServer - Gitee.com
1、上传文件存放的目录,不应该写死在代码中,所以上面提供了在配置文件中配置 uploadFile.location 属性,将来类中使用 @Value 取值即可。
2、文件上传官网介绍:Handling Multipart File Uploads,除了配置上面的单个文件最大值已经单次上传总量以外,官网还提供了如下配置:
# MULTIPART (MultipartProperties)
spring.servlet.multipart.enabled=true # 是否启用上传支持.
spring.servlet.multipart.file-size-threshold=0B #将文件写入磁盘的阈值.
spring.servlet.multipart.location= # 上传文件的临时存储目录(默认为系统临时目录).
spring.servlet.multipart.max-file-size=1MB # 单个文件支持的最大大小.
spring.servlet.multipart.max-request-size=10MB # 单次请求时,全部文件的不能超过的总大小.
spring.servlet.multipart.resolve-lazily=false # 是否在访问文件或参数时延迟解析多部分请求.
三:文件上传后台控制台层代码如下:
/**
* 请求 url 中的资源映射,不推荐写死在代码中,最好提供可配置,如 /uploadFiles/**
*/
@Value("${uploadFile.resourceHandler}")
private String resourceHandler;
/**
* 上传文件保存的本地目录,使用 @Value获 取全局配置文件中配置的属性值,如 E:/wmx/uploadFiles/
*/
@Value("${uploadFile.location}")
private String uploadFileLocation;
/**
* http://localhost:8080/fileServer/uploadFile
* 文件上传,因为只是演示,所以使用 @ResponseBody 将结果直接返回给页面
*
* @param singleFile :上传的文件对象
* @param request :请求对象
* @return :返回的是文件名称,将来客户端可以用来下载
* @throws IOException
*/
@PostMapping("uploadFile")
@ResponseBody
public String uploadFile(MultipartFile singleFile, HttpServletRequest request) throws IOException {
if (singleFile == null || singleFile.isEmpty()) {
logger.debug("上传文件为空...");
return "上传文件为空...";
}
//basePath拼接完成后,形如:http://192.168.1.20:8080/fileServer
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
String fileName = singleFile.getOriginalFilename();
String fileServerPath = basePath + resourceHandler.substring(0, resourceHandler.lastIndexOf("/") + 1) + fileName;
logger.debug("文件访问路径:{}", fileServerPath);
File saveFile = new File(uploadFileLocation, fileName);
/**
* 文件保存,注意目的文件夹必须事先存在,否则保存时报错
* 在{@link MyWebMvcConfigurer}中已经处理了,如果不存在,自动新建存储目录
*/
singleFile.transferTo(saveFile);
logger.info("文件保存路径:{}", saveFile.getPath());
return "<a target='_blank' href='" + fileServerPath + "'>" + fileServerPath + "</a>";
}
src/main/java/com/wmx/www/controller/UploadFileController.java · 汪少棠/fileServer - Gitee.com
四:前端文件上传 form 表单如下:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>文件服务器</title>
</head>
<body>
<h3>单文件上传,成功后返回访问地址,可以查看并下载</h3>
<form method="post" target="_blank" action="/fileServer/uploadFile" enctype="multipart/form-data">
<input type="file" name="singleFile" style="font-size: 20px"/><br>
<input type="submit" id="singleFileUploadButton1" value="上传" style="font-size: 30px">
</form>
</body>
</html>
src/main/resources/templates/index.html · 汪少棠/fileServer - Gitee.com
spring.servlet.multipart.location 临时目录
1、Spring boot 文件上传时有时候会出现此错误(临时上传存储位置无效或者是不存在):Caused by: java.io.IOException: The temporary upload location [/tmp/tomcat.2877894020006618597.7030/work/Tomcat/localhost/ROOT] is not valid。Spring boot 默认会使用系统的临时目录,然而服务运行过程中,人为删除了临时目录,或者系统自动删除了临时目录(比如Linux系统有时会自动清理长时间未使用的临时目录),当服务再次使用时临时目录时就会报错。
2、解决办法:
方式1)使用默认临时目录时,可以重启项目,此时会自动重新生成临时存储路径 |
方式2)全局配置文件中配置(推荐方式):spring.servlet.multipart.location=上传文件的临时存储目录 2.1、当上传文件时,会先将文件上传到该目录下的临时文件夹中,成功后再移动到最终的目录中,这样可以保证在上传文件过程中出现异常时,临时文件被及时清理。 2.2、如果上传失败,临时文件会一直存在,直到被清理或重新上传。Spring提供了一个ScheduledExecutorService,用于在后台周期性地清理过期的临时文件。默认情况下,过期时间是1个小时。 2.3、亲测 Spring boot 2.3.5,当目录不存在时会自动创建,低版本可能不会自动创建此目录而报错:The temporary upload location [/xxx/xxx] is not valid |
方式3)手动注入 MultipartConfigElement,设置临时存储路径,覆盖全局yml文件的配置,代码如下: @Bean |
方式4:如果报错临时目录不存在了,直接登陆服务器给它手动创建,或者提供一个接口进行调用然后创建 createDirByPath。 |
3、MultipartConfigElement不仅可以手动注入用于覆盖配置,它本身默认也会自动注入到Spring容器中,可以直接获取对象,然后获取配置信息。或者运行期可以动态修改配置。
Web 网络文件下载(批量压缩)
1、单个文件下载,多个文件时压缩成一个文件下载。
/**
* 多个文件压缩成一个文件后下载.
* 1、本方法使用原生API进行压缩。
* 2、也可以使用第三方的解压库:
* * Apache Commons Compress 文件解压缩库:https://wangmaoxiong.blog.csdn.net/article/details/103943688#文件压缩并提供网络下载
*
* @param fileId :被下载的文件ID,多个时使用逗号分割,如 1,2,3,4
* @param response
* @throws Exception
*/
@GetMapping("/zipDownload1")
public void zipDownload1(String fileId, HttpServletResponse response) throws Exception {
// 要下载的文件列表
List<Path> listPath = this.listPath(fileId);
String zipFileName = listPath.get(0).getFileName() + "等" + listPath.size() + "个.zip";
// 设置发送到客户端的响应的内容类型
response.setContentType("application/zip");
// 设置返回的头信息(Content-Disposition),对文件名称进行编码,否则中文容易乱码。
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(zipFileName, "UTF-8"));
// 压缩多个文件到zip文件中,并且响应给客户端
try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
for (Path path : listPath) {
try (InputStream inputStream = Files.newInputStream(path)) {
zipOutputStream.putNextEntry(new ZipEntry(path.getFileName().toString()));
StreamUtils.copy(inputStream, zipOutputStream);
zipOutputStream.flush();
}
}
}
}
2、Post 请求文件下载在线演示源码:后端导出接口、POI Word工具类、前端页面
3、Easyexcel 上传、下载:https://wangmaoxiong.blog.csdn.net/article/details/106978811#Easyexcel 上传、下载。
4、web 下载 excel 文件:src/main/java/com/wmx/controller/ExcelController.java · 汪少棠/apache-poi-study - Gitee.com。
解决下载文件名称乱码
https://wangmaoxiong.blog.csdn.net/article/details/106978811#t5