大家好,我是bigsai,今天我們學習SpringMVC的文件上傳下載。
文件上傳和下載是互聯網web應用非常重要的組成部分,它是信息交互傳輸的重要渠道之一。你可能經常在網頁上傳下載文件,你可能也曾沉浸於互聯網技術的神秘,而本篇就為你解開它神秘的面紗。
您的關注、點贊、轉發,是我創作努力源源不斷的動力!
本文為筆者原創,已收錄在公眾號:bigsai 頭條:碼農bigsai 和博學谷中 同時同步,歡迎關注!
你肯定會問:通過本篇可能能夠學到什麼?
那我很負責任的告訴你,通過本篇文章,你能夠掌握SpringMVC文件上傳(單文件、多文件)文件下載知識和內容的使用,並能夠根據這些實現一些基本的案例。
你可能會問:,這麼一個完整的項目是如何分工運行?
不急不急,我來告訴你,其實這麼一個文件上傳下載的項目,它是一個b-s結構的web項目,涉及到前端和服務端,從宏觀來看它是這樣的一個結構:
但是從文件上傳、下載兩個功能來看它們之間又是有所區別的,文件上傳的主要核心是用戶上傳的文件
服務端接收存儲:而文件下載更重要的部分是用戶請求之後服務端給用戶返回二進制文件:
所以文件上傳和文件下載的項目大體結構相似,只是各個部分在具體實現上有差別,我們需要更多關注下文件上傳和下載服務端的實現和區別。
在本案例中,用到了以下知識點:
html頁面form表單:
在前端無論是html還是jsp等模板引擎編寫上傳的頁面時候。
- 表單能夠包含若干 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進行以下配置:
<code># 允許項目中文件上傳 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/<code>
當然,你對文件有大小等其他要求可以對配置進行自行更改。到這裡帶有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文件,裡面具體的代碼內容為:
<code>html> 單文件上傳單文件上傳
/<code>
其中action="onfile"代表的為請求地址為onfile,這裡都在項目內所以用相對地址即可,如果上傳為其他接口也可填寫對應的絕對地址。這樣前端頁面就編寫完成,我們還需要編寫文件上傳對應服務端模塊。
服務端設計
服務端主要負責文件接受,在前端看起來實現文件上傳的頁面很簡單,但實際上在服務端的文件接收並沒有那麼容易,因為傳過來的不光光是這一個(或多個)二進制文件,還附帶一些頭信息、文件名等等數據。打包過來的數據如果是文本數據解析可能還好,但是二進制文件數據一旦出現一點錯誤可能得到的整個文件都是損壞的。並且在咱們java web技術棧中文件上傳也是有一定發展的歷史的:
servlet文件上傳(3.0以前)在servlet3.0以前,文件上傳在服務端接收需要使用request.getInputStream()獲取表單的二進制數據,但是在解析時候非常麻煩和複雜,對於文件上傳這麼一個很基本的模塊在接收的時候可能要耗費很大的成本和精力去解決它,並且很多初級攻城獅很可能由於對io模塊陌生無法實現上傳文件在服務端的接收。
所以這個時候一些具有責任感的公司、組織就把它們的解析方法貢獻出來供大家使用,大家不需瞭解傳輸文件底層內容,這些開源的處理方式中,最流行的當屬apache旗下開源的commons-fileupload和 commons-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-fileupload和 commons-io包。將其進行二次集成和封裝至SpringMVC,將方法和內容封裝至MultipartFile接口讓我們使用起來更加方便,能夠容易實現單文件、多文件上傳。
對於上述各種文件上傳服務端實現方式,大致可以通過下圖展示:
通過上圖你就可明白SpringMVC文件上傳實現的原理,那麼下面你就可以進行大顯身手啦!SpringMVC處理上傳文件很簡單,我們需要在java目錄下創建一個uploadController.java創建這麼一個控制器,在上面加上@Controller註解。在Controller中編寫以下代碼:
<code> @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"; }/<code>
其中:
- @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的頁面。裡面的具體內容為:
<code>html> 多文件上傳同一類別多個文件上傳
圖片:
/<code>
這樣前端頁面就編寫完成,其中action要改為onfiles2,也就是待會要在服務端編寫的接口。還有注意的這些input 所有type為file代指類型為文件,而name均為img意思是上傳一組名稱為img圖片的集合。
服務端設計
而在我們服務端,其實用MultipartFile[]數組就可以對這樣的多文件進行接收,我們在controller中編寫以下代碼:
<code>@PostMapping("onfiles2") @ResponseBody public String onfiles2(MultipartFile img[]) throws IOException { for(int i=0;i/<code>
這個處理方式和前面的很相似,只不過是需要遍歷MultipartFile[]對每個文件進行接收處理,當然文件為空的時候不進行處理。
運行測試
這樣打開瀏覽器輸入:http://localhost:8080/index2.html,上傳文件測試效果:
這樣一組類似相冊上傳的功能就完成啦,當然實際開發中的文件上傳的要求肯定比這個要求嚴格很多,可能對文件的格式、大小都有一定的要求,這就要求你在前端和服務端都要對文件的後綴名、大小等信息進行校驗,以達到自己場景化的需求。
文件下載
文件下載估計你在日常生活中會經常遇到,而你下載的其實就是服務端(服務器)的資源,對於文件類型有多種多樣的,瀏覽器也能夠識別很多種資源,事實上你現在訪問的這個網頁也是服務端的html文件、圖片文件等資源,只不過這些資源瀏覽器能夠顯示而不會保存到本地。
直接訪問資源VS下載資源
如果直接訪問的資源是瀏覽器所不能識別解析的,例如doc、zip等類型文件,那訪問的時候會默認下載到本地。而當你在SpringMVC中使用下載功能時,無論是什麼資源都以下載的形式返回給客戶端。這種區別可以參考下圖:
在文件下載方面的實現,servlet本身也是實現文件下載的,不過使用起來有點繁瑣。其原理就是往HttpServletResponse response的輸出流寫字節內容。而我們SpringMVC對文件下載也做了封裝,將下載功能封裝至ResponseEntity類中,我們在使用的時候也很方便。下面就來實戰文件下載的功能。
首先,我們在F盤建立download文件夾,在裡面添加對應文件,這個文件夾我們作為服務端的資源。
前端設計
我們在創建一個文件下載的前端頁面,在static目錄下創建index3.html,頁面的具體內容為:
<code>html> SpringMVC文件下載SpringMVC文件下載
個人照片個人照片.png
個人簡歷個人簡歷.pdf /<code>
其中href是下載的超鏈接,download是下載的接口名,而鏈接最後面部分則是下載資源名稱。
服務端設計
文件下載的原理就是服務端向客戶端返回二進制流和信息,而SpringMVC通過ResponseEntity完成。我們在controller中編寫以下接口實現下載的功能:
<code>@GetMapping("download/{filename}") public ResponseEntitydownload(@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 headers = new HttpHeaders(); headers.add("Content-Disposition", "attchement;filename=" + file.getName()); //設置響應狀態 HttpStatus statusCode = HttpStatus.OK; in.close(); ResponseEntity entity = new ResponseEntity(body, headers, statusCode); return entity;//返回 }/<code>
這樣就是實現了文件下載功能,如果用傳統servlet的方式下載文件可能需要在HttpServletResponse response中設置各種信息,而使用SpringMVC的ResponseEntity只需要將文件二進制主體、頭信息以及狀態碼設置好即可進行文件下載,在易用性和簡潔上更勝一籌。
運行測試
打開瀏覽器輸入:http://localhost:8080/index3.html;點擊需要下載的文件,就實現了文件下載的功能,運行情況圖如下:
此時你就遇到了一個文件下載非常常見的問題:中文文件名錯誤顯示。這個解決方案也很容易解決,只需將Content-Disposition內容後面的文件名進行url編碼即可,具體代碼為(替換上面對於部分):
<code>headers.add("Content-Disposition", "attchement;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));/<code>
這樣重啟程序,刷新頁面再次點擊下載的鏈接,你就會發現文件被成功的下載了:
總結與拓展
至此,SpringMVC的單文件上傳、多文件上傳以及文件下載你已經全部掌握了,是不是滿滿的成就感想去實現一個自己的小網站並把相關內容放進去?不過SpringMVC文件上傳下載雖然簡單,但你依然需要掌握其原理,學好java中的io文件傳輸,這樣在各種場景的文件傳輸任務中方能勝任。
總結
前面所講文件上傳,前端就是form表單用表示客戶端要上傳文件,而服務端主要使用MultipartFile或者MultipartFile[]分別接收單個文件和多個文件。而在存儲到本地也僅僅需要在本地磁盤創建對應文件然後MultipartFile調用transferTo()方法即可將上傳的文件儲存。
而文件下載的前端需要一個請求的url鏈接,服務端需要編寫這個鏈接對應的接口。通過一些名稱找到文件在本地真實的位置通過ResponseEntity即可將二進制文件返回給客戶達到文件下載的功能。而ResponseEntity使用也很簡單在創建時候只需要傳入二進制主體、頭和狀態碼即可成功返回,而這些SpringMVC已進行了很好封裝你可以直接使用。
而無論是文件上傳、多文件上傳還是文件下載,一個完整的案例大致都需要這樣一個過程:
- 構思需求和頁面大體樣式
- 編寫前端html頁面
- 編寫服務端響應的請求
- 啟動程序運行測試
在其中過程如果有問題可以根據編譯器的錯誤提示、運行時的錯誤日誌找到根源進行修正,這樣完整的案例就可以成功完成啦!
案例拓展
你是否覺得自己掌握的可以了?那好,咱們拓展提升一下,我給你來一個需求:單文件和多文件混合上傳
假設小明需要實現一個文件上傳功能,小明需要上傳一份簡歷和若干份照片(小於3)。這個項目該如何設計呢?它的計劃頁面可能是這樣的:
我覺得聰明的你一定不會被難住,對於前端界面的html有什麼想法呢?
對於種類來說有簡歷和照片兩種文件,對於它們各自來說,簡歷只有一份,而照片可能有多份。
那麼咱們的html頁面可以這樣設計:
<code>html> 個人信息上傳個人信息上傳
姓名:
年齡:
圖片: 簡歷:
/<code>
這裡面和前面的單文件上傳不同的是有多個input標籤,此外action也要改成infoupload意思是你需要寫這麼一個接口來處理這個文件上傳的內容。在controller中編寫以下代碼:
<code> 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/<code>
這個理解起來其實也很容易,這個和上面主要的區別就是函數中的多參數,其實每一個參數都是要和前端頁面的form表單input標籤的內容對應(名稱一致)。form表單中的file類型在SpringMVC的controller中就是對應MultipartFile類型,form表單中的text類型對應controller中的String類型。如果上傳單個文件,在服務端就用MultipartFile類型參數接收,如果多文件就用MultipartFile[]進行接收。上傳類型和個數根據你自己的需求設計定義。
我們啟動程序打開瀏覽器輸入http://localhost:8080/index4.html選擇文件進行上傳,然後在本地你可以看到文件成功被保存。
至此,本篇的內容就結束了,本文主要簡單講解了SpringMVC中文件上傳、多文件上傳、文件下載的實現,現在你已熟練掌握。青山不改,綠水長流,我們下期再見!下課!
公眾號:bigsai
頭條號:碼農bigsai 一個等你關注而朝思夜暮的博主!如果有幫助,記得轉發分享!