彻底跟路径错误say拜拜!
题图:from Google
1.问题描述
- 关键字: SpringMVC 4.2.4 、 Spring Boot 1.3.1 、Servlet 3.0 、文件上传
-
报错信息:
java.io.IOException: java.io.FileNotFoundException: /tmp/tomcat.273391201583741210.8080/work/Tomcat/localhost/ROOT/tmp/source/IMG_20160129_132623.jpg (No such file or directory)
- 问题源码: transferTo方法报错
// 前端传入mulFileSource
// 创建压缩前源文件
File fileSourcePath = new File("tmp/source/");
File fileSource = new File(fileSourcePath, mulFileSource.getOriginalFilename());
if (!fileSourcePath.exists()) {
fileSourcePath.mkdirs();
}
// 将接收得图片暂存到临时文件中
mulFileSource.transferTo(fileSource);
2.问题分析
- 首先,看源码中文件定义,相对路径,预期路径应该是
项目路径/tmp/source/
,但是报错确是一个系统临时文件路径(tomcat的)。 - 其次,由于是transferTo方法报错,因此应该是该方法写入文件时报错,因此,我们跟入方法源码。
public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
//中间代码省略
/**
* Spring MultipartFile adapter, wrapping a Servlet 3.0 Part object.
*/
@SuppressWarnings("serial")
private static class StandardMultipartFile implements MultipartFile, Serializable {
//中间代码省略
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
this.part.write(dest.getPath());
}
}
}
源码一目了然,使用Servlet3.0的支持的上传文件功能时,如果我们没有使用绝对路径的话,transferTo方法会在相对路径前添加一个location
路径,即:file = new File(location, fileName);
。当然,这也影响了SpringMVC的Multipartfile的使用。
package org.apache.catalina.core;
/**
* Adaptor to allow {@link FileItem} objects generated by the package renamed
* commons-upload to be used by the Servlet 3.0 upload API that expects
* {@link Part}s.
*/
public class ApplicationPart implements Part {
//中间代码省略
@Override
public void write(String fileName) throws IOException {
File file = new File(fileName);
if (!file.isAbsolute()) {
file = new File(location, fileName);
}
try {
fileItem.write(file);
} catch (Exception e) {
throw new IOException(e);
}
}
}
3.问题解决方案
由于我们创建的File在项目路径/tmp/source/
,而transferTo方法预期写入的文件路径为/tmp/tomcat.273391201583741210.8080/work/Tomcat/localhost/ROOT/tmp/source/
,我们并没有创建该目录,因此会抛出异常。
- 使用绝对路径
-
修改
location
的值
这个location
可以理解为临时文件目录,我们可以通过配置location
的值,使其指向我们的项目路径,这样就解决了我们遇到的问题。
在Spring Boot下配置location
,可以在main()
方法所在文件中添加如下代码:
/**
* 文件上传临时路径
*/
@Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setLocation("/app/pttms/tmp");
return factory.createMultipartConfig();
}