* method="post"html
* enctype="multipart/form-data"若是不加這個,它會把體中的中文生成url編碼的形式;上傳的時候用它java
* 表單中須要添加文件表單項:<input type="file" name="xxx" ...>web
* request.getParameter("xxx");這個方法在表單爲enctype="multipart/form-data"時,它做廢了。它永遠返回null。apache
* ServletInputStream request.getInputStream();包含整個請求的體!數組
---------------------------------------------------------------------瀏覽器
1.每隔出多個部件,即一個表單項一個部件緩存
2.一個部件中本身包含請求頭和空行,以及請求體。服務器
3普通表單項:jsp
> 一個頭:Content-Disposition:包含name="xxx",即表單項名稱。post
> 體就是表單項的值。
4.文件表單項:
> 兩個頭:
* Content-Disposition:包含name="xxx",即表單項名稱;還有一個filename="yyy.jpg",表示上傳文件的名稱。
* Content-Type:它是上傳文件的MIME類型,例如image/pjpeg,表示上傳的是圖片,圖中是jpg擴展名的圖片。
> 體就是上傳文件的內容。
------WebKitFormBoundary6opWGr8NaACK9My4 Content-Disposition: form-data; name="username" taeyeon ------WebKitFormBoundary6opWGr8NaACK9My4 Content-Disposition: form-data; name="zhaoPian"; filename="lovejrr.jpg" Content-Type: image/jpeg 文件內容
------WebKitFormBoundary6opWGr8NaACK9My4--
commons-fileupload-1.2.2.jar------這個小組件,它會幫咱們解析request中上傳的數據,解析後的結果是一個表單項數據封裝到一個FileItem對象中。咱們只須要調用FileItem的方法便可!
commons-io.jar-----依賴
相關類:
* 工廠:DiskFileItemFactory
* 解析器:ServletFileUpload
* 表單項:FileItem
a)建立工廠:DiskFileItemFactory factory = new DiskFileItemFactory();
b)建立解析器:ServletFileUpload sfu = new ServletFileUpload(factory);
c)使用解析器來解析request,獲得FileItem集合:List<FileItem> fileItemList = sfu.parseRequest(request);
* boolean isFormField():是否爲普通表單項!返回true爲普通表單項,若是返回false即文件表單項!
* String getFieldName():返回表單項的名稱;
* String getString(String charset):返回表單項的值;
* String getName():返回上傳的文件名稱;
* long getSize():返回上傳文件的字節數;
* InputStream getInputStream():返回上傳文件對應的輸入流;
* void write(File destFile):把上傳的文件內容保存到指定的文件中。
* String getContentType():返回上傳文件的類型。若是是普通表單,返回的是null。
form2.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <body> <h1>上傳2</h1> <form action='<c:url value="/Upload2Servlet"></c:url>' method="post" enctype="multipart/form-data"> 用戶名:<input type="text" name="username"/><br> 照片:<input type="file" name="zhaoPian"><br> <input type="submit" value="上傳"> </form> </body>
Upload2Servlet.java:
package day22_1; import java.io.File; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; public class Upload2Servlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 上傳三步 * 1.建立工廠 * 2.經過工廠建立解析器 * 3.解析request,獲得FileItem集合 * 4.遍歷FileItem集合,調用 其API完成文件的保存 */ DiskFileItemFactory factory=new DiskFileItemFactory(); ServletFileUpload sfu=new ServletFileUpload(factory); try { List<FileItem> fileItemList = sfu.parseRequest(request); FileItem fi1=fileItemList.get(0); FileItem fi2=fileItemList.get(1); System.out.println("普通表單項演示:"+fi1.getFieldName()+"="+fi1.getString("UTF-8")); System.out.println("Content-Type:"+fi2.getContentType()); System.out.println("Size:"+fi2.getSize()); System.out.println("filename:"+fi2.getName()); //保存文件 File destFile=new File("F:/jrrlove.jpg"); try { fi2.write(destFile); } catch (Exception e) { throw new RuntimeException(e); } } catch (FileUploadException e) { throw new RuntimeException(e);//異常轉換一下 } } }
1)文件必須保存到WEB-INF下!
* 目的是不讓瀏覽器直接訪問到!
* 把文件保存到WEB-INF目錄下!
2)文件名稱相關問題
* 有的瀏覽器上傳的文件名是絕對路徑,這須要切割!C:\files\baibing.jpg
String filename=fi2.getName();//獲取文件名(有可能帶路徑) int index=filename.lastIndexOf("\\"); if(index != -1){ filename=filename.substring(index+1); }
* 文件名亂碼或者普通表單項亂碼:request.setCharacterEncoding("utf-8");由於fileupload內部會調用reque.getCharacterEncoding();//優先級低
fileUpload.setHeaderEncoding(String):這種方式的優先級高於前一種。
上傳文件內容中包含中文:
一般咱們不關心上傳文件的內容,由於是把文件保存到硬盤上!若是在控制檯顯示上傳的文件內容,那麼可使用fileItem.getString("utf-8")來處理編碼。
文本文件內容和普通表單項內容使用FileItem類的getString("utf-8")來處理編碼。
* 文件同名問題:咱們須要爲每一個文件添加名稱前綴,這個前綴要保證不能重複。uuid
> filename = CommonsUtils.uuid() + "_" + filename;
3)目錄打散問題
* 不能在一個目錄下存放過多文件。
> 首字母打散:使用文件的首字母作爲目錄名稱,例如:abc.txt,那麼咱們把文件保存到a目錄下。若是a目錄不存在,那麼建立它。
> 時間打散:使用當前日期作爲目錄。
> 哈希打散:
# 經過文件名稱獲得int值,即調用hashCode()
# 它int 值轉換成16進制0~9,A~F
# 獲取16進制的前兩位用來生成目錄,目錄爲兩層!例如:1B2C3D4E5F,/1/B/保存文件。
jar包:commons-fileupload.jar
commons-io.jar
commons-beanutils.jar
commons-logging.jar
itcast-tools.jar
form3.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <body> <h1>上傳3</h1> <form action='<c:url value="/Upload3Servlet"></c:url>' method="post" enctype="multipart/form-data"> 用戶名:<input type="text" name="username" /><br> 照片:<input type="file" name="zhaoPian"><br> <input type="submit" value="上傳"> </form> </body>
Upload3Servlet.java:
package day22_1; import java.io.File; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import cn.itcast.commons.CommonUtils; public class Upload3Servlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 上傳三步 */ //工廠 DiskFileItemFactory factory=new DiskFileItemFactory(); //解析器 ServletFileUpload sfu=new ServletFileUpload(factory); //解析,獲得List try { List<FileItem> list = sfu.parseRequest(request); FileItem fi=list.get(1);//獲取文件表單項 ////////////////////////////////////// /* * 1.獲得文件保存的路徑 */ String root = request.getServletContext().getRealPath("/WEB-INF/files"); /* * 2.生成兩層目錄 * 1)獲得文件名稱 * 2)獲得hashCode * 3)轉換成16進制 * 4)獲取前兩個字符用來生成目錄 */ String filename = fi.getName();//獲取上傳的文件名稱 /* * 處理文件名絕對路徑問題 */ int index = filename.lastIndexOf("\\"); if(index != -1){ filename=filename.substring(index+1); } /* * 給文件名稱加uuid前綴,處理文件同名問題---itcast-tools.jar */ String savename=CommonUtils.uuid()+"_"+filename; /* * 1.獲得hashCode */ int hCode=filename.hashCode(); String hex = Integer.toHexString(hCode); /* * 2獲取hex的前兩個字母,與root鏈接在一塊兒,生成一個完整路徑 */ File dirFile=new File(root,hex.charAt(0)+"/"+hex.charAt(1)); /* * 3.建立目錄鏈 */ dirFile.mkdirs(); /* * 4.建立目錄文件 */ File destFile=new File(dirFile,savename); /* * 5.保存 */ fi.write(destFile); ////////////////////////////////////// } catch (FileUploadException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
在WEB-INF下建一個files目錄,上傳的圖片都會保存到服務器的這個項目下的這files目錄下。
4)上傳文件的大小限制
* 單個文件大小限制
> sfu.setFileSizeMax(100*1024):限制單個文件大小爲100KB
> 上面的方法調用,必須在解析開始以前調用!
> 若是上傳的文件超出限制,在parseRequest()方法執行時,會拋出異常!FileUploadBase.FileSizeLimitExceedeException。
舉例:-----單個文件大小限制
sfu.setFileSizeMax(100*1024); catch (FileUploadException e) { if(e instanceof FileUploadBase.FileSizeLimitExceededException){ request.setAttribute("msg", "您上傳的文件超出了100KB!"); request.getRequestDispatcher("/form3.jsp").forward(request, response); }
* 整個請求全部數據大小限制
> sfu.setSizeMax(1024*1024);//限制整個表單大小爲1M
> 這個方法也必須在parseRequest()方法以前調用!
> 若是上傳的文件超出限制,在parseRequest()方法執行時,會拋出異常!FileUploadBase.SizeLimitExceedeException。
5)緩存大小與臨時目錄
上傳的過程:客戶端--->服務器內存--->再到硬盤,因此對應特別大的文件須要使用到緩存(一次一次的從內存向硬盤保存數據)。
* 緩存大小:超出多大,才向硬盤上保存!默認爲10KB。
* 臨時目錄:向硬盤的什麼目錄保存。
設置緩存大小與臨時目錄:new DiskFileItemFactory(20*1024, new File("F:/temp"))
//工廠-----參數:1緩存大小 2臨時目錄 DiskFileItemFactory factory=new DiskFileItemFactory(20*1024,new File("F:/f/temp"));
*******上傳的文件臨時存放到這個目錄下,等上傳完畢,就會複製到files目錄下,而後該臨時目錄下的文件消失******
原來咱們響應的都是html的字符數據!
把一個文件變成字節數組,使用response.getOutputStream()來響應給瀏覽器!!!
* 兩個頭一個流!
> Content-Type:你傳遞給客戶端的文件是什麼MIME類型,例如:image/pjpeg
經過文件名稱調用ServletContext的getMimeType()方法,獲得MIME類型!
> Content-Disposition:它的默認值爲inline,表示在瀏覽器窗口中打開!attachment;filename=xxx
在filename=後面跟隨的是顯示在下載框中的文件名稱!
> 流:要下載的文件數據!
本身new一個輸入流便可!
示例:
package com.xjs.web.servlet; import java.io.FileInputStream; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; public class Download1Servlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 兩個頭一個流 * 1.Content-Type * 2.Content-Disposition * 3.流 下載文件的數據 */ String filename="G:/GD.mp3"; //經過ServletContent的getMimeTypeZ()方法獲得文件的MIME類型 String contentType = this.getServletContext().getMimeType(filename); String contentDisposition="attachment;filename=jrr.mp3"; FileInputStream in=new FileInputStream(filename); //設置頭 response.setHeader("Content-Type", contentType); response.setHeader("Content-Disposition", contentDisposition); //獲取綁定響應客戶端的流 ServletOutputStream out=response.getOutputStream(); IOUtils.copy(in, out);//把輸入流中的數據寫入到輸出流中。 in.close(); } }
1.顯示在下載框中的中文名稱時,會出現亂碼。
*FireFox:Base64編碼。
* 其餘大部分瀏覽器:URL編碼。
通用方案:filename=new String(filename.getBytes("GBK"), "ISO-8859-1");
// 用來對下載的文件名稱進行編碼的! public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException { String agent = request.getHeader("User-Agent"); //獲取瀏覽器 if (agent.contains("Firefox")) { BASE64Encoder base64Encoder = new BASE64Encoder(); filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?="; } else if(agent.contains("MSIE")) { filename = URLEncoder.encode(filename, "utf-8"); } else { filename = URLEncoder.encode(filename, "utf-8"); } return filename; }
對中文(不亂嗎)名稱文件的下載:
package com.xjs.web.servlet; import java.io.FileInputStream; import java.io.IOException; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import sun.misc.BASE64Encoder; public class Download1Servlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 兩個頭一個流 * 1.Content-Type * 2.Content-Disposition * 3.流 下載文件的數據 */ String filename="G:/金泰妍.mp3"; //爲了使下載框中顯示中文文件名稱不出亂碼! // String framename=new String("金泰妍.mp3".getBytes("GBK"),"ISO-8859-1"); String framename=filenameEncoding("金泰妍.mp3", request); //經過ServletContent的getMimeTypeZ()方法獲得文件的MIME類型 String contentType = this.getServletContext().getMimeType(filename); String contentDisposition="attachment;filename="+framename; FileInputStream in=new FileInputStream(filename); //設置頭 response.setHeader("Content-Type", contentType); response.setHeader("Content-Disposition", contentDisposition); //獲取綁定響應客戶端的流 ServletOutputStream out=response.getOutputStream(); IOUtils.copy(in, out);//把輸入流中的數據寫入到輸出流中。 in.close(); } // 用來對下載的文件名稱進行編碼的! public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException { String agent = request.getHeader("User-Agent"); //獲取瀏覽器 if (agent.contains("Firefox")) { BASE64Encoder base64Encoder = new BASE64Encoder(); filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?="; } else if(agent.contains("MSIE")) { filename = URLEncoder.encode(filename, "utf-8"); } else { filename = URLEncoder.encode(filename, "utf-8"); } return filename; } }