本次的項目環境為 Running with Spring Boot v1.5.10.RELEASE, Spring v4.3.14.RELEASE, 服務器環境為CentOS7.0.
transferTo
我們在上傳文件的時候會用到transferTo 這個方法,
transferTo
是 package org.springframework.web.multipart;
幫我們已經封裝好,所以我們上傳文件的時候是很方便的,只需要一行代碼。
Spring 大哥真的是強大。
今天我們上傳的時候老是報錯,錯誤提示
java.io.IOException: java.io.FileNotFoundException: /opt/xxxxxxx/xxx.png (Is a directory)

蹩腳的英文好好的翻譯一下,xxx.png 是一個目錄。
對的,看到這里大家大概就明吧了。
String path = FILE_PATH + PATH_NAME + File.separator + dirName;
String fileName = file.getOriginalFilename();
======>注意這里等下要講的
File targetFile = new File(path , fileName);
if (!targetFile.exists()) {
targetFile.mkdirs();
}
boolean upState = false;
try {
file.transferTo(targetFile);
upState = true;
} catch (IOException e) {
log.error("file transferTo e", e);
e.printStackTrace();
}
分析下
其實這個這里上傳文件只是一個方法,用到了 Spring
自己封裝的MultipartFile
類,上傳只是用到了transferTo,參數是文件的路徑。
好了,來一步步調試看源碼。
- 第一步看transferTo中的源碼
public void transferTo(File dest) throws IOException, IllegalStateException {
======>注意這里調用的是part 類中的write 方法,參數是File的路徑
this.part.write(dest.getPath());
if (dest.isAbsolute() && !dest.exists()) {
FileCopyUtils.copy(this.part.getInputStream(), new FileOutputStream(dest));
}
}
- 第二步看 ApplicationPart 中的write方法
public void write(String fileName) throws IOException {
File file = new File(fileName);
if (!file.isAbsolute()) {
file = new File(this.location, fileName);
}
try {
this.fileItem.write(file);
} catch (Exception var4) {
throw new IOException(var4);
}
}
大家注意看第二步中的 if (!file.isAbsolute())
,是的問題就在這里。
這時候如果!file.isAbsolute()成立,也就是我們沒有使用絕對路徑,那么file = new File(location,fileName);
這個時候會創建一個新的路徑,看下面的代碼
public File(File parent, String child) {
if (child == null) {
throw new NullPointerException();
}
if (parent != null) {
if (parent.path.equals("")) {
this.path = fs.resolve(fs.getDefaultParent(),
fs.normalize(child));
} else {
this.path = fs.resolve(parent.path,
fs.normalize(child));
}
} else {
this.path = fs.normalize(child);
}
this.prefixLength = fs.prefixLength(this.path);
}
看到這里應該了解了/opt/xxxxxxx/xxx.png
,這個圖片被當成文件夾了,在這里創建了一個文件夾,而不是圖片。
解決方法
- 代碼注入,修改源碼中的location
@Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setLocation(你的上傳路徑path);
return factory.createMultipartConfig();
}
- 文件上傳時候創建文件
String path = FILE_PATH + PATH_NAME + File.separator + dirName;
String fileName = file.getOriginalFilename();
======> 注意要先創建圖片的目錄mkdirs
File sourceFile = new File(path);
if (!sourceFile.exists()) {
sourceFile.mkdirs();
}
File targetFile = new File(path + File.separator + fileName);
boolean upState = false;
try {
file.transferTo(targetFile);
upState = true;
} catch (IOException e) {
log.error("file transferTo e", e);
e.printStackTrace();
}
結論
這里其實就是 new File(path + File.separator + fileName)
的問題,因為Spring的源碼中不會根據你給的路徑自動創建圖片的上一層路徑。
寫這個就是要說明,遇到問題的時候要多讀源碼,一步步的調試。
當你一步步接近真相的時候,你會感覺到很有成就感,就像爬過一座山頭一樣。
多讀源碼
,不要只是拷貝,很多場景拷貝的時候細節問題不一樣的。