SpringBoot: 淺談文件上傳和訪問的坑 (MultiPartFile)


本次的項目環境為 SpringBoot 2.0.4, JDK8.0. 服務器環境為CentOS7.0, Nginx的忘了版本.


前言


SpringBoot使用MultiPartFile接收來自表單的file文件,然后進行服務器的上傳是一個項目最基本的需求,我以前的項目都是基於SpringMVC框架搭建的,所以在使用SpringBoot的時候進行MultiPartFile上傳遇到了坑,這里說一下,其中主要包含兩個坑點.

  • 使用transferTo()方法寫入File時找不到文件路徑.

  • 訪問文件時Nginx的403 forbidden問題.


使用transferTo()方法寫入File時找不到文件路徑


在我們解決問題之前,我們先看一下封裝的上傳方法以及報錯日志.

    public static final String BASE_PATH = "/test/";
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> upload(MultipartFile imageFile) {

    <span class="hljs-keyword">if</span> (imageFile.isEmpty()) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }
    <span class="hljs-built_in">String</span> filename = imageFile.getOriginalFilename();
    
    <span class="hljs-built_in">String</span> ext= <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">if</span>(filename.contains(<span class="hljs-string">"."</span>)){
        ext = filename.substring(filename.lastIndexOf(<span class="hljs-string">"."</span>));
    }<span class="hljs-keyword">else</span>{
        ext = <span class="hljs-string">""</span>;
    }
    
    <span class="hljs-built_in">String</span> uuid =  UUID.randomUUID().toString().replaceAll(<span class="hljs-string">"-"</span>, <span class="hljs-string">""</span>);
    <span class="hljs-built_in">String</span> nfileName = uuid + ext;
    <span class="hljs-built_in">String</span> dirPath = DateFormatUtils.format(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(), <span class="hljs-string">"yyyyMMdd"</span>);
    <span class="hljs-built_in">String</span> filepath = BASE_PATH.endsWith(<span class="hljs-string">"/"</span>) ? BASE_PATH+dirPath : BASE_PATH+<span class="hljs-string">"/"</span>+dirPath;
    File targetFile = <span class="hljs-keyword">new</span> File(filepath, nfileName);
    <span class="hljs-keyword">if</span> (!targetFile.exists()) {
        targetFile.mkdirs();
    } <span class="hljs-keyword">else</span> {
        targetFile.delete();
    }
    <span class="hljs-keyword">try</span> {
        imageFile.transferTo(targetFile);
    } <span class="hljs-keyword">catch</span> (IllegalStateException e) {
        e.printStackTrace();
    } <span class="hljs-keyword">catch</span> (IOException e) {
        e.printStackTrace();
    }

    <span class="hljs-built_in">String</span> accessUrl =  <span class="hljs-string">"/"</span>+nfileName;
    logger.debug(<span class="hljs-string">"上傳文件成功 URL:"</span> + nfileName);
    <span class="hljs-keyword">return</span> accessUrl;
}

報錯日志如下所示.

java.io.IOException: java.io.FileNotFoundException: /test/20181025/be3676dffca94c6dac5e96a1a41dcd97.jpg (Is a directory)
    at org.apache.catalina.core.ApplicationPart.write(ApplicationPart.java:122)
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.transferTo(StandardMultipartHttpServletRequest.java:255)
    at com.dong.runline.common.utils.UploadUtils.upload(UploadUtils.java:56)
    at com.dong.runline.controller.TimeLineController.createTimeLineAction(TimeLineController.java:55)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

這里就出現了java.io.FileNotFoundException錯誤,這是怎么造成的呢?,通過Debug下的斷點我們發現是下面的位置發生了錯誤.

這里我用的本地環境進行了測試,發現本地創建了這個樣的一個路徑,最后的圖片被創建成一個這樣的路徑.

在SpringMVC環境下並沒有這樣的問題,在SpringBoot卻出現了這樣的問題,那么到底怎么造成的呢?網上的很多博客寫到,通過查詢transferTo()方法源碼找到了問題關鍵所在.

@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
    this.part.write(dest.getPath());
}

我們接着進入write()方法.

    @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);
        }
    }

這時候我們看到如果!file.isAbsolute()成立,也就是我們沒有使用絕對路徑,那么file = new File(location,fileName);會在原來的基礎上加上location路徑.這就是原因所在,解決起來也很方便,網上總共有兩種方案.

  • 使用絕對路徑
  • 修改location的值

第一種方案我們就不過多解釋了,我們看一下如何修改location的值.我們只需要在啟動類中注入如下Bean即可.把路徑指向我們的存儲路徑.

    @Bean
    MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        factory.setLocation(UploadUtils.BASE_PATH);
        return factory.createMultipartConfig();
    }

然后我們再修改下UploadUtils中的創建File方法即可.

        String uuid =  UUID.randomUUID().toString().replaceAll("-", "");
        String nfileName = uuid + ext;
        File targetFile = new File(nfileName);
        if (!targetFile.exists()) {
            targetFile.mkdirs();
        } else {
            targetFile.delete();
        }


訪問文件時Nginx的403 forbidden問題


所謂的Nginx的403錯誤其實就是訪問權限錯誤.當前用戶沒有訪問該資源的權限,這樣理解的話,我們就有兩種方案可行.一,降低文件訪問的權限等級.二,升高用戶的訪問權限.下面我們一個一個來看一下如何進行實現.

  • 降低文件訪問的權限等級

降低文件的訪問權限,我們只需要用到 chmod指令即可.這里簡單解釋一下chmod指令.

在Mac的使用過程中我們也經常會修改 某個文件的權限,例如:

chmod 777 file

如果如上設置的話,那么任何一個用戶都會對這個file文件擁有全部權限.

那么為什么是三位數呢?這是因為這三位數分別代表着檔案擁有者User、群組Group、其他Other三者的權限.也就是說擁有者的權限等級為7,群組的權限等級為7,其他權限等級也為7.

那么為什么是7呢?這是因為一個linux文件總共有三種權限,分別是讀r,寫w,操作x.對應的值分別是4,2,1.當一個用戶對某個文件擁有7的數值時,這時候為4+2+1,也就是說他擁有該文件全部的權限.

上面說了 chmod指令的如何使用,那么接下來我們就可以對服務器的文件使用chmod 664 file指令,然后降低文件訪問的權限等級.使全部用戶都擁有文件的訪問權限.但是問題來了,難道用戶上傳一次,我們就需要手動修改一次文件的權限,這顯然是不正確的,那么我們該怎么辦呢?這時候我們就需要提高用戶的訪問權限了.

  • 升高用戶的訪問權限

提高用戶的訪問權限,這里其實是修改Nginx的啟動者,我們把啟動者設置為最多權限者,那么我們就可以訪問到文件了.

首先我們先看是誰啟動了Nginx需要用到如下的指令.

 ps aux | grep "nginx: worker process" | awk '{print $1}'

這里我已經做了修改,截圖如下所示.

本來要是不對Nginx的配置進行任何設置訪問的話,那么第一個root應該為nobody,也就是Nginx的啟動者.先前已經使用** ls -l file **指令查詢了文件的權限情況,root 擁有讀寫權限,other沒有任何權限.所以我們要把啟動者改為root即可.

打開Nginx配置文件所在的位置,{nginx}表示你的nginx安裝路徑.

vi {nginx}/conf/nginx.conf

添加啟動者,如下所示.

user root

返回到sbin目錄中,准備檢測配置文件和重新啟動Ngnix.

cd ../sbin/

檢測配置文件的正確性

./nginx -t

檢測沒有任何問題,重新啟動

./nginx -s reload

這時候即可正常訪問到文件了.


結語


這篇博客算是日常的問題收集吧,整理一下.沒什么可多說的,就是搞事.歡迎關注騷棟



      </div>

原文地址:https://www.jianshu.com/p/d8666f2e698f


免責聲明!

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



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