文件上傳 表單 指定<form>的method="post", enctype="multipart/form-data"。 accept指定文件類型,有多種類型時逗號分隔,multiple指定可以選擇多個文件。 傳統處理方式 選擇的文件是放在請求消息體中的。獲取的輸入流中包含了上傳的所有文件,如果 ...
文件上傳
表單
<form action="HandlerServlet" method="post" enctype="multipart/form-data">
請選擇要上傳的文件:<input type="file" name="uploadFile" accept="*/*" multiple /><br />
<button type="submit">上傳</button>
</form>
指定<form>的method="post", enctype="multipart/form-data"。
accept指定文件類型,有多種類型時逗號分隔,multiple指定可以選擇多個文件。
傳統處理方式
ServletInputStream inputStream = request.getInputStream();
選擇的文件是放在請求消息體中的。獲取的輸入流中包含了上傳的所有文件,如果單個上傳還好處理,如果同時上傳多個,不好從中分離出單個文件的數據,且自己寫代碼獲取文件名很麻煩。
Commons-FileUpload
Commons-FileUpload是Apache的開源組件,提供了一套處理上傳文件的API。
使用FileUpload需要2個jar包:
- commons-fileupload.jar
- commons-io.jar 這是FileUpload依賴的IO包,要單獨下載添加。
FileUpload組件主要的介面、類:
- DiskFileItemFactory類 將請求消息實體中的每一個參數都封裝為單獨的FileItem對象
- ServletFileUpload類 調用DiskFileItemFactory類來封裝請求參數,並對封裝好的FileItem做進一步處理,以List<FileItem>形式返回
- FileItem介面 用於封裝請求參數,提供了一系列操作上傳文件的方法。
DiskFileItemFactory類的常用方法
- DiskFileItemFactory() //空參的構造方法,使用預設臨界值、系統臨時文件夾
- DisFileItemfactory(int size, File repository) //帶參的構造方法,指定臨界值、緩存目錄
- FileItem createItem() //將一個請求參數封裝為FileItem,並返回封裝好的FileItem。這個方法由DiskFileItemFactory內部自動調用。不用我們管。
- set|SizeThreshold(int size) //設置臨界值
- setRepository(File repository) //設置緩存目錄。這2個方法都要對應的get方法。
JVM的記憶體是有限的,比如用戶上傳一個1G的文件,直接保存在JVM記憶體中,實打實地占用伺服器記憶體,這絕對不行。
臨界值指定文件大小,小於臨界值的文件直接存儲在JVM記憶體中,大於臨界值的文件,以臨時文件(.tmp)的形式保存在緩存目錄中。預設為10240,預設單位Byte,即10KB。
緩存目錄可以指定為磁碟上的某一個文件夾,以File形式指定,不指定時預設使用系統的臨時文件夾。
ServletFileUpload類的常用方法
- ServletFileUpload() //空參構造器
- ServletFileUpload(FileItemFactory fileItemFactory) //帶參的構造器,指定使用的FileItemFactory對象。FileItemFactory是介面,一般使用它的實現類DiskFileItemFactory(上面那個類)。
- setFileItemFactory(FileItemFactory fileItemFactory)
- setSizeMax(long size) //設置請求消息實體內容(傳遞的所有請求參數)的最大尺寸,防止用戶惡意上傳超大文件浪費伺服器存儲空間。預設單位Byte。
- setFileSizeMax(long size) //設置單個文件的最大尺寸
- setHeaderEncoding("utf-8") //設置請求參數的字元集,未設置時使用HttpServletRequest設置的字元集,若HttpServletRequest也未設置字元集,則使用系統預設的字元集。
以上幾個setter()方法均有對應的getter()方法。
- List<FileItem> parseRequest(HttpServletRequest request) //調用FileItemFactory對象的方法,開始封裝每個請求參數,以List<FileItem>形式返回封裝好的所有的請求參數
- FileItemIterator getItemIterator(HttpServletRequest request) //同上,只是返回的是迭代器
註意:是封裝每一個請求參數,不是只封裝上傳的文件,所以有些FileItem保存的是普通的請求欄位。
FileItem介面常用的方法
- boolean isFormField() //判斷這個FileItem封裝的是不是普通的表單欄位
- String getFieldName() //獲取欄位名,即表單控制項的name屬性
- String getName() //獲取上傳文件的文件名(帶有尾碼,比如:1.jpg),如果封裝的是普通的表單欄位,得到是null
- long getSize() //獲取文件的大小,預設單位位元組
- String getContentType() //獲取文件類型,比如image/jpeg
- boolean isInMemory() //是否存儲在JVM記憶體中。如果存儲在記憶體中,返回true;存儲在緩存目錄中,返回false。
- void write(File file) //將上傳文件寫到伺服器的磁碟中。如果此文件是存儲在緩存目錄中的,寫完會自動刪除緩存目錄中對應的臨時文件。
- InputStream getInputStream() //獲取輸入流
- void delete() //清除FileItem存儲的主體內容,如果存儲在緩存目錄中,會刪除緩存目錄中對應的臨時文件。雖然GC會自動清除不再使用的臨時文件,但使用完立即刪除是最好的。
Servlet處理上傳文件示例
1 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 response.setContentType("text/html;charset=utf-8"); 3 4 //創建FileItemFactory對象 5 DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory(); 6 File tempFolder=new File("E:/tempFolder"); //Java中的File即指文件,又指文件夾 7 if (!tempFolder.exists()){ //不存在就新建 8 tempFolder.mkdir(); 9 } 10 //指定緩存目錄 11 diskFileItemFactory.setRepository(tempFolder); 12 13 //創建ServletFileUpload對象 14 ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory); 15 //指定編碼字元集 16 servletFileUpload.setHeaderEncoding("utf-8"); 17 18 try { 19 //獲取List<FileItem> 20 List<FileItem> fileItems = servletFileUpload.parseRequest(request); 21 for (FileItem fileItem:fileItems){ 22 if(!fileItem.isFormField()){ //是上傳文件 23 //使用UUID創建唯一的文件名 24 String fileName= UUID.randomUUID().toString()+"_"+fileItem.getName(); 25 String path="C:/Users/chy/Desktop/upload/"+fileName; 26 fileItem.write(new File(path)); //寫到伺服器磁碟上。寫完會自動刪除臨時文件。 27 } 28 } 29 30 } catch (FileUploadException e) { 31 e.printStackTrace(); 32 } catch (Exception e) { 33 e.printStackTrace(); 34 } 35 36 37 }
文件下載
最簡單的方式:使用<a>標簽直接鏈接到文件地址。
<a href="http://www.xxx.com/resource/特斯拉.mp4">下載xxx</a>
有2個問題:
- 某些類型的文件,比如jpg、mp3、mp4、txt等會直接在網頁中打開、播放,而非下載。
- 如果以後資源地址換了,比如換ftp伺服器了、改資源路徑了,需要一個個地修改<a>連接的href屬性,十分麻煩。
解決方式:
(1)鏈接到Servlet,並傳遞文件名。
<a href="downloadServlet?fileName=特斯拉.mp4">下載xxx</a>
(2)Servlet
1 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 request.setCharacterEncoding("utf-8"); 3 response.setContentType("text/html;charset=utf-8"); 4 5 String fileName = request.getParameter("fileName"); 6 //對文件名進行iso8859-1編碼 7 String browerFileName=new String(fileName.getBytes("utf-8"),"iso8859-1"); 8 9 //指定瀏覽器行為為下載 10 response.addHeader("Content-Type","application/octet-stream"); 11 //指定下載文件名。註意這兩個都是addHeader(),不是setHeader() 12 response.addHeader("Content-Disposition","attachment;filename="+browerFileName); 13 14 InputStream is = getServletContext().getResourceAsStream("resource/"+fileName); //換路徑時只需修改此句中的路徑,不用修改每個<a> 15 ServletOutputStream os= response.getOutputStream(); 16 byte[] buffer = new byte[1024]; 17 int len; 18 while ((len=is.read(buffer))!= -1){ //註意是-1,不是0 19 os.write(buffer); 20 } 21 22 }
紅字是為瞭解決下載文件名中文亂碼。
因為我們使用的是utf-8字元集,而chrome這些瀏覽器預設使用 iso8859-1 字元集。要轉換一下,這樣瀏覽器在設置下載文件名時才能識別文件名中的中文。
其實 <a href="downloadServlet?fileName=特斯拉.mp4">下載xxx</a> ,url中的中文也要轉換一下,一個中文字元轉換為%xxxx的形式(%後面跟著4個十六進位數)。
<a href="downloadServlet?fileName=<%=URLEncoder.encode("特斯拉.mp4","utf-8")%>">下載</a>
String URLEncoder.encode(String str, String charset) //靜態方法,返回編碼後的字元串
當然,不轉換也行,都能正確識別。