1. 程式人生 > >每一次對問題的深究,總會收穫新的知識和體會

每一次對問題的深究,總會收穫新的知識和體會

jsp中實現檔案下載的最簡單的方式是在網頁上做超級連結,如:<a href="music/abc.mp3">點選下載</a>。但是這樣伺服器上的目錄資源會直接暴露給終端使用者,會給網站帶來一些不安全的因素。因此可以採用其它方式實現下載,可以採用:

1、RequestDispatcher的方式進行;2、採用檔案流輸出的方式下載。
 
1、採用RequestDispatcher的方式進行
            jsp頁面中新增如下程式碼:

<%  
      response.setContentType("application/x-download");//設定為下載application/x-download  
      String filedownload = "/要下載的檔名";//即將下載的檔案的相對路徑  
      String filedisplay = "最終要顯示給使用者的儲存檔名";//下載檔案時顯示的檔案儲存名稱  
      filenamedisplay = URLEncoder.encode(filedisplay,"UTF-8");  
      response.addHeader("Content-Disposition","attachment;filename=" + filedisplay);  
      
      try  
      {  
          RequestDispatcher dis = application.getRequestDispatcher(filedownload);  
          if(dis!= null)  
          {  
              dis.forward(request,response);  
          }  
          response.flushBuffer();  
      }  
      catch(Exception e)  
      {  
          e.printStackTrace();  
      }  
      finally  
      {  
      
      }  
%> 


 
2、採用檔案流輸出的方式下載

<%@ page import="java.io.OutputStream" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="java.net.URLEncoder" %>
<%@ page language="java" contentType="application/x-msdownload" pageEncoding="UTF-8" %>
<%
    //關於檔案下載時採用檔案流輸出的方式處理:
    String bath = request.getSession().getServletContext().getRealPath("");
    response.setContentType("application/x-download");
    String fileDownloadName = bath + request.getParameter("fileDownloadName"); // 下載的檔案的物理路徑+檔名
    String fileDisplayName = String.valueOf(System.currentTimeMillis())+fileDownloadName.substring(fileDownloadName.lastIndexOf("."),fileDownloadName.length()); // 給使用者提供的下載檔名
    fileDisplayName = URLEncoder.encode(fileDisplayName, "UTF-8");
    response.addHeader("Content-Disposition", "attachment;filename=" + fileDisplayName);
    OutputStream outp = null;
    FileInputStream in = null;
    try {
        outp = response.getOutputStream();
        in = new FileInputStream(fileDownloadName);
        byte[] b = new byte[1024];
        int i = 0;
        while ((i = in.read(b)) > 0) {
            outp.write(b, 0, i);
        }
        outp.flush();
    } catch (Exception e) {
        System.out.println("檔案下載失敗!");
        e.printStackTrace();
    } finally {
        if (in != null) {
            in.close();
            in = null;
        }
        if (outp != null) {
            outp.close();
            outp = null;
            out.clear();
            out = pageContext.pushBody();
        }
    }
%>

對於第二種方法,我認為應該是比較常用的。不過有幾個地方是值得我們注意的:

一、採用第二種方法的主要優點是實際檔案的存放路徑對客戶端來說是透明的。
這個檔案可以存在於任何你的伺服器能夠取得到的地方,而客戶端不一定能直接得到。

例如檔案來自於資料庫或者內部網路的一個FTP伺服器。還句話說,這種方式可以實現隱藏實際檔案的URL地址。

二、為了防止客戶端瀏覽器直接開啟目標檔案(例如在裝了MS Office套件的Windows中的IE瀏覽器可能就會直接在IE瀏覽器中開啟你想下載的doc或者xls檔案),你必須在響應頭裡加入強制下載的MIME型別:
response.setContentType("application/force-download");//設定為下載application/force-download
這樣,就可以保證在使用者點選下載連結的時候瀏覽器一定會彈出提示視窗來詢問你是下載還是直接開啟並允許你選擇要開啟的應用程式,除非你設定了瀏覽器的一些預設行為。

或者,你想讓客戶端自行處理各種不同的檔案型別,你可以在伺服器的配置檔案中配置MIME型別對映,通過簡單的判斷檔案字尾名來處理。例如,在Tomcat中設定MIME響應型別:
如果檔案在客戶端中的響應程式型別和期望不一致,修改$TOMCAT_HOME\conf\web.xml檔案中的如下部分 :
<mime-mapping>
  <extension>zip</extension>
  <mime-type>application/zip</mime-type>
</mime-mapping>
<mime-mapping>
  <extension>mht</extension>
  <mime-type>message/rfc822</mime-type>
</mime-mapping>
……

三、在響應頭中儘量不要設定瀏覽器快取期限。
有時候使用者在點選了下載連結後,在彈出視窗中,使用者想直接點選“開啟”,而不想儲存到指定路徑。這時候如果我們在響應頭中限制了不允許使用瀏覽器快取(即總是重新整理),在IE瀏覽器中我們將無法直接開啟該檔案。因為限制了不允許使用快取,瀏覽器無法將檔案儲存到臨時資料夾(即快取)。
也就是說,在響應頭中不要進行如下的設定(已註釋):
  //response.addHeader("pragma","NO-cache");
  //response.addHeader("Cache-Control","no-cache");
  //response.addDateHeader("Expries",0);

四、檔名為中文或其他unicode字元時的處理。
有時候提供下載的檔名中包含中文字元或者其他unicode字元,會導致瀏覽器無法正確的採用預設的檔名儲存檔案。我們應該記住在響應頭中包含filename欄位並採用ISO8859-1編碼(推薦)或者採用UTF-8編碼:
response.setHeader("Content-disposition","attachment; filename="+new String(filename.getBytes("UTF-8"),"iso8859-1")); //採用ISO8859-1編碼
response.setHeader("Content-disposition","attachment; filename="+URLEncoder.encode(filename, "UTF-8")); //採用UTF-8編碼
但是,這種方式在不同的瀏覽器中表現也有所不同。例如在IE和Firefox中,採用ISO8859-1編碼可以正確顯示檔名,而在Opera中不管採用那種編碼,預設儲存的檔名都無法做到正確顯示。
所以最好的方法其實就是儘量在檔名中使用ascii編碼。

五、由於採用流的方式進行輸入輸出,我們必須保證在使用完畢後關閉流的資源。