SpringMVC檔案上傳下載(單檔案、多檔案)

bigsai發表於2020-07-30

前言

大家好,我是bigsai,今天我們學習Springmvc的檔案上傳下載。

檔案上傳和下載是網際網路web應用非常重要的組成部分,它是資訊互動傳輸的重要渠道之一。你可能經常在網頁上傳下載檔案,你可能也曾沉浸於網際網路技術的神祕,而本篇就為你解開它神祕的面紗。

本文已收錄在公眾號:bigsai中(微信搜尋bigsai),,同時也收錄在博學谷中(以關卡方式展開歡迎體驗),更多好玩精彩等待給您分享,歡迎關注!

案例分析

你肯定會問:通過本篇可能能夠學到什麼?

那我很負責任的告訴你,通過本篇文章,你能夠掌握Springmvc檔案上傳(單檔案、多檔案)檔案下載知識和內容的使用,並能夠根據這些實現一些基本的案例。

核心思路拆解

你可能會問:,這麼一個完整的專案是如何分工執行?

不急不急,我來告訴你,其實這麼一個檔案上傳下載的專案,它是一個b-s結構的web專案,涉及到前端和服務端,從巨集觀來看它是這樣的一個結構:

在這裡插入圖片描述

但是從檔案上傳、下載兩個功能來看它們之間又是有所區別的,檔案上傳的主要核心是使用者上傳的檔案服務端接受儲存

在這裡插入圖片描述

而檔案下載更重要的部分是使用者請求之後服務端給使用者返回二進位制檔案

在這裡插入圖片描述

所以檔案上傳和檔案下載的專案大體結構相似,只是各個部分在具體實現上有差別,我們需要更多關注下檔案上傳和下載服務端的實現和區別。

案例所涉及知識點

在本案例中,用到了以下知識點:

html頁面form表單:

在前端無論是html還是jsp等模板引擎編寫上傳的頁面時候。<form> 標籤就意為一個(檔案)上傳的表單。

  • 表單能夠包含若干 input 標籤,而input標籤又有不同型別比如文字欄位、核取方塊、單選框、檔案等等。
  • 我們通常使用表單編寫若干標籤代表我們想要向服務端傳送的資料,然後通過標籤的按鈕將資料請求提交至服務端。
  • 表單的method表示請求的型別(一般為post),action表示需要請求的url地址,enctype表示傳輸資料型別。

Springmvc:

案例的檔案上傳和下載基於Springmvc,而我們在Springboot專案中整合Springmvc。

  • 本案例使用Springmvc作為專案mvc架構的框架,將模型(Model),檢視(View),控制器(Controller)分離降低專案的耦合性。
  • 本案例使用Springmvc的MultipartFile介面和ResponseEntity介面實現檔案上傳和下載。

建立Springmvc專案

Springmvc為一個mvc架構的web框架,建立Springmvc專案的方式有很多,你可以選擇直接通過IDEA建立Springmvc專案,也可以通過Maven方式建立web專案然後新增Springmvc的依賴,但這兩種方式有太多的配置還需要配置tomcat,在效果一致的情況下我們們儘量簡化一些開發配置類的工作,所以不採用以上兩種方式建立專案。

而Springboot簡化了Spring專案的開發,開箱即用,且內嵌tomcat,所以我們們選擇建立基於Springboot且整合Springmvc的專案方便快捷,更能直奔主題進行操作。

專案建立

首先,開啟IDEA,建立專案,選擇Spring Initializr型別初始化點選next。
在這裡插入圖片描述
然後你會得到一個選擇專案名和一些配置的頁面,我們在Group中填寫com,而Artifact我們們填寫fileupload。點選next。
在這裡插入圖片描述
接著在選擇對應模組依賴的時候,選擇Spring web 模組,此模組就是包含Springmvc的web模組
在這裡插入圖片描述
接著選擇需要建立專案的地址目錄,點選next在這裡插入圖片描述

這樣你就可以得到一個完整的包含web模組(Springmvc)的Springboot專案,就可以在裡面編寫我們們專案的程式碼。
在這裡插入圖片描述

目錄介紹

上面建立完的基於Springboot的Springmvc專案,預設有若干檔案和資料夾,不同檔案和資料夾有著不同的職責:

  • java:用來編寫java服務端相關程式碼,例如Controller,Dao,Service等。
  • application.properties: 編寫一些專案和框架的配置內容以及和第三方框架整合配置等
  • static: 靜態資源目錄,用來存放html、JavaScript、圖片等資源。
  • teamplates:用來編寫Thymeleaf等模板引擎,這裡不使用
  • pom.xml:編寫maven專案jar包資源依賴。如果專案需要引入其他依賴或者修改打包方式可以進行修改。

對於web專案的檔案上傳,需要進行一定配置以滿足我們的使用需求,我們在application.propertis進行以下配置:

# 允許專案中檔案上傳
spring.servlet.multipart.enabled=true
# 上傳檔案的臨時目錄 (一般情況下不用特意修改)
#spring.servlet.multipart.location=
# 上傳檔案最大為 1M (預設值 1M 根據自身業務自行控制即可)
spring.servlet.multipart.max-file-size=104857600
# 上傳請求最大為 10M(預設值10M 根據自身業務自行控制即可)
spring.servlet.multipart.max-request-size=104857600
# 檔案大小閾值,當大於這個閾值時將寫入到磁碟,否則存在記憶體中,(預設值0 一般情況下不用特意修改)
spring.servlet.multipart.file-size-threshold=0
# 判斷是否要延遲解析檔案(相當於懶載入,一般情況下不用特意修改)
spring.servlet.multipart.resolve-lazily=false

當然,你對檔案有大小等其他要求可以對配置進行自行更改。到這裡帶有Springmvc環境的專案已經建立完成啦,剩下的只需要編寫前端、服務端程式碼執行測試即可。

單檔案上傳

下面請跟我實戰 Springmvc單檔案上傳。一個完整的檔案上傳專案有兩部分組成:前端介面和服務端程式。

前端設計

對於前端頁面,我們使用你一定熟悉的html而不選用其他模板引擎。而form表單是html檔案上傳的核心元件,你在使用前需要了解它的一些屬性。

表單的enctype屬性
上面說了一個表單檔案傳輸的大體流程,你也知道表單有個至關重要的屬性:enctype。而entype值通常有以下三種:

  • application/x-www-form-urlencoded:預設編碼方式,在傳送前編碼所有字元(預設)使用url編碼方式,和get請求有些相似。但這種方式如果傳送大量二進位制資料效率會比較低。
  • multipart/form-data:不對字元編碼。在使用包含檔案上傳控制元件的表單時,必須使用該值。通常用來向服務端傳送二進位制資料,而我們的檔案也主要以二進位制的方式進行傳輸。
  • text/plain:空格轉換為 "+" 加號,但不對特殊字元編碼。

所以本單檔案上傳案例中,需要注意以下事項:

  • 表單的enctype要為multipart/form-data型別,表示二進位制傳輸。
  • 在一個form表單內定義一個input為file屬性的標籤,代表檔案上傳。
  • form表單的method需要為post。
  • enctype要為multipart/form-data型別,表示二進位制傳輸。

前端頁面的規則瞭解之後你在static下建立一個index1.html檔案,裡面具體的程式碼內容為:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>單檔案上傳</title>
</head>
<body>
<h2>單檔案上傳</h2>
<form  action="onfile" method="post" enctype='multipart/form-data'>
    <input type="file" name="file" ><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

其中action="onfile"代表的為請求地址為onfile,這裡都在專案內所以用相對地址即可,如果上傳為其他介面也可填寫對應的絕對地址。這樣前端頁面就編寫完成,我們還需要編寫檔案上傳對應服務端模組。

服務端設計

服務端主要負責檔案接受,在前端看起來實現檔案上傳的頁面很簡單,但實際上在服務端的檔案接收並沒有那麼容易,因為傳過來的不光光是這一個(或多個)二進位制檔案,還附帶一些頭資訊、檔名等等資料。打包過來的資料如果是文字資料解析可能還好,但是二進位制檔案資料一旦出現一點錯誤可能得到的整個檔案都是損壞的。並且在我們們java web技術棧中檔案上傳也是有一定發展的歷史的:

servlet檔案上傳(3.0以前)
在servlet3.0以前,檔案上傳在服務端接收需要使用request.getInputStream()獲取表單的二進位制資料,但是在解析時候非常麻煩和複雜,對於檔案上傳這麼一個很基本的模組在接收的時候可能要耗費很大的成本和精力去解決它,並且很多初級攻城獅很可能由於對io模組陌生無法實現上傳檔案在服務端的接收。
在這裡插入圖片描述

所以這個時候一些具有責任感的公司、組織就把它們的解析方法貢獻出來供大家使用,大家不需瞭解傳輸檔案底層內容,這些開源的處理方式中,最流行的當屬apache旗下開源的commons-fileuploadcommons-io,把兩個jar包加入到專案中你直接瞭解下這個api如何使用即可。有了這兩個jar包,簡單學習它的api,你就可以在普通的web專案中很容易的實現上傳檔案的功能!
在這裡插入圖片描述
servlet3.0以後
隨著servlet版本更新,設計者可能看到javaweb開發中原生api對檔案上傳支援不太友好的問題,所以在api對檔案上傳的支援得到優化,簡化了Java Web的開發。在servlet3.0中主要增加Part這個類用來讀取檔案資料和資訊,在Part中直接將傳輸檔案的名稱、頭資訊、二進位制檔案分割開,通過簡單的api就可以實現檔案上傳的功能。不需要再新增外部jar包

Springmvc檔案上傳
檔案上傳和下載是web開發常用模組,而Springmvc作為一款優秀的web框架,對很多模組和內容進行更高度的封裝和整合,而這麼常用的檔案上傳肯定是少不了的,所以Springmvc的檔案上傳基於apache旗下開源的commons-fileuploadcommons-io包。將其進行二次整合和封裝至Springmvc,將方法和內容封裝至MultipartFile介面讓我們使用起來更加方便,能夠容易實現單檔案、多檔案上傳。

對於上述各種檔案上傳服務端實現方式,大致可以通過下圖展示:
在這裡插入圖片描述
通過上圖你就可明白Springmvc檔案上傳實現的原理,那麼下面你就可以進行大顯身手啦!Springmvc處理上傳檔案很簡單,我們需要在java目錄下建立一個uploadController.java建立這麼一個控制器,在上面加上@Controller註解。在Controller中編寫以下程式碼:

 @PostMapping("onfile")
 @ResponseBody
 public String onfile(MultipartFile file) throws IOException {
     File file1 =new File("F:/fileupload/"+file.getOriginalFilename());//建立file物件
     if(!file1.exists())
            file1.createNewFile();//在磁碟建立該檔案
     file.transferTo(file1);//將接受的檔案儲存
     return "sucucess";
 }

其中:

  • @PostMapping("onfile") 的意思為該請求方式為post,且請求的url在專案中的相對地址為onfile
  • @ResponseBody指不返回web頁面,而是返回字串或json字串,在這裡我們直接用一個成功單詞代表跳轉後的介面。
  • public String onfile(MultipartFile file) 函式名不重複就行,而MultipartFile file就是Springmvc封裝的一個處理檔案的介面,其中引數名(這裡是file)要和前端介面檔名相同(input type="file",name="file"中的name),通過這個介面你可以更容易的對檔案進行各種操作,而本案例就是將上傳的檔案儲存到本地F盤。

對於函式中的幾行核心程式碼各司其職,除了註釋的解釋外,大致的流程可以參考如下圖:
在這裡插入圖片描述

執行測試

這樣啟動專案,在瀏覽器輸入http://localhost:8080/index1.html,選擇檔案上傳,點選上傳之後就可以在本地看到上傳的檔案啦。
在這裡插入圖片描述
至此,單檔案上傳就完成啦,單檔案上傳前端需要注意的就是form表單的method型別以及 enctype引數,而服務端也只需要用MultipartFile 介面就可以很容易的對檔案進行接受。

第四關 多檔案上傳

上面講的是單檔案上傳,很多時候你可能遇到的需求不光光是單檔案上傳。就比如你一定熟悉這個頁面:
在這裡插入圖片描述

如上你可以看到,這麼一次檔案上傳不止一個圖片,並且數量也不確定,但都屬於同一名稱和集合的內容。這就是多檔案上傳。對於這種情況無論在前端還是服務端也是很容易處理的。

前端設計

我們這裡實現一個多張圖片的上傳,首先在static目錄下建立一個index2.html的頁面。裡面的具體內容為:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>多檔案上傳</title>
</head>
<body>
<h2>同一類別多個檔案上傳</h2>
<form name="onfile"  action="onfiles2" method="post" enctype="multipart/form-data">
    圖片:
    <input type="file" name="img"><br>
    <input type="file" name="img"><br>
    <input type="file" name="img"><br>
    <input type="file" name="img"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

這樣前端頁面就編寫完成,其中action要改為onfiles2,也就是待會要在服務端編寫的介面。還有注意的這些input 所有type為file代指型別為檔案,而name均為img意思是上傳一組名稱為img圖片的集合。

服務端設計

而在我們服務端,其實用MultipartFile[]陣列就可以對這樣的多檔案進行接收,我們在controller中編寫以下程式碼:

@PostMapping("onfiles2")
@ResponseBody
public String onfiles2(MultipartFile img[]) throws IOException {
   for(int i=0;i<img.length;i++)
   {
       if(!img[i].isEmpty())//檔案不空
       {
           File imgfile =new File("F:/fileupload/"+img[i].getOriginalFilename());
           imgfile.createNewFile();
           img[i].transferTo(imgfile);
           logger.info(img[i].getOriginalFilename());
       }
   }
    return "sucucess";
}

這個處理方式和前面的很相似,只不過是需要遍歷MultipartFile[]對每個檔案進行接收處理,當然檔案為空的時候不進行處理。

執行測試

這樣開啟瀏覽器輸入:http://localhost:8080/index2.html,上傳檔案測試效果:
在這裡插入圖片描述

這樣一組類似相簿上傳的功能就完成啦,當然實際開發中的檔案上傳的要求肯定比這個要求嚴格很多,可能對檔案的格式、大小都有一定的要求,這就要求你在前端和服務端都要對檔案的字尾名、大小等資訊進行校驗,以達到自己場景化的需求。

檔案下載

檔案下載估計你在日常生活中會經常遇到,而你下載的其實就是服務端(伺服器)的資源,對於檔案型別有多種多樣的,瀏覽器也能夠識別很多種資源,事實上你現在訪問的這個網頁也是服務端的html檔案、圖片檔案等資源,只不過這些資源瀏覽器能夠顯示而不會儲存到本地。

直接訪問資源VS下載資源

如果直接訪問的資源是瀏覽器所不能識別解析的,例如doc、zip等型別檔案,那訪問的時候會預設下載到本地。而當你在Springmvc中使用下載功能時,無論是什麼資源都以下載的形式返回給客戶端。這種區別可以參考下圖:
在這裡插入圖片描述

在檔案下載方面的實現,servlet本身也是實現檔案下載的,不過使用起來有點繁瑣。其原理就是往HttpServletResponse response 的輸出流寫位元組內容。而我們Springmvc對檔案下載也做了封裝,將下載功能封裝至ResponseEntity類中,我們在使用的時候也很方便。下面就來實戰檔案下載的功能。

首先,我們在F盤建立download資料夾,在裡面新增對應檔案,這個資料夾我們作為服務端的資源。
在這裡插入圖片描述

前端設計

我們在建立一個檔案下載的前端頁面,在static目錄下建立index3.html,頁面的具體內容為:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Springmvc檔案下載</title>
</head>
<body>
<h2>Springmvc檔案下載</h2>
個人照片<a href="/download/個人照片.png">個人照片.png</a><br>
個人簡歷<a href="/download/個人簡歷.pdf">個人簡歷.pdf</a>
</body>
</html>

其中href是下載的超連結,download是下載的介面名,而連結最後面部分則是下載資源名稱。

服務端設計

檔案下載的原理就是服務端向客戶端返回二進位制流和資訊,而Springmvc通過ResponseEntity完成。我們在controller中編寫以下介面實現下載的功能:

@GetMapping("download/{filename}")
public ResponseEntity<byte[]>download(@PathVariable String filename) throws IOException {
    //下載檔案的路徑(這裡絕對路徑)
    String filepath= "F:/download/"+filename;
    File file =new File(filepath);
    //建立位元組輸入流,這裡不實用Buffer類
    InputStream in = new FileInputStream(file);
    //available:獲取輸入流所讀取的檔案的最大位元組數
    byte[] body = new byte[in.available()];
    //把位元組讀取到陣列中
    in.read(body);
    //設定請求頭
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add("Content-Disposition", "attchement;filename=" + file.getName());
    //設定響應狀態
    HttpStatus statusCode = HttpStatus.OK;
    in.close();
    ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(body, headers, statusCode);
    return entity;//返回
}

這樣就是實現了檔案下載功能,如果用傳統servlet的方式下載檔案可能需要在HttpServletResponse response中設定各種資訊,而使用Springmvc的ResponseEntity只需要將檔案二進位制主體、頭資訊以及狀態碼設定好即可進行檔案下載,在易用性和簡潔上更勝一籌。

執行測試

開啟瀏覽器輸入:http://localhost:8080/index3.html;點選需要下載的檔案,就實現了檔案下載的功能,執行情況圖如下:
在這裡插入圖片描述

此時你就遇到了一個檔案下載非常常見的問題:中文檔名錯誤顯示。這個解決方案也很容易解決,只需將Content-Disposition內容後面的檔名進行url編碼即可,具體程式碼為(替換上面對於部分):

headers.add("Content-Disposition", "attchement;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));

這樣重啟程式,重新整理頁面再次點選下載的連結,你就會發現檔案被成功的下載了:
在這裡插入圖片描述

總結與擴充

至此,Springmvc的單檔案上傳、多檔案上傳以及檔案下載你已經全部掌握了,是不是滿滿的成就感想去實現一個自己的小網站並把相關內容放進去?不過Springmvc檔案上傳下載雖然簡單,但你依然需要掌握其原理,學好java中的io檔案傳輸,這樣在各種場景的檔案傳輸任務中方能勝任。

總結

前面所講檔案上傳,前端就是form表單用<input type="file">表示客戶端要上傳檔案,而服務端主要使用MultipartFile或者MultipartFile[]分別接收單個檔案和多個檔案。而在儲存到本地也僅僅需要在本地磁碟建立對應檔案然後MultipartFile呼叫transferTo()方法即可將上傳的檔案儲存。

而檔案下載的前端需要一個請求的url連結,服務端需要編寫這個連結對應的介面。通過一些名稱找到檔案在本地真實的位置通過ResponseEntity即可將二進位制檔案返回給客戶達到檔案下載的功能。而ResponseEntity使用也很簡單在建立時候只需要傳入二進位制主體、頭和狀態碼即可成功返回,而這些Springmvc已進行了很好封裝你可以直接使用。

而無論是檔案上傳、多檔案上傳還是檔案下載,一個完整的案例大致都需要這樣一個過程:

  • 構思需求和頁面大體樣式
  • 編寫前端html頁面
  • 編寫服務端響應的請求
  • 啟動程式執行測試

在其中過程如果有問題可以根據編譯器的錯誤提示、執行時的錯誤日誌找到根源進行修正,這樣完整的案例就可以成功完成啦!

案例擴充

你是否覺得自己掌握的可以了?那好,我們們擴充提升一下,我給你來一個需求:單檔案和多檔案混合上傳

假設小明需要實現一個檔案上傳功能,小明需要上傳一份簡歷和若干份照片(小於3)。這個專案該如何設計呢?它的計劃頁面可能是這樣的:

在這裡插入圖片描述

我覺得聰明的你一定不會被難住,對於前端介面的html有什麼想法呢?

對於種類來說有簡歷和照片兩種檔案,對於它們各自來說,簡歷只有一份,而照片可能有多份。

那麼我們們的html頁面可以這樣設計:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>個人資訊上傳</title>
</head>
<body>
<h2>個人資訊上傳</h2>
<form name="onfile"  action="infoupload" method="post" enctype="multipart/form-data">
    姓名:<input type="text" name="name" ><br>
    年齡:<input type="text" name="age"> <br>
    圖片:<input type="file" name="img">
         <input type="file" name="img">
    簡歷:<input type="file" name="resume"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

這裡面和前面的單檔案上傳不同的是有多個input標籤,此外action也要改成infoupload意思是你需要寫這麼一個介面來處理這個檔案上傳的內容。在controller中編寫以下程式碼:

 private  static Logger logger= LoggerFactory.getLogger(uploadController.class);
    @PostMapping("infoupload")
    @ResponseBody
    public String onfile(String name,String age, MultipartFile img[],MultipartFile resume) throws IOException {
        logger.info(name);//日誌中列印傳輸的name
        logger.info(age);
        //接收img[]
        for(int i=0;i<img.length;i++)
        {
            if(!img[i].isEmpty())//檔案不空
            {
                File imgfile =new File("F:/fileupload/"+img[i].getOriginalFilename());
                imgfile.createNewFile();
                img[i].transferTo(imgfile);
            }
        }
         //接收resume
        File resumefile =new File("F:/fileupload/"+resume.getOriginalFilename());
        //在磁碟中建立檔案,此時檔案存在但沒有內容
        resumefile.createNewFile();
        //將接受的檔案複製到建立的檔案中
        resume.transferTo(resumefile);
        return "sucucess";
    }

這個理解起來其實也很容易,這個和上面主要的區別就是函式中的多引數,其實每一個引數都是要和前端頁面的form表單input標籤的內容對應(名稱一致)。form表單中的file型別在Springmvc的controller中就是對應MultipartFile型別,form表單中的text型別對應controller中的String型別。如果上傳單個檔案,在服務端就用MultipartFile型別引數接收,如果多檔案就用MultipartFile[]進行接收。上傳型別和個數根據你自己的需求設計定義。

我們啟動程式開啟瀏覽器輸入http://localhost:8080/index4.html選擇檔案進行上傳,然後在本地你可以看到檔案成功被儲存。

在這裡插入圖片描述

至此,本篇的內容就結束了,本文主要簡單講解了Springmvc中檔案上傳、多檔案上傳、檔案下載的實現,現在你已熟練掌握。青山不改,綠水長流,我們下期再見!下課!

博主微信公眾號:bigsai 一個等你關注而朝思夜暮的程式猿。
在這裡插入圖片描述

相關文章