文件上傳transferTo一行代碼的bug


本次的項目環境為 Running with Spring Boot v1.5.10.RELEASE, Spring v4.3.14.RELEASE, 服務器環境為CentOS7.0.

transferTo

我們在上傳文件的時候會用到transferTo 這個方法,

transferTopackage 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的源碼中不會根據你給的路徑自動創建圖片的上一層路徑。

寫這個就是要說明,遇到問題的時候要多讀源碼,一步步的調試。

當你一步步接近真相的時候,你會感覺到很有成就感,就像爬過一座山頭一樣。

多讀源碼 ,不要只是拷貝,很多場景拷貝的時候細節問題不一樣的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM