JavaWeb中(zhōng)(文(wén)件上傳和(hé)下(xià)載) - 新聞資(zī)訊 - 雲南小程序開發|雲南軟件開發|雲南網站(zhàn)建設-西山區知普網絡科技工作室

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不管是網站(zhàn),軟件還是小程序,都要直接或間接能為您産生價值,我們在追求其視覺表現的同時,更側重于功能的便捷,營銷的便利,運營的高效,讓網站(zhàn)成為營銷工具,讓軟件能切實提升企業(yè)内部管理水平和(hé)效率。優秀的程序為後期升級提供便捷的支持!

您當前位置>首頁 » 新聞資(zī)訊 » 網站(zhàn)建設 >

JavaWeb中(zhōng)(文(wén)件上傳和(hé)下(xià)載)

發表時間:2020-8-19

發布人:葵宇科技

浏覽次數:53

引言:

  • 文(wén)件的上傳和(hé)下(xià)載在web應用中(zhōng)是非常常用,也是非常有用的功能。

    • 例如(rú):發送電子(zǐ)郵件時可(kě)以同過上傳附件發送文(wén)件,OA系統中(zhōng)可(kě)以通(tōng)過上傳文(wén)件來提交公文(wén),社交網站(zhàn)通(tōng)過上傳圖片來自定義頭像等等。
    • 例如(rú):下(xià)載實際上隻要資(zī)源放在用戶可(kě)訪問(wèn)的目錄中(zhōng)用戶就可(kě)以直接通(tōng)過地址下(xià)載,但是一些資(zī)源是存放到數據庫中(zhōng)的,還有一些資(zī)源需要一定權限才能下(xià)載,這裡就需要我們通(tōng)過Servlet來完成下(xià)載的功能。
  • 可(kě)以說上傳和(hé)下(xià)載是每一個(gè)web應用都需要具有的一個(gè)功能,所以需要我們掌握。

  • 第1章 文(wén)件的上傳

1.1 文(wén)件上傳的步驟

文(wén)件的上傳主要分成兩個(gè)步驟:

  1. 用戶在頁面中(zhōng)選擇要上傳的文(wén)件,然後将請求提交到Servlet

  2. Servlet收到請求,解析用戶上傳的文(wén)件,然後将文(wén)件存儲到服務器(qì)

1.2 創建上傳文(wén)件的表單

  1. 創建一個(gè)form表單
<form action="" method="post" enctype="multipart/form-data">
	<input type="file" name="file" /><br /><br />
	<input type="submit" value="上傳" />
</form>

  • 文(wén)件上傳的表單和(hé)之前的表單類似,但有以下(xià)内容需要注意:

    • 表單的method屬性必須為post
    • 表單的enctype屬性必須為multipart/form-data
    • 上傳文(wén)件的控件是input,type屬性為file
  • 該表單打開後是如(rú)下(xià)效果:

    • IE

      [外鍊圖片轉存失敗,源站(zhàn)可(kě)能有防盜鍊機制,建議将圖片保存下(xià)來直接上傳(img-0ohOyE7Q-1597829644379)(尚矽谷_張春勝_文(wén)件的上傳和(hé)下(xià)載.assets/1558975331009.png)]

    • Chrome

      [外鍊圖片轉存失敗,源站(zhàn)可(kě)能有防盜鍊機制,建議将圖片保存下(xià)來直接上傳(img-bQ5iOiZO-1597829644383)(尚矽谷_張春勝_文(wén)件的上傳和(hé)下(xià)載.assets/1558975309963.png)]

    • 火狐

      [外鍊圖片轉存失敗,源站(zhàn)可(kě)能有防盜鍊機制,建議将圖片保存下(xià)來直接上傳(img-VYT1AJDN-1597829644385)(尚矽谷_張春勝_文(wén)件的上傳和(hé)下(xià)載.assets/1558975370024.png)]

  1. 編寫Servelet

    • 頁面的表單控件創建好以後,選中(zhōng)文(wén)件點擊上傳按鈕請求将會提交到指定的Servlet來處理。

    • 注意:這裡不能再像以前的Servlet中(zhōng)那樣,通(tōng)過request.getParamter()來獲取請求參數了,當enctype=“multipart/form-data” 時,再使用getParamter()獲取到内容永遠(yuǎn)為空。因為浏覽器(qì)發送請求的方式已經改變。

    • 既然以前的方法不能使用了,這裡我們必須要引入一個(gè)新的工具來解析請求中(zhōng)的參數和(hé)文(wén)件,這個(gè)工具就是commons-fileupload。

1.3 commons-fileupload

  • commons-fileupload是Apache開發的一款專門用來處理上傳的工具,它的作用就是可(kě)以從request對象中(zhōng)解析出,用戶發送的請求參數和(hé)上傳文(wén)件的流。

  • commons-fileupload包依賴commons-io,兩個(gè)包需要同時導入。

  • 核心類:

    1. DiskFileItemFactory

      1. 工廠類,用于創建ServletFileUpload,設置緩存等

      2. 該類一般直接使用構造器(qì)直接創建實例

      3. 方法:

        • public void setSizeThreshold(int sizeThreshold):用于設置緩存文(wén)件的大小(默認值10kb)

        • public void setRepository(File repository):用于設置緩存文(wén)件位置(默認系統緩存目錄)

    2. ServletFileUpload

      1. 該類用于解析request對象從而獲取用戶發送的請求參數(包括普通(tōng)參數和(hé)文(wén)件參數)

      2. 該類需要調用有參構造器(qì)創建實例,構造器(qì)中(zhōng)需要一個(gè)DiskFileItemFactory作為參數

      3. 方法:

        • public List parseRequest(HttpServletRequest request):解析request對象,獲取請求參數,返回的是一個(gè)List,List中(zhōng)保存的是一個(gè)FileItem對象,一個(gè)對象代表一個(gè)請求參數。

        • public void setFileSizeMax(long fileSizeMax):設置單個(gè)文(wén)件的大小限制,單位為B。如(rú)果上傳文(wén)件超出限制,會在parseRequest()抛出異常FileSizeLimitExceededException。

        • public void setSizeMax(long sizeMax):限制請求内容的總大小,單位為B。如(rú)果上傳文(wén)件超出限制,會在parseRequest()抛出異常SizeLimitExceededException。

    3. FileItem

      1. 該類用于封裝用戶發送的參數和(hé)文(wén)件,也就是用戶發送來的信息将會被封裝成一個(gè)FileItem對象,我們通(tōng)過該對象獲取請求參數或上傳文(wén)件的信息。

      2. 該類不用我們手動(dòng)創建,由ServletFileItem解析request後返回。

      3. 方法:

        • String getFieldName():獲取表單項的名字,也就是input當中(zhōng)的name屬性的值。

        • String getName():獲取上傳的文(wén)件名,普通(tōng)的請求參數為null。

        • String getString(String encoding):獲取内容,encoding參數需要指定一個(gè)字符集。

          ? ① 若為文(wén)件,将文(wén)件的流轉換為字符串。

          ? ② 若為請求參數,則獲取請求參數的value。

        • boolean isFormField():判斷當前的FileItem封裝的是普通(tōng)請求參數,還是一個(gè)文(wén)件。

          ? ① 如(rú)果為普通(tōng)參數返回:true

          ? ② 如(rú)果為文(wén)件參數返回:false

        • String getContentType():獲取上傳文(wén)件的MIME類型

        • long getSize():獲取内容的大小

        • write():将文(wén)件上傳到服務器(qì)

  • 示例代碼:創建一個(gè)Servlet并在doPost()方法中(zhōng)編寫如(rú)下(xià)代碼

    //創建工廠類
    DiskFileItemFactory factory = new DiskFileItemFactory();
    //創建請求解析器(qì)
    ServletFileUpload fileUpload = new ServletFileUpload(factory);
    //設置上傳單個(gè)文(wén)件的的大小
    fileUpload.setFileSizeMax(1024*1024*3);
    //設置上傳總文(wén)件的大小
    fileUpload.setSizeMax(1024*1024*3*10);
    //設置響應内容的編碼
    response.setContentType("text/html;charset=utf-8");
    try {
    	//解析請求信息,獲取FileItem的集合
    	List<FileItem> items = fileUpload.parseRequest(request);
    	//遍曆集合
    	for (FileItem fileItem : items) {
    		//如(rú)果是普通(tōng)的表單項
    		if(fileItem.isFormField()){
    		    //獲取參數名
    		    String fieldName = fileItem.getFieldName();
    		    //獲取參數值
    		    String value = fileItem.getString("utf-8");
    		    System.out.println(fieldName+" = "+value);
    	        //如(rú)果是文(wén)件表單項
    	    }else{
    		    //獲取文(wén)件名
    		    String fileName = fileItem.getName();
    		    //獲取上傳路(lù)徑
    		    String realPath = getServletContext().getRealPath("/WEB-INF/upload");
    		    //檢查upload文(wén)件夾是否存在,如(rú)果不存在則創建
    		    File f = new File(realPath);
    		    if(!f.exists()){
    			    f.mkdir();
    		    };
    		    //為避免重名生成一個(gè)uuid作為文(wén)件名的前綴
    		    String prefix = UUID.randomUUID().toString().replace("-", "");
    		    //将文(wén)件寫入到服務器(qì)中(zhōng)
    		    fileItem.write(new File(realPath+"/"+prefix+"_"+fileName));
    		    //清楚文(wén)件緩存
    		    fileItem.delete();
    	    }
    }
    } catch (Exception e) {
    	if(e instanceof SizeLimitExceededException){
    		//文(wén)件總大小超出限制
    		response.getWriter().print("上傳文(wén)件的總大小不能超過30M");
    	}else if(e instanceof FileSizeLimitExceededException){
    		//單個(gè)文(wén)件大小超出限制
    		response.getWriter().print("上傳單個(gè)文(wén)件的大小不能超過3M");
    	}
    } 
    response.getWriter().print("上傳成功");
    
    

第2章 文(wén)件的下(xià)載

2.1 使用說明

  • 文(wén)件下(xià)載最直接的方法就是把文(wén)件直接放到服務器(qì)的目錄中(zhōng),用戶直接訪問(wèn)該文(wén)件就可(kě)以直接下(xià)載。

  • 但是實際上這種方式并不一定好用,比如(rú)我們在服務器(qì)上直接放置一個(gè)MP3文(wén)件,然後通(tōng)過浏覽器(qì)訪問(wèn)該文(wén)件的地址,如(rú)果是IE浏覽器(qì)可(kě)能就會彈出下(xià)載窗口,而如(rú)果是FireFox和(hé)Chrome則有可(kě)能直接播放。再有就是有一些文(wén)件我們是不希望用戶可(kě)以直接訪問(wèn)到的,這是我們就要通(tōng)過Servlet來完成下(xià)載功能。

  • 下(xià)載文(wén)件的關(guān)鍵是幾點:

    1. 服務器(qì)以一個(gè)流的形式将文(wén)件發送給浏覽器(qì)。

    2. 發送流的同時還需要設置幾個(gè)響應頭,來告訴浏覽器(qì)下(xià)載的信息。

      • 具體響應頭如(rú)下(xià):
        • Content-Type
          • 下(xià)載文(wén)件的MIME類型
          • 可(kě)以通(tōng)過servletContext. getMimeType(String file)獲取
          • 也可(kě)以直接手動(dòng)指定
          • 使用response.setContentType(String type);
          • 響應頭樣式:Content-Type: audio/mpeg
        • Content-Disposition
          • 下(xià)載文(wén)件的名字,主要作用是提供一個(gè)默認的用戶名
          • 通(tōng)過response.setHeader(“Content-Disposition”, disposition)設置
          • static/file/attachment;filename=xxx.html
        • Content-Length
          • 下(xià)載文(wén)件的長度,用于設置文(wén)件的長處(不必須)
          • 通(tōng)過response. setContentLength(int len)設置。
          • 設置後樣式:Content-Length: 3140995
    3. 接下(xià)來需要以輸入流的形式讀入硬盤上的文(wén)件

      • FileInputStream is = new FileInputStream(file);
      • 這個(gè)流就是我們一會要發送給浏覽器(qì)的内容
    4. 通(tōng)過response獲取一個(gè)輸出流,并将文(wén)件(輸入流)通(tōng)過該流發送給浏覽器(qì)

      • 獲取輸出流:ServletOutputStream out = response.getOutputStream();

      • 通(tōng)過輸出流向浏覽器(qì)發送文(wén)件(不要忘了關(guān)閉輸入流)

        byte[] b = new byte[1024];
        int len = 0;
        while((len=is.read(b))> 0){
        	out.write(b, 0, len);
        }
        is.close();
        
        

2.2 步驟演示

  • 一下(xià)步驟都是在同一個(gè)Servlet的doGet()方法中(zhōng)編寫的

  • 我所下(xià)載的文(wén)件是放在WEB-INF下(xià)mp3文(wén)件夾中(zhōng)的文(wén)件

  • 具體步驟

    1. 獲取文(wén)件的流:

      String realPath = getServletContext().getRealPath("static/file/attachment;filename=xxx.html");
      //獲取文(wén)件的File對象
      File file = new File(realPath);
      //獲取文(wén)件的輸入流
      FileInputStream is = new FileInputStream(file);
      
      
    2. 獲取頭信息:

      //獲取文(wén)件的MIME信息
      String contentType = getServletContext().getMimeType(realPath);
      //設置下(xià)載文(wén)件的名字
      String filename = "static/file/attachment;filename=xxx.html";
      //創建Content-Disposition信息
      String disposition = "attachment; filename="+ filename ;
      //獲取文(wén)件長度
      long size = file.length();
      
      
    3. 設置頭信息

      //設置Content-Type
      response.setContentType(contentType);
      //設置Content-Disposition
      response.setHeader("Content-Disposition", disposition);
      //設置文(wén)件長度
      response.setContentLength((int)size);
      
      
    4. 發送文(wén)件

      //通(tōng)過response獲取輸出流,用于向浏覽器(qì)輸出内容
      ServletOutputStream out = response.getOutputStream();
      //将文(wén)件輸入流通(tōng)過輸出流輸出
      byte[] b = new byte[1024];
      int len = 0;
      while((len=is.read(b))> 0){
      	out.write(b, 0, len);
      }
      //最後不要忘記關(guān)閉輸入流,輸出流由Tomcat自己處理,我們不用手動(dòng)關(guān)閉
      is.close();
      
      

2.3 亂碼

  • 至此實際上文(wén)件下(xià)載的主要功能都已經完成。但是還有一個(gè)問(wèn)題我們這裡沒有體現出來,因為目前我們的文(wén)件名使用的是純英文(wén)的,沒有亂碼問(wèn)題。這裡如(rú)果我們要使用中(zhōng)文(wén)文(wén)件名的話,毫無疑問(wèn)會出現亂碼問(wèn)題。

  • 解決此問(wèn)題的方法很簡單,在獲取文(wén)件名之後為文(wén)件名進行編碼:

filename = java.net.URLEncoder.encode(filename,"utf-8");
  • 但是注意這裡火狐浏覽器(qì)比較特殊,因為他默認是以BASE64解碼的,所以這塊如(rú)果需要考慮火狐的問(wèn)題的話還需要特殊處理一下(xià)。

    1. 先要獲取客戶端信息(通(tōng)過獲取請求頭中(zhōng)的User-Agent信息)
    //獲取客戶端信息
    String ua = request.getHeader("User-Agent");
    
    
    1. 然後判斷浏覽器(qì)版本,做不同的處理(通(tōng)過判斷頭信息中(zhōng)是否包含Firefox字符串來判斷浏覽器(qì)版本)
    //判斷客戶端是否為火狐
    if(ua.contains("Firefox")){
    	//若為火狐使用BASE64編碼
    	filename = "=?utf-8?B?"+new BASE64Encoder()
    .encode(filename.getBytes("utf-8"))+"?=";
    }else{
    	//否則使用UTF-8
    	filename = URLEncoder.encode(filename,"utf-8");
    }
    
    

完整代碼:

  //設置響應字符集
        response.setContentType("text/html; charset=UTF-8");

        DiskFileItemFactory diskFileItemFactory=new DiskFileItemFactory();
        //創建解析器(qì)
        ServletFileUpload servletFileUpload=new ServletFileUpload(diskFileItemFactory);
        //設置上傳文(wén)件的大小
        servletFileUpload.setFileSizeMax(1024*500);
        //獲取上傳的真實路(lù)徑
        String realPath = getServletContext().getRealPath("/upload");
        //優化1:如(rú)果文(wén)件路(lù)勁不存在,則重新創建一個(gè)
         File refile=new File(realPath);
         if(refile.exists()==false){
             refile.mkdirs();
         }
         //優化2:若同名文(wén)件 則添加不上,因此解決同名文(wén)件,讓其添加上。
        //優化3:設置上傳文(wén)件大小
        //在前台傳來的文(wén)件雖然為同一個(gè)文(wén)件,但是在後台我們可(kě)以将文(wén)件名進行擴展。
        // 通(tōng)過ServletFileUpload中(zhōng)的List<FileItem>  parseRequest(request),将request解析為List<FileItem>
        try {
            List<FileItem> fileItems = servletFileUpload.parseRequest(request);
            //遍曆集合
            for (FileItem fileItem : fileItems) {
                if(fileItem.isFormField()==false){
                    //獲取文(wén)件名
                    String fname = fileItem.getName();
                    //處理文(wén)件名
                    String replacename = UUID.randomUUID().toString().replace("-", "");
                   //将文(wén)件上傳到服務器(qì)的指定位置
                    File file=new File(realPath+File.separator+replacename+fname);
                    fileItem.write(file);
                    PrintWriter writer = response.getWriter();
                    writer.write("upload success");

                }
            }
        }
        catch (FileUploadBase.FileSizeLimitExceededException e){
            response.getWriter().write("上傳文(wén)件大小超過50KB");
        }
        catch (Exception e) {
            e.printStackTrace();
        }

jsp代碼:

<%--
  Created by IntelliJ IDEA.
  User: admin
  Date: 2020/8/19
  Time: 16:29
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>下(xià)載demo</title>
</head>
<body>
<a href="DownServlet?fname=gson.jar">jar包下(xià)載</a>
<a href="DownServlet?fname=a698e5967c4d4c23a895ce42e3289d260.jpg">千庫網_粉色背景裡的玫瑰花(huā)束_攝影圖編号73147.jpg</a>
<a href="">xxx.pptx</a>
</body>
</html>

相關(guān)案例查看更多