一、文件上傳
1谦疾、注意事項(xiàng)
1.1南蹂、表單要求
- 必須使用表單,而不能是超鏈接
- 表單的method必須是POST念恍,而不能是GET
- 表單的enctype必須是multipart/form-data
1.2六剥、Servlet的要求:
- 不能再使用request.getParameter()來(lái)獲取表單數(shù)據(jù)
- 可以使用request.getInputStream()得到所有的表單數(shù)據(jù),而不是一個(gè)表單項(xiàng)的數(shù)據(jù)
- 這說(shuō)明不使用fileupload峰伙,我們需要自己來(lái)對(duì)request.getInputStream()的內(nèi)容進(jìn)行解析
2疗疟、使用Servlet的API
2.1、servlet3.0上傳文件注意事項(xiàng):
- html中 表示文件上傳控件瞳氓;
- form的 enctype="multipart/form-data"策彤;
- 在Servlet類(lèi)前加上 @MultipartConfig
- request.getPart()獲得
request.getPart("字段名字"); - part對(duì)象
- String getContentType()
獲取上傳文件的MIME類(lèi)型 - String getName();
獲取表單項(xiàng)的名稱(chēng),不是文件名稱(chēng) - String getHeader(String header);
獲取指定的頭的值 - String getSize();
獲取上傳文件的大小 - InputStream getInputStream();
獲取文件流內(nèi)容 - void write(String filePath);
/把上傳文件保存到指定的位置
- String getContentType()
2.2匣摘、示例代碼
- HTML代碼
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <form method="post" enctype="multipart/form-data" action="upload"> <input type="file" id="file" name="file" /> <input type="text" id="name" name="name" /> <input type="submit" value="提交" /> </form> </body> </html>
- Java代碼
@WebServlet(name = "UploadServlet", urlPatterns = {"/upload"}) @MultipartConfig public class UploadServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); } public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Part part = request.getPart("file"); request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<body>"); out.println("此文件的大械晔:" + part.getSize() + "<br />"); out.println("此文件類(lèi)型:" + part.getContentType() + "<br />"); out.println("文本框內(nèi)容:" + request.getParameter("name") + "<br />"); out.println(UploadUtil.getFileName(part) + "<br />"); out.println("</body>"); out.println("</html>"); } }
3、第三方框架 commons-fileupload-1.3.3.jar
3.1音榜、核心類(lèi)api介紹
1.1庞瘸、FileItem 表示文件上傳表單中 每個(gè)數(shù)據(jù)部分
- isFormField
public boolean isFormField()
isFormField方法用于判斷FileItem類(lèi)對(duì)象封裝的數(shù)據(jù)是否屬于一個(gè)普通表單字段,還是屬于一個(gè)文件表單字段赠叼,如果是普通表單字段則返回true擦囊,否則返回false。
- getName方法
public String getName()
getName方法用于獲得文件上傳字段中的文件名梅割,對(duì)于描述頭霜第,getName方法返回的結(jié)果為字符串
“C:/bg.gif”。如果FileItem類(lèi)對(duì)象對(duì)應(yīng)的是普通表單字段户辞,getName方法將返回null。即使用戶(hù)沒(méi)有通過(guò)網(wǎng)頁(yè)
表單中的文件字段傳遞任何文件癞谒,但只要設(shè)置了文件表單字段的name屬性底燎,瀏覽器也會(huì)將文件字段的信息傳
遞給服務(wù)器刃榨,只是文件名和文件內(nèi)容部分都為空,但這個(gè)表單字段仍然對(duì)應(yīng)一個(gè)FileItem對(duì)象双仍,此時(shí)枢希,
getName方法返回結(jié)果為空字符串"",讀者在調(diào)用Apache文件上傳組件時(shí)要注意考慮這個(gè)情況朱沃。
注意:如果用戶(hù)使用Windows系統(tǒng)上傳文件苞轿,瀏覽器將傳遞該文件的完整路徑,如果用戶(hù)使用Linux或者
Unix系統(tǒng)上傳文件逗物,瀏覽器將只傳遞該文件的名稱(chēng)部分搬卒。
- getFieldName方法
getFieldName方法用于返回表單字段元素的name屬性值,也就是返回name屬性值翎卓,例如“name=img”中的“img”契邀。 - write方法 public void write(File file)
write 方法用于將FileItem對(duì)象中保存的主體內(nèi)容保存到某個(gè)指定的文件中。如果FileItem對(duì)象中的主體內(nèi)容是保存在某個(gè)臨時(shí)文件中失暴,該方法順利完成后坯门,臨時(shí)文件有可能會(huì)被清除。該方法也可將普通表單字段內(nèi)容寫(xiě)入到一個(gè)文件中逗扒,但它主要用途是將上傳的文件內(nèi)容保存在本地文件系統(tǒng)中古戴。 - getString方法
getString方法用于將FileItem對(duì)象中保存的主體內(nèi)容作為一個(gè)字符串返回,它有兩個(gè)重載的定義形式:前者使用缺省的字符集編碼將主體內(nèi)容轉(zhuǎn)換成字符串矩肩,后者使用參數(shù)指定的字符集編碼將主體內(nèi)容轉(zhuǎn)換成字符串现恼。如果在讀取普通表單字段元素的內(nèi)容時(shí)出現(xiàn)了中文亂碼現(xiàn)象,請(qǐng)調(diào)用第二個(gè)getString方法,并為之傳遞正確的字符集編碼名稱(chēng)。 - getContentType方法
getContentType 方法用于獲得上傳文件的類(lèi)型斥难,對(duì)于描述頭豹芯,getContentType方法返回的結(jié)果為字符串“image/gif”,即 “Content-Type”字段的值部分辜妓。如果FileItem類(lèi)對(duì)象對(duì)應(yīng)的是普通表單字段,該方法將返回null。getContentType 方法的完整語(yǔ)法定義如下: - isInMemory方法
isInMemory方法用來(lái)判斷FileItem類(lèi)對(duì)象封裝的主體內(nèi)容是存儲(chǔ)在內(nèi)存中艺配,還是存儲(chǔ)在臨時(shí)文件中,如果存儲(chǔ)在內(nèi)存中則返回true衍慎,否則返回false转唉。 - .delete方法
delete方法用來(lái)清空FileItem類(lèi)對(duì)象中存放的主體內(nèi)容,如果主體內(nèi)容被保存在臨時(shí)文件中稳捆,delete方法將刪除該臨時(shí)文件赠法。盡管 Apache組件使用了多種方式來(lái)盡量及時(shí)清理臨時(shí)文件,但系統(tǒng)出現(xiàn)異常時(shí)乔夯,仍有可能造成有的臨時(shí)文件被永久保存在了硬盤(pán)中砖织。在有些情況下款侵,可以調(diào)用這個(gè)方法來(lái)及時(shí)刪除臨時(shí)文件
1.2、ServletFileUpload 文件上傳核心類(lèi)
- 說(shuō)明
是Apache文件上傳組件處理文件上傳的核心高級(jí)類(lèi)(所謂高級(jí)就是不需要管底層實(shí)現(xiàn)侧纯,暴露給用戶(hù)的簡(jiǎn)單易用的接口)新锈。使用其 parseRequest(HttpServletRequest) 方法可以將通過(guò)表單中每一個(gè)HTML標(biāo)簽提交的數(shù)據(jù)封裝成一個(gè)FileItem對(duì)象,然后以List列表的形式返回眶熬。使用該方法處理上傳文件簡(jiǎn)單易用妹笆。 如果你希望進(jìn)一步提高性能,你可以采用 getItemIterator 方法娜氏,直接獲得每一個(gè)文件項(xiàng)的數(shù)據(jù)輸入流拳缠,對(duì)數(shù)據(jù)做直接處理。 在使用ServletFileUpload對(duì)象解析請(qǐng)求時(shí)需要根據(jù)DiskFileItemFactory對(duì)象的屬性 sizeThreshold(臨界值)和repository(臨時(shí)目錄) 來(lái)決定將解析得到的數(shù)據(jù)保存在內(nèi)存還是臨時(shí)文件中牍白,如果是臨時(shí)文件脊凰,保存在哪個(gè)臨時(shí)目錄中?茂腥。所以狸涌,我們需要在進(jìn)行解析工作前構(gòu)造好DiskFileItemFactory對(duì)象,
- 構(gòu)造方法
-
public ServletFileUpload():
構(gòu)造一個(gè)未初始化的實(shí)例最岗,需要在解析請(qǐng)求之前先調(diào)用setFileItemFactory()方法設(shè)置 fileItemFactory屬性帕胆。
-
public ServletFileUpload(FileItemFactory fileItemFactory)
構(gòu)造一個(gè)實(shí)例,并根據(jù)參數(shù)指定的FileItemFactory 對(duì)象般渡,設(shè)置 fileItemFactory屬性
-
- public void setSizeMax(long sizeMax)
方法setSizeMax方法繼承自FileUploadBase類(lèi)懒豹,用于設(shè)置請(qǐng)求消息實(shí)體內(nèi)容(即所有上傳數(shù)據(jù))的最大尺寸限制,以防止客戶(hù)端惡意上傳超大文件來(lái)浪費(fèi)服務(wù)器端的存儲(chǔ)空間驯用。其參數(shù)是以字節(jié)為單位的long型數(shù)字脸秽。 在請(qǐng)求解析的過(guò)程中,如果請(qǐng)求消息體內(nèi)容的大小超過(guò)了setSizeMax方法的設(shè)置值蝴乔,將會(huì)拋出FileUploadBase內(nèi)部定義的SizeLimitExceededException異常
- public ServletFileUpload(FileItemFactory fileItemFactory)
構(gòu)造一個(gè)實(shí)例记餐,并根據(jù)參數(shù)指定的FileItemFactory 對(duì)象,設(shè)置 fileItemFactory屬性
- public void setSizeMax(long sizeMax)
setSizeMax方法繼承自FileUploadBase類(lèi)薇正,用于設(shè)置請(qǐng)求消息實(shí)體內(nèi)容(即所有上傳數(shù)據(jù))的最大尺寸限制片酝,以防止客戶(hù)端惡意上傳超大文件來(lái)浪費(fèi)服務(wù)器端的存儲(chǔ)空間。其參數(shù)是以字節(jié)為單位的long型數(shù)字挖腰。
在請(qǐng)求解析的過(guò)程中雕沿,如果請(qǐng)求消息體內(nèi)容的大小超過(guò)了setSizeMax方法的設(shè)置值,將會(huì)拋出FileUploadBase內(nèi)部定義的SizeLimitExceededException異常(FileUploadException的子類(lèi))猴仑。
- public void setFileSizeMax(long fileSizeMax)
方法setFileSizeMax方法繼承自FileUploadBase類(lèi)审轮,用于設(shè)置單個(gè)上傳文件的最大尺寸限制,以防止客戶(hù)端惡意上傳超大文件來(lái)浪費(fèi)服務(wù)器端的存儲(chǔ)空間。其參數(shù)是以字節(jié)為單位的long型數(shù)字断国。該方法有一個(gè)對(duì)應(yīng)的讀方法:public long geFileSizeMax()方法贤姆。
在請(qǐng)求解析的過(guò)程中榆苞,如果單個(gè)上傳文件的大小超過(guò)了setFileSizeMax方法的設(shè)置值稳衬,將會(huì)拋出FileUploadBase內(nèi)部定義的FileSizeLimitExceededException異常(FileUploadException的子類(lèi))。
- public List parseRequest(javax.servlet.http.HttpServletRequest req)
parseRequest 方法是ServletFileUpload類(lèi)的重要方法坐漏,它是對(duì)HTTP請(qǐng)求消息體內(nèi)容進(jìn)行解析的入口方法薄疚。它解析出FORM表單中的每個(gè)字段的數(shù)據(jù),并將它們分別包裝成獨(dú)立的FileItem對(duì)象赊琳,然后將這些FileItem對(duì)象加入進(jìn)一個(gè)List類(lèi)型的集合對(duì)象中返回街夭。 該方法拋出FileUploadException異常來(lái)處理諸如文件尺寸過(guò)大、請(qǐng)求消息中的實(shí)體內(nèi)容的類(lèi)型不是“multipart/form-data”躏筏、IO異常板丽、請(qǐng)求消息體長(zhǎng)度信息丟失等各種異常。每一種異常都是FileUploadException的一個(gè)子類(lèi)型趁尼。
- public FileItemIterator getItemIterator(HttpServletRequest request)
getItemIterator方法和parseRequest 方法基本相同埃碱。但是getItemIterator方法返回的是一個(gè)迭代器,該迭代器中保存的不是FileItem對(duì)象酥泞,而是FileItemStream 對(duì)象砚殿,如果你希望進(jìn)一步提高性能,你可以采用 getItemIterator 方法芝囤,直接獲得每一個(gè)文件項(xiàng)的數(shù)據(jù)輸入流似炎,做底層處理;如果性能不是問(wèn)題悯姊,你希望代碼簡(jiǎn)單羡藐,則采用parseRequest方法即可。 - public stiatc boolean isMultipartContent(HttpServletRequest req)
isMultipartContent方法方法用于判斷請(qǐng)求消息中的內(nèi)容是否是“multipart/form-data”類(lèi)型悯许,是則返回true仆嗦,否則返回false。isMultipartContent方法是一個(gè)靜態(tài)方法岸晦,不用創(chuàng)建ServletFileUpload類(lèi)的實(shí)例對(duì)象即可被調(diào)用 - getFileItemFactory()和setFileItemFactory(FileItemFactory)
兩個(gè)方法繼承自FileUpload類(lèi)欧啤,用于設(shè)置和讀取fileItemFactory屬性。 - public void setProgressListener(ProgressListener pListener)
設(shè)置文件上傳進(jìn)度監(jiān)聽(tīng)器启上。該方法有一個(gè)對(duì)應(yīng)的讀取方法:ProgressListener getProgressListener()邢隧。 - public void setHeaderEncoding()
在文件上傳請(qǐng)求的消息體中,除了普通表單域的值是文本內(nèi)容以外冈在,文件上傳字段中的文件路徑名也是文本倒慧,在內(nèi)存中保存的是它們的某種字符集編碼的字節(jié)數(shù)組,Apache文件上傳組件在讀取這些內(nèi)容時(shí),必須知道它們所采用的字符集編碼纫谅,才能將它們轉(zhuǎn)換成正確的字符文本返回炫贤。
setHeaderEncoding方法繼承自FileUploadBase類(lèi),用于設(shè)置上面提到的字符編碼付秕。如果沒(méi)有設(shè)置兰珍,則對(duì)應(yīng)的讀方法getHeaderEncoding()方法返回null,將采用HttpServletRequest設(shè)置的字符編碼询吴,如果HttpServletRequest的字符編碼也為null掠河,則采用系統(tǒng)默認(rèn)字符編碼∶图疲可以通過(guò)一下語(yǔ)句獲得系統(tǒng)默認(rèn)字符編碼:System.getProperty("file.encoding"));
1.3唠摹、DiskFileItemFactory 磁盤(pán)文件項(xiàng)工廠類(lèi)
- 構(gòu)造工廠時(shí),指定內(nèi)存緩沖區(qū)大小和臨時(shí)文件存放位置
public DiskFileItemFactory(int sizeThreshold, File repository)
2. 設(shè)置內(nèi)存緩沖區(qū)大小奉瘤,Apache文件上傳組件在解析和處理上傳數(shù)據(jù)中的每個(gè)字段內(nèi)容時(shí)勾拉,需要臨時(shí)保存解析出的數(shù)據(jù)。因?yàn)閇Java](http://lib.csdn.net/base/java)虛擬機(jī)默認(rèn)可以使用的內(nèi)存空間是有限的([測(cè)試](http://lib.csdn.net/base/softwaretest)不大于100M)盗温,超出限制時(shí)將會(huì)發(fā)生“java.lang.OutOfMemoryError”錯(cuò)誤藕赞,如果上傳的文件很大,例如上傳800M的文件肌访,在內(nèi)存中將無(wú)法保存該文件內(nèi)容找默,Apache文件上傳組件將用臨時(shí)文件來(lái)保存這些數(shù)據(jù);但如果上傳的文件很小吼驶,例如上傳600個(gè)字節(jié)的文件惩激,顯然將其直接保存在內(nèi)存中更加有效。setSizeThreshold方法用于設(shè)置是否使用臨時(shí)文件保存解析出的數(shù)據(jù)的那個(gè)臨界值蟹演,該方法傳入的參數(shù)的單位是字節(jié)
3. 默認(rèn)10K
public void setSizeThreshold(int sizeThreshold)
4. 設(shè)置臨時(shí)文件存放位置风钻,如果不設(shè)置存放路徑,那么臨時(shí)文件將被儲(chǔ)存在"java.io.tmpdir"這個(gè)JVM環(huán)境屬性所指定的目錄中酒请,tomcat 將這個(gè)屬性設(shè)置為了“/temp/”目錄
public void setRepository(File repository)
5. 說(shuō)明
- 內(nèi)存緩沖區(qū)
> 上傳文件時(shí)骡技,上傳文件的內(nèi)容優(yōu)先保存在內(nèi)存緩沖區(qū)中,當(dāng)上傳文件大小超過(guò)緩沖區(qū)大小羞反,就會(huì)在服務(wù)器端產(chǎn)生臨時(shí)文件
- 臨時(shí)文件存放位置
> 保存超過(guò)了內(nèi)存緩沖區(qū)大小上傳文件而產(chǎn)生臨時(shí)文件 布朦,產(chǎn)生臨時(shí)文件可以通過(guò) FileItem的delete()方法刪除
#### 3.2、示例代碼
1. Java部分
public class UploadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//得到上傳文件的保存目錄昼窗,將上傳的文件存放于WEB-INF目錄下是趴,不允許外界直接訪問(wèn),保證上傳文件的安全
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
//上傳時(shí)生成的臨時(shí)文件保存目錄
String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
File tmpFile = new File(tempPath);
if (!tmpFile.exists()) {
//創(chuàng)建臨時(shí)目錄
tmpFile.mkdir();
}
//消息提示
String message = "";
try{
//使用Apache文件上傳組件處理文件上傳步驟:
//1澄惊、創(chuàng)建一個(gè)DiskFileItemFactory工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
//設(shè)置工廠的緩沖區(qū)的大小唆途,當(dāng)上傳的文件大小超過(guò)緩沖區(qū)的大小時(shí)富雅,就會(huì)生成一個(gè)臨時(shí)文件存放到指定的臨時(shí)目錄當(dāng)中。
factory.setSizeThreshold(1024100);//設(shè)置緩沖區(qū)的大小為100KB肛搬,如果不指定没佑,那么緩沖區(qū)的大小默認(rèn)是10KB
//設(shè)置上傳時(shí)生成的臨時(shí)文件的保存目錄
factory.setRepository(tmpFile);
//2、創(chuàng)建一個(gè)文件上傳解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//監(jiān)聽(tīng)文件上傳進(jìn)度
upload.setProgressListener(new ProgressListener(){
public void update(long pBytesRead, long pContentLength, int arg2) {
System.out.println("文件大小為:" + pContentLength + ",當(dāng)前已處理:" + pBytesRead);
/*
* 文件大小為:14608,當(dāng)前已處理:4096
/
}
});
//解決上傳文件名的中文亂碼
upload.setHeaderEncoding("UTF-8");
//3温赔、判斷提交上來(lái)的數(shù)據(jù)是否是上傳表單的數(shù)據(jù)
if(!ServletFileUpload.isMultipartContent(request)){
//按照傳統(tǒng)方式獲取數(shù)據(jù)
return;
}
//設(shè)置上傳單個(gè)文件的大小的最大值蛤奢,目前是設(shè)置為10241024字節(jié),也就是1MB
upload.setFileSizeMax(10241024);
//設(shè)置上傳文件總量的最大值让腹,最大值=同時(shí)上傳的多個(gè)文件的大小的最大值的和远剩,目前設(shè)置為10MB
upload.setSizeMax(1024102410);
//4、使用ServletFileUpload解析器解析上傳數(shù)據(jù)骇窍,解析結(jié)果返回的是一個(gè)List<FileItem>集合,每一個(gè)FileItem對(duì)應(yīng)一個(gè)Form表單的輸入項(xiàng)
List<FileItem> list = upload.parseRequest(request);
for(FileItem item : list){
//如果fileitem中封裝的是普通輸入項(xiàng)的數(shù)據(jù)
if(item.isFormField()){
String name = item.getFieldName();
//解決普通輸入項(xiàng)的數(shù)據(jù)的中文亂碼問(wèn)題
String value = item.getString("UTF-8");
//value = new String(value.getBytes("iso8859-1"),"UTF-8");
System.out.println(name + "=" + value);
}else{//如果fileitem中封裝的是上傳文件
//得到上傳的文件名稱(chēng)锥余,
String filename = item.getName();
System.out.println(filename);
if(filename==null || filename.trim().equals("")){
continue;
}
//注意:不同的瀏覽器提交的文件名是不一樣的腹纳,有些瀏覽器提交上來(lái)的文件名是帶有路徑的,如: c:\a\b\1.txt驱犹,而有些只是單純的文件名嘲恍,如:1.txt
//處理獲取到的上傳文件的文件名的路徑部分,只保留文件名部分
filename = filename.substring(filename.lastIndexOf("\")+1);
//得到上傳文件的擴(kuò)展名
String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
//如果需要限制上傳的文件類(lèi)型雄驹,那么可以通過(guò)文件的擴(kuò)展名來(lái)判斷上傳的文件類(lèi)型是否合法
System.out.println("上傳的文件的擴(kuò)展名是:"+fileExtName);
//獲取item中的上傳文件的輸入流
InputStream in = item.getInputStream();
//得到文件保存的名稱(chēng)
String saveFilename = makeFileName(filename);
//得到文件的保存目錄
String realSavePath = makePath(saveFilename, savePath);
//創(chuàng)建一個(gè)文件輸出流
FileOutputStream out = new FileOutputStream(realSavePath + "\" + saveFilename);
//創(chuàng)建一個(gè)緩沖區(qū)
byte buffer[] = new byte[1024];
//判斷輸入流中的數(shù)據(jù)是否已經(jīng)讀完的標(biāo)識(shí)
int len = 0;
//循環(huán)將輸入流讀入到緩沖區(qū)當(dāng)中佃牛,(len=in.read(buffer))>0就表示in里面還有數(shù)據(jù)
while((len=in.read(buffer))>0){
//使用FileOutputStream輸出流將緩沖區(qū)的數(shù)據(jù)寫(xiě)入到指定的目錄(savePath + "\" + filename)當(dāng)中
out.write(buffer, 0, len);
}
//關(guān)閉輸入流
in.close();
//關(guān)閉輸出流
out.close();
//刪除處理文件上傳時(shí)生成的臨時(shí)文件
//item.delete();
message = "文件上傳成功!";
}
}
}catch (FileUploadBase.FileSizeLimitExceededException e) {
e.printStackTrace();
request.setAttribute("message", "單個(gè)文件超出最大值R接摺7馈!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}catch (FileUploadBase.SizeLimitExceededException e) {
e.printStackTrace();
request.setAttribute("message", "上傳文件的總的大小超出限制的最大值J呓R佟!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}catch (Exception e) {
message= "文件上傳失斚蓟场惫东!";
e.printStackTrace();
}
request.setAttribute("message",message);
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
/*
* @Method: makeFileName
* @生成上傳文件的文件名,文件名以:uuid+""+文件的原始名稱(chēng)
* @param filename 文件的原始名稱(chēng)
* @return uuid+""+文件的原始名稱(chēng)
/
private String makeFileName(String filename){ //2.jpg
//為防止文件覆蓋的現(xiàn)象發(fā)生毙石,要為上傳文件產(chǎn)生一個(gè)唯一的文件名
return UUID.randomUUID().toString() + "_" + filename;
}
/*
* @Method: makePath
* @param filename 文件名廉沮,要根據(jù)文件名生成存儲(chǔ)目錄
* @param savePath 文件存儲(chǔ)路徑
* @return 新的存儲(chǔ)目錄
*/
private String makePath(String filename,String savePath){
//得到文件名的hashCode的值,得到的就是filename這個(gè)字符串對(duì)象在內(nèi)存中的地址
int hashcode = filename.hashCode();
int dir1 = hashcode&0xf; //0--15
int dir2 = (hashcode&0xf0)>>4; //0-15
//構(gòu)造新的保存目錄
String dir = savePath + "\" + dir1 + "\" + dir2; //upload\2\3 upload\3\5
//File既可以代表文件也可以代表目錄
File file = new File(dir);
//如果目錄不存在
if(!file.exists()){
//創(chuàng)建目錄
file.mkdirs();
}
return dir;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
2. jsp部分,
<form action="/uploadimg"
method="post" enctype="multipart/form-data">
用戶(hù)名:<input type="text" name="username"/>
文件1:<input type="file" name="img"/>
<input type="submit" value="上傳"/>
</form>
3. 注意細(xì)節(jié)
1徐矩、為保證服務(wù)器安全滞时,上傳文件應(yīng)該放在外界無(wú)法直接訪問(wèn)的目錄下,比如放于WEB-INF目錄下丧蘸。 2漂洋、為防止文件覆蓋的現(xiàn)象發(fā)生遥皂,要為上傳文件產(chǎn)生一個(gè)唯一的文件名。 3刽漂、要限制上傳文件的最大值演训。 4、要限制上傳文件的類(lèi)型贝咙,在收到上傳文件名時(shí)样悟,判斷后綴名是否合法。
## 二庭猩、文件下載
### 1窟她、JAVA代碼
/**
- 列出Web系統(tǒng)中所有下載文件
/
public class ListFileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//獲取上傳文件的目錄
String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");
//存儲(chǔ)要下載的文件名
Map<String,String> fileNameMap = new HashMap<String,String>();
//遞歸遍歷filepath目錄下的所有文件和目錄,將文件的文件名存儲(chǔ)到map集合中
listfile(new File(uploadFilePath),fileNameMap);//File既可以代表一個(gè)文件也可以代表一個(gè)目錄
//將Map集合發(fā)送到listfile.jsp頁(yè)面進(jìn)行顯示
request.setAttribute("fileNameMap", fileNameMap);
request.getRequestDispatcher("/listfile.jsp").forward(request, response);
}
public void listfile(File file,Map<String,String> map){
//如果file代表的不是一個(gè)文件蔼水,而是一個(gè)目錄
if(!file.isFile()){
//列出該目錄下的所有文件和目錄
File files[] = file.listFiles();
//遍歷files[]數(shù)組
for(File f : files){
//遞歸
listfile(f,map);
}
}else{
/*
* 處理文件名震糖,上傳后的文件是以u(píng)uid_文件名的形式去重新命名的,去除文件名的 *uuid_部分file.getName().indexOf("")
* 那么file.getName().substring(file.getName().indexOf("")+1)
*/
String realName = file.getName().substring(file.getName().indexOf("_")+1);
//file.getName()得到的是文件的原始名稱(chēng)趴腋,這個(gè)名稱(chēng)是唯一的吊说,因此可以作為key,realName是處理過(guò)后的名稱(chēng)优炬,有可能會(huì)重復(fù)
map.put(file.getName(), realName);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
### 2颁井、文件下載jsp頁(yè)面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML>
<html>
<head>
<title>下載文件顯示頁(yè)面</title>
</head>
<body>
<c:forEach var="me" items="{me.key}"></c:param>
</c:url>
{downurl}">下載</a>
</c:forEach>
</body>
</html>
### 3、下載的Servlet
public class DownLoadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//得到要下載的文件名
String fileName = request.getParameter("filename"); //23239283-92489-阿凡達(dá).avi
fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
//上傳的文件都是保存在/WEB-INF/upload目錄下的子目錄當(dāng)中
String fileSaveRootPath=this.getServletContext().getRealPath("/WEB-INF/upload");
//通過(guò)文件名找出文件的所在目錄
String path = findFileSavePathByFileName(fileName,fileSaveRootPath);
//得到要下載的文件
File file = new File(path + "\" + fileName);
//如果文件不存在
if(!file.exists()){
request.setAttribute("message", "您要下載的資源已被刪除4阑ぁ雅宾!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
//處理文件名
String realname = fileName.substring(fileName.indexOf("_")+1);
//設(shè)置響應(yīng)頭,控制瀏覽器下載該文件
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8"));
//讀取要下載的文件葵硕,保存到文件輸入流
FileInputStream in = new FileInputStream(path + "\" + fileName);
//創(chuàng)建輸出流
OutputStream out = response.getOutputStream();
//創(chuàng)建緩沖區(qū)
byte buffer[] = new byte[1024];
int len = 0;
//循環(huán)將輸入流中的內(nèi)容讀取到緩沖區(qū)當(dāng)中
while((len=in.read(buffer))>0){
//輸出緩沖區(qū)的內(nèi)容到瀏覽器眉抬,實(shí)現(xiàn)文件下載
out.write(buffer, 0, len);
}
//關(guān)閉文件輸入流
in.close();
//關(guān)閉輸出流
out.close();
}
/**
* 通過(guò)文件名和存儲(chǔ)上傳文件根目錄找出要下載的文件的所在路徑
* @param filename 要下載的文件名
* @param saveRootPath 上傳文件保存的根目錄,也就是/WEB-INF/upload目錄
* @return 要下載的文件的存儲(chǔ)目錄
*/
public String findFileSavePathByFileName(String filename,String saveRootPath){
int hashcode = filename.hashCode();
int dir1 = hashcode&0xf; //0--15
int dir2 = (hashcode&0xf0)>>4; //0-15
String dir = saveRootPath + "\" + dir1 + "\" + dir2; //upload\2\3 upload\3\5
File file = new File(dir);
if(!file.exists()){
//創(chuàng)建目錄
file.mkdirs();
}
return dir;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
## 三贬芥、Servlet3.0后支持文件上傳(上課筆記)
1吐辙、文件上傳功能:Servlet3.0之后才支持,3.0以前使用commons-fileupload.jar第三方j(luò)ar實(shí)現(xiàn)文件上傳
#### 2蘸劈、注意事項(xiàng);
(1)昏苏、必須使用post請(qǐng)求,Content-Type:multipart/form-data
(2)威沫、必須在Servlet上添加@MultipartConfig
? @MultipartConfig屬性說(shuō)明:
? maxFileSize :表示一次上傳文件的最大大小贤惯,即總共數(shù)據(jù)的大小(不是的單個(gè)文件)棒掠,默認(rèn)沒(méi)有限制
? maxRequestSize :顯示文件上傳的最大數(shù)孵构,默認(rèn)沒(méi)有限制
#### 3、文件上傳java代碼步驟:
##### (1)原生的代碼實(shí)現(xiàn)單個(gè)文件上傳
```java
import javax.servlet.*;
import java.io.*;
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取上傳文件要保存在服務(wù)器的路徑烟很,沒(méi)有就在服務(wù)器根目錄創(chuàng)建
ServletContext context = req.getServletContext();
String savePath = context.getRealPath("/upload/img");
File file = new File(savePath);
if (!file.exists()){
file.mkdirs();
}
//實(shí)例化
Part part = req.getPart("file");
//獲取輸入流颈墅,用來(lái)將文件讀取到程序內(nèi)存
InputStream is = part.getInputStream();
//保存路徑+文件名稱(chēng)
String imgPath= savePath+File.separator+getFileName(part);
//獲取輸出流
OutputStream os = new FileOutputStream(imgPath);
//原生的方法讀寫(xiě)開(kāi)始
byte[] data = new byte[1024];
int length = -1;
while ((length = is.read(data))!=-1){
os.write(data,0,length);
}
os.flush();
os.close();
is.close();
resp.getWriter().write("success");
}
/**
* 得到上傳文件的具體名稱(chēng)
* @param part
* @return
*/
public String getFileName(Part part){
String fileName = null;
//獲取請(qǐng)求頭
String value = part.getHeader("Content-Disposition");
String[] values = value.split(";");
for (String key : values) {
if (key.trim().startsWith("filename")){
fileName = key.substring(key.indexOf("=") + 2, key.length() - 1);
}
}
return fileName;
}
}
(2)簡(jiǎn)便的方法實(shí)現(xiàn)單個(gè)文件上傳
import javax.servlet.*;
import java.io.*;
@WebServlet("/newupload")
@MultipartConfig
public class NewUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = req.getServletContext();
String savePath = context.getRealPath("/upload/img");
File file = new File(savePath);
if (!file.exists()){
file.mkdirs();
}
Part part = req.getPart("file");
//Servlet3.1之后支持的獲取文件名稱(chēng)的簡(jiǎn)便方法 part.getSubmittedFileName();
String fileName = part.getSubmittedFileName();
String imgPath = savePath+"/"+fileName;
//不用原生的代碼蜡镶,直接調(diào)用方法上傳文件到服務(wù)器指定的路徑
part.write(imgPath);
resp.getWriter().write("success");
}
}
說(shuō)明:Servlet3.1之后支持獲取文件名稱(chēng)的簡(jiǎn)便方法 part.getSubmittedFileName();
(3)實(shí)現(xiàn)多個(gè)文件上傳
import javax.servlet.*;
import java.io.*;
@WebServlet("/uploadmany")
@MultipartConfig
public class UploadManyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = req.getServletContext();
String savePath = context.getRealPath("/upload/img");
File file = new File(savePath);
if (!file.exists()){
file.mkdirs();
}
//獲取多個(gè)文件對(duì)象
Collection<Part> parts = req.getParts();
for (Part part : parts) {
String fileName = part.getSubmittedFileName();
String imgPath = savePath+"/"+ fileName;
part.write(imgPath);
}
resp.getWriter().write("success");
}
}