JavaWeb中(文件上(shàng)傳和(hé / huò)下載) - 新聞資訊 - 雲南小程序開發|雲南軟件開發|雲南網站建設-昆明融晨信息技術有限公司

159-8711-8523

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

知識

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

您當前位置>首頁 » 新聞資訊 » 網站建設 >

JavaWeb中(文件上(shàng)傳和(hé / huò)下載)

發表時(shí)間:2020-8-19

發布人(rén):融晨科技

浏覽次數:80

引言:

  • 文件的(de)上(shàng)傳和(hé / huò)下載在(zài)web應用中是(shì)非常常用,也(yě)是(shì)非常有用的(de)功能。

    • 例如:發送電子(zǐ)郵件時(shí)可以(yǐ)同過上(shàng)傳附件發送文件,OA系統中可以(yǐ)通過上(shàng)傳文件來(lái)提交公文,社交網站通過上(shàng)傳圖片來(lái)自定義頭像等等。
    • 例如:下載實際上(shàng)隻要(yào / yāo)資源放在(zài)用戶可訪問的(de)目錄中用戶就(jiù)可以(yǐ)直接通過地(dì / de)址下載,但是(shì)一些資源是(shì)存放到(dào)數據庫中的(de),還有一些資源需要(yào / yāo)一定權限才能下載,這(zhè)裏就(jiù)需要(yào / yāo)我們通過Servlet來(lái)完成下載的(de)功能。
  • 可以(yǐ)說(shuō)上(shàng)傳和(hé / huò)下載是(shì)每一個(gè)web應用都需要(yào / yāo)具有的(de)一個(gè)功能,所以(yǐ)需要(yào / yāo)我們掌握。

  • 第1章 文件的(de)上(shàng)傳

1.1 文件上(shàng)傳的(de)步驟

文件的(de)上(shàng)傳主要(yào / yāo)分成兩個(gè)步驟:

  1. 用戶在(zài)頁面中選擇要(yào / yāo)上(shàng)傳的(de)文件,然後将請求提交到(dào)Servlet

  2. Servlet收到(dào)請求,解析用戶上(shàng)傳的(de)文件,然後将文件存儲到(dào)服務器

1.2 創建上(shàng)傳文件的(de)表單

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

  • 文件上(shàng)傳的(de)表單和(hé / huò)之(zhī)前的(de)表單類似,但有以(yǐ)下内容需要(yào / yāo)注意:

    • 表單的(de)method屬性必須爲(wéi / wèi)post
    • 表單的(de)enctype屬性必須爲(wéi / wèi)multipart/form-data
    • 上(shàng)傳文件的(de)控件是(shì)input,type屬性爲(wéi / wèi)file
  • 該表單打開後是(shì)如下效果:

    • IE

      [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議将圖片保存下來(lái)直接上(shàng)傳(img-0ohOyE7Q-1597829644379)(尚矽谷_張春勝_文件的(de)上(shàng)傳和(hé / huò)下載.assets/1558975331009.png)]

    • Chrome

      [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議将圖片保存下來(lái)直接上(shàng)傳(img-bQ5iOiZO-1597829644383)(尚矽谷_張春勝_文件的(de)上(shàng)傳和(hé / huò)下載.assets/1558975309963.png)]

    • 火狐

      [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議将圖片保存下來(lái)直接上(shàng)傳(img-VYT1AJDN-1597829644385)(尚矽谷_張春勝_文件的(de)上(shàng)傳和(hé / huò)下載.assets/1558975370024.png)]

  1. 編寫Servelet

    • 頁面的(de)表單控件創建好以(yǐ)後,選中文件點擊上(shàng)傳按鈕請求将會提交到(dào)指定的(de)Servlet來(lái)處理。

    • 注意:這(zhè)裏不(bù)能再像以(yǐ)前的(de)Servlet中那樣,通過request.getParamter()來(lái)獲取請求參數了(le/liǎo),當enctype=“multipart/form-data” 時(shí),再使用getParamter()獲取到(dào)内容永遠爲(wéi / wèi)空。因爲(wéi / wèi)浏覽器發送請求的(de)方式已經改變。

    • 既然以(yǐ)前的(de)方法不(bù)能使用了(le/liǎo),這(zhè)裏我們必須要(yào / yāo)引入一個(gè)新的(de)工具來(lái)解析請求中的(de)參數和(hé / huò)文件,這(zhè)個(gè)工具就(jiù)是(shì)commons-fileupload。

1.3 commons-fileupload

  • commons-fileupload是(shì)Apache開發的(de)一款專門用來(lái)處理上(shàng)傳的(de)工具,它的(de)作用就(jiù)是(shì)可以(yǐ)從request對象中解析出(chū),用戶發送的(de)請求參數和(hé / huò)上(shàng)傳文件的(de)流。

  • commons-fileupload包依賴commons-io,兩個(gè)包需要(yào / yāo)同時(shí)導入。

  • 核心類:

    1. DiskFileItemFactory

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

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

      3. 方法:

        • public void setSizeThreshold(int sizeThreshold):用于(yú)設置緩存文件的(de)大(dà)小(默認值10kb)

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

    2. ServletFileUpload

      1. 該類用于(yú)解析request對象從而(ér)獲取用戶發送的(de)請求參數(包括普通參數和(hé / huò)文件參數)

      2. 該類需要(yào / yāo)調用有參構造器創建實例,構造器中需要(yào / yāo)一個(gè)DiskFileItemFactory作爲(wéi / wèi)參數

      3. 方法:

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

        • public void setFileSizeMax(long fileSizeMax):設置單個(gè)文件的(de)大(dà)小限制,單位爲(wéi / wèi)B。如果上(shàng)傳文件超出(chū)限制,會在(zài)parseRequest()抛出(chū)異常FileSizeLimitExceededException。

        • public void setSizeMax(long sizeMax):限制請求内容的(de)總大(dà)小,單位爲(wéi / wèi)B。如果上(shàng)傳文件超出(chū)限制,會在(zài)parseRequest()抛出(chū)異常SizeLimitExceededException。

    3. FileItem

      1. 該類用于(yú)封裝用戶發送的(de)參數和(hé / huò)文件,也(yě)就(jiù)是(shì)用戶發送來(lái)的(de)信息将會被封裝成一個(gè)FileItem對象,我們通過該對象獲取請求參數或上(shàng)傳文件的(de)信息。

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

      3. 方法:

        • String getFieldName():獲取表單項的(de)名字,也(yě)就(jiù)是(shì)input當中的(de)name屬性的(de)值。

        • String getName():獲取上(shàng)傳的(de)文件名,普通的(de)請求參數爲(wéi / wèi)null。

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

          ? ① 若爲(wéi / wèi)文件,将文件的(de)流轉換爲(wéi / wèi)字符串。

          ? ② 若爲(wéi / wèi)請求參數,則獲取請求參數的(de)value。

        • boolean isFormField():判斷當前的(de)FileItem封裝的(de)是(shì)普通請求參數,還是(shì)一個(gè)文件。

          ? ① 如果爲(wéi / wèi)普通參數返回:true

          ? ② 如果爲(wéi / wèi)文件參數返回:false

        • String getContentType():獲取上(shàng)傳文件的(de)MIME類型

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

        • write():将文件上(shàng)傳到(dào)服務器

  • 示例代碼:創建一個(gè)Servlet并在(zài)doPost()方法中編寫如下代碼

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

第2章 文件的(de)下載

2.1 使用說(shuō)明

  • 文件下載最直接的(de)方法就(jiù)是(shì)把文件直接放到(dào)服務器的(de)目錄中,用戶直接訪問該文件就(jiù)可以(yǐ)直接下載。

  • 但是(shì)實際上(shàng)這(zhè)種方式并不(bù)一定好用,比如我們在(zài)服務器上(shàng)直接放置一個(gè)MP3文件,然後通過浏覽器訪問該文件的(de)地(dì / de)址,如果是(shì)IE浏覽器可能就(jiù)會彈出(chū)下載窗口,而(ér)如果是(shì)FireFox和(hé / huò)Chrome則有可能直接播放。再有就(jiù)是(shì)有一些文件我們是(shì)不(bù)希望用戶可以(yǐ)直接訪問到(dào)的(de),這(zhè)是(shì)我們就(jiù)要(yào / yāo)通過Servlet來(lái)完成下載功能。

  • 下載文件的(de)關鍵是(shì)幾點:

    1. 服務器以(yǐ)一個(gè)流的(de)形式将文件發送給浏覽器。

    2. 發送流的(de)同時(shí)還需要(yào / yāo)設置幾個(gè)響應頭,來(lái)告訴浏覽器下載的(de)信息。

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

      • FileInputStream is = new FileInputStream(file);
      • 這(zhè)個(gè)流就(jiù)是(shì)我們一會要(yào / yāo)發送給浏覽器的(de)内容
    4. 通過response獲取一個(gè)輸出(chū)流,并将文件(輸入流)通過該流發送給浏覽器

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

      • 通過輸出(chū)流向浏覽器發送文件(不(bù)要(yào / yāo)忘了(le/liǎo)關閉輸入流)

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

2.2 步驟演示

  • 一下步驟都是(shì)在(zài)同一個(gè)Servlet的(de)doGet()方法中編寫的(de)

  • 我所下載的(de)文件是(shì)放在(zài)WEB-INF下mp3文件夾中的(de)文件

  • 具體步驟

    1. 獲取文件的(de)流:

      String realPath = getServletContext().getRealPath("/WEB-INF/mp3/中國(guó)話.mp3");
      //獲取文件的(de)File對象
      File file = new File(realPath);
      //獲取文件的(de)輸入流
      FileInputStream is = new FileInputStream(file);
      
      
    2. 獲取頭信息:

      //獲取文件的(de)MIME信息
      String contentType = getServletContext().getMimeType(realPath);
      //設置下載文件的(de)名字
      String filename = "zhongguohua.mp3";
      //創建Content-Disposition信息
      String disposition = "attachment; filename="+ filename ;
      //獲取文件長度
      long size = file.length();
      
      
    3. 設置頭信息

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

      //通過response獲取輸出(chū)流,用于(yú)向浏覽器輸出(chū)内容
      ServletOutputStream out = response.getOutputStream();
      //将文件輸入流通過輸出(chū)流輸出(chū)
      byte[] b = new byte[1024];
      int len = 0;
      while((len=is.read(b))> 0){
      	out.write(b, 0, len);
      }
      //最後不(bù)要(yào / yāo)忘記關閉輸入流,輸出(chū)流由Tomcat自己處理,我們不(bù)用手動關閉
      is.close();
      
      

2.3 亂碼

  • 至此實際上(shàng)文件下載的(de)主要(yào / yāo)功能都已經完成。但是(shì)還有一個(gè)問題我們這(zhè)裏沒有體現出(chū)來(lái),因爲(wéi / wèi)目前我們的(de)文件名使用的(de)是(shì)純英文的(de),沒有亂碼問題。這(zhè)裏如果我們要(yào / yāo)使用中文文件名的(de)話,毫無疑問會出(chū)現亂碼問題。

  • 解決此問題的(de)方法很簡單,在(zài)獲取文件名之(zhī)後爲(wéi / wèi)文件名進行編碼:

filename = java.net.URLEncoder.encode(filename,"utf-8");
  • 但是(shì)注意這(zhè)裏火狐浏覽器比較特殊,因爲(wéi / wèi)他(tā)默認是(shì)以(yǐ)BASE64解碼的(de),所以(yǐ)這(zhè)塊如果需要(yào / yāo)考慮火狐的(de)問題的(de)話還需要(yào / yāo)特殊處理一下。

    1. 先要(yào / yāo)獲取客戶端信息(通過獲取請求頭中的(de)User-Agent信息)
    //獲取客戶端信息
    String ua = request.getHeader("User-Agent");
    
    
    1. 然後判斷浏覽器版本,做不(bù)同的(de)處理(通過判斷頭信息中是(shì)否包含Firefox字符串來(lái)判斷浏覽器版本)
    //判斷客戶端是(shì)否爲(wéi / wèi)火狐
    if(ua.contains("Firefox")){
    	//若爲(wéi / wèi)火狐使用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();
        //創建解析器
        ServletFileUpload servletFileUpload=new ServletFileUpload(diskFileItemFactory);
        //設置上(shàng)傳文件的(de)大(dà)小
        servletFileUpload.setFileSizeMax(1024*500);
        //獲取上(shàng)傳的(de)真實路徑
        String realPath = getServletContext().getRealPath("/upload");
        //優化1:如果文件路勁不(bù)存在(zài),則重新創建一個(gè)
         File refile=new File(realPath);
         if(refile.exists()==false){
             refile.mkdirs();
         }
         //優化2:若同名文件 則添加不(bù)上(shàng),因此解決同名文件,讓其添加上(shàng)。
        //優化3:設置上(shàng)傳文件大(dà)小
        //在(zài)前台傳來(lái)的(de)文件雖然爲(wéi / wèi)同一個(gè)文件,但是(shì)在(zài)後台我們可以(yǐ)将文件名進行擴展。
        // 通過ServletFileUpload中的(de)List<FileItem>  parseRequest(request),将request解析爲(wéi / wèi)List<FileItem>
        try {
            List<FileItem> fileItems = servletFileUpload.parseRequest(request);
            //遍曆集合
            for (FileItem fileItem : fileItems) {
                if(fileItem.isFormField()==false){
                    //獲取文件名
                    String fname = fileItem.getName();
                    //處理文件名
                    String replacename = UUID.randomUUID().toString().replace("-", "");
                   //将文件上(shàng)傳到(dào)服務器的(de)指定位置
                    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("上(shàng)傳文件大(dà)小超過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>下載demo</title>
</head>
<body>
<a href="DownServlet?fname=gson.jar">jar包下載</a>
<a href="DownServlet?fname=a698e5967c4d4c23a895ce42e3289d260.jpg">千庫網_粉色背景裏的(de)玫瑰花束_攝影圖編号73147.jpg</a>
<a href="">xxx.pptx</a>
</body>
</html>

相關案例查看更多