15绒怨、文件上傳與下載

一、文件上傳

1谦疾、注意事項(xiàng)

1.1南蹂、表單要求

  1. 必須使用表單,而不能是超鏈接
  2. 表單的method必須是POST念恍,而不能是GET
  3. 表單的enctype必須是multipart/form-data

1.2六剥、Servlet的要求:

  1. 不能再使用request.getParameter()來(lái)獲取表單數(shù)據(jù)
  2. 可以使用request.getInputStream()得到所有的表單數(shù)據(jù),而不是一個(gè)表單項(xiàng)的數(shù)據(jù)
  3. 這說(shuō)明不使用fileupload峰伙,我們需要自己來(lái)對(duì)request.getInputStream()的內(nèi)容進(jìn)行解析

2疗疟、使用Servlet的API

2.1、servlet3.0上傳文件注意事項(xiàng):

  1. html中 表示文件上傳控件瞳氓;
  2. form的 enctype="multipart/form-data"策彤;
  3. 在Servlet類(lèi)前加上 @MultipartConfig
  4. request.getPart()獲得
    request.getPart("字段名字");
  5. 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);
      /把上傳文件保存到指定的位置

2.2匣摘、示例代碼

  1. 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>
    
  2. 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ù)部分
  1. isFormField

    public boolean isFormField()

    isFormField方法用于判斷FileItem類(lèi)對(duì)象封裝的數(shù)據(jù)是否屬于一個(gè)普通表單字段,還是屬于一個(gè)文件表單字段赠叼,如果是普通表單字段則返回true擦囊,否則返回false。

  2. 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)部分搬卒。

  3. getFieldName方法
    getFieldName方法用于返回表單字段元素的name屬性值,也就是返回name屬性值翎卓,例如“name=img”中的“img”契邀。
  4. 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)中古戴。
  5. 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)。
  6. getContentType方法
    getContentType 方法用于獲得上傳文件的類(lèi)型斥难,對(duì)于描述頭豹芯,getContentType方法返回的結(jié)果為字符串“image/gif”,即 “Content-Type”字段的值部分辜妓。如果FileItem類(lèi)對(duì)象對(duì)應(yīng)的是普通表單字段,該方法將返回null。getContentType 方法的完整語(yǔ)法定義如下:
  7. isInMemory方法
    isInMemory方法用來(lái)判斷FileItem類(lèi)對(duì)象封裝的主體內(nèi)容是存儲(chǔ)在內(nèi)存中艺配,還是存儲(chǔ)在臨時(shí)文件中,如果存儲(chǔ)在內(nèi)存中則返回true衍慎,否則返回false转唉。
  8. .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)
  1. 說(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ì)象,

  2. 構(gòu)造方法
    1. public ServletFileUpload():

      構(gòu)造一個(gè)未初始化的實(shí)例最岗,需要在解析請(qǐng)求之前先調(diào)用setFileItemFactory()方法設(shè)置 fileItemFactory屬性帕胆。

    2. public ServletFileUpload(FileItemFactory fileItemFactory)

      構(gòu)造一個(gè)實(shí)例,并根據(jù)參數(shù)指定的FileItemFactory 對(duì)象般渡,設(shè)置 fileItemFactory屬性

  3. 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異常

  4. public ServletFileUpload(FileItemFactory fileItemFactory)

    構(gòu)造一個(gè)實(shí)例记餐,并根據(jù)參數(shù)指定的FileItemFactory 對(duì)象,設(shè)置 fileItemFactory屬性

  5. 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))猴仑。

  6. 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))。

  7. 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)型趁尼。
    
  8. 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方法即可。
  9. 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)用
  10. getFileItemFactory()和setFileItemFactory(FileItemFactory)
    兩個(gè)方法繼承自FileUpload類(lèi)欧啤,用于設(shè)置和讀取fileItemFactory屬性。
  11. public void setProgressListener(ProgressListener pListener)
    設(shè)置文件上傳進(jìn)度監(jiān)聽(tīng)器启上。該方法有一個(gè)對(duì)應(yīng)的讀取方法:ProgressListener getProgressListener()邢隧。
  12. 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)
  1. 構(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è)置為1024
1024字節(jié),也就是1MB
upload.setFileSizeMax(10241024);
//設(shè)置上傳文件總量的最大值让腹,最大值=同時(shí)上傳的多個(gè)文件的大小的最大值的和远剩,目前設(shè)置為10MB
upload.setSizeMax(1024
102410);
//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="{fileNameMap}"> <c:url value="/servlet/DownLoadServlet" var="downurl"> <c:param name="filename" value="{me.key}"></c:param>
</c:url>
{me.value}<a href="{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");
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市恤筛,隨后出現(xiàn)的幾起案子官还,更是在濱河造成了極大的恐慌,老刑警劉巖毒坛,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件望伦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡煎殷,警方通過(guò)查閱死者的電腦和手機(jī)屯伞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)豪直,“玉大人劣摇,你說(shuō)我怎么就攤上這事《ド。” “怎么了饵撑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)唆貌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)垢乙,這世上最難降的妖魔是什么锨咙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮追逮,結(jié)果婚禮上酪刀,老公的妹妹穿的比我還像新娘。我一直安慰自己钮孵,他們只是感情好骂倘,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著巴席,像睡著了一般历涝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漾唉,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天荧库,我揣著相機(jī)與錄音,去河邊找鬼赵刑。 笑死分衫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的般此。 我是一名探鬼主播蚪战,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼牵现,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了邀桑?” 一聲冷哼從身側(cè)響起瞎疼,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎概漱,沒(méi)想到半個(gè)月后丑慎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓤摧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年竿裂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片照弥。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腻异,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出这揣,到底是詐尸還是另有隱情悔常,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布给赞,位于F島的核電站机打,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏片迅。R本人自食惡果不足惜残邀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柑蛇。 院中可真熱鬧芥挣,春花似錦、人聲如沸耻台。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盆耽。三九已至蹋砚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間征字,已是汗流浹背都弹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匙姜,地道東北人畅厢。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像氮昧,于是被迫代替她去往敵國(guó)和親框杜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浦楣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容