練功房推薦書單

  • 猛虎出柙雙劍合璧版:最新 OCA / OCP Java SE 7 Programmer 專業認證
  • 流浪教師存零股存到3000萬
  • SCJP 6.0認證教戰手冊(第二版)Oracle Certified Professional Java Programmer(附光碟)
  • 小資女艾蜜莉:我的資產翻倍存股筆記
JSP精選實用範例(三):檔案下載  RSS feed
Forum Index » 網頁程式設計 Web Development
Author Message
andowson

七段學員
[Avatar]

Joined: 2007-01-02 22:20:40
Messages: 705
Location: 台北
Offline
要被下載的檔案先寫入到一個特定的目錄下如/WEB-INF/export,然後呼叫download.jsp?file=targetFileName取得,下載後並將檔案刪除。
程式碼:
download.jsp
<%@ page import= "java.io.*" %>
<%!
     private static final int BUFSIZE = 2048;
	  /**
     *  Sends a file to the ServletResponse output stream.  Typically
     *  you want the browser to receive a different name than the
     *  name the file has been saved in your local database, since
     *  your local names need to be unique.
     *
     *  @param request The request
     *  @param response The response
     *  @param filename The name of the file you want to download.
     *  @param original_filename The name the browser should receive.
     */
    private void doDownload( HttpServletRequest request, HttpServletResponse response,
                             String filename, String original_filename )
        throws IOException
    {
        File                f        = new File(filename);
        int                 length   = 0;
        ServletOutputStream op       = response.getOutputStream();
        ServletContext      context  = getServletConfig().getServletContext();
        String              mimetype = context.getMimeType( filename );

        //
        //  Set the response and go!
        //
        //
        response.setContentType( (mimetype != null) ? mimetype : "application/octet-stream" );
        response.setContentLength( (int)f.length() );
        response.setHeader( "Content-Disposition", "attachment; filename=\"" + original_filename + "\"" );

        //
        //  Stream to the requester.
        //
        byte[] bbuf = new byte[BUFSIZE];
        DataInputStream in = new DataInputStream(new FileInputStream(f));

        while ((in != null) && ((length = in.read(bbuf)) != -1))
        {
            op.write(bbuf,0,length);
        }

        in.close();
        op.flush();
        op.close();
    }
%>
<%
	String original_filename = request.getParameter("file");
    // Security Isuue: User can type file=../WEB-INF/web.xml
    //String filename = application.getRealPath(original_filename);
    boolean error = false;
    if (original_filename != null && !"".equals(original_filename) && !original_filename.startsWith("../")) {
    	String filename = application.getRealPath("/")+"WEB-INF/export/" + original_filename;
    	File file = new File(filename);	
    	if (file.exists()) {
    	    doDownload(request, response, filename, original_filename);
    	    // delete the file after download
    	    boolean deleted = file.delete();
    	    System.out.println("File " + original_filename + " deleted: " + deleted);
    	} else {
    		error = true;
    	}
    } else {
    	error = true;
    }
    if (error) {
    	response.setContentType("text/html; charset=UTF-8");
		out.println("File not found: " + original_filename);
    }
%>    

參考資料:
http://www.jspwiki.org/wiki/MakingADownloadServlet
 Filename download.jsp [Disk] Download
 Description 檔案下載程式
 Filesize 3 Kbytes
 Downloaded:  183 time(s)


分享經驗 累積智慧
[WWW] [MSN]
lionsgogo

十級學員

Joined: 2010-03-07 20:11:11
Messages: 3
Offline
我有在WEB-INF/export建立一個資料夾

但是好像找不到檔案

出現"File not found: null"

是怎麼回事?
andowson

七段學員
[Avatar]

Joined: 2007-01-02 22:20:40
Messages: 705
Location: 台北
Offline
lionsgogo您好:
您應該是直接打download.jsp這樣子去執行吧?正確的使用方式是需要提供一個參數file來指定要下載的檔案名稱(位於WEB-INF/export目錄下),例如:download.jsp?file=test.csv

分享經驗 累積智慧
[WWW] [MSN]
mylipton

十級學員

Joined: 2010-05-22 23:28:23
Messages: 6
Offline
andowson您好
這範例程式如果下載中文檔名的檔案會有問題,自己試了一些方法,還是不成功
請問您有辦法可以讓他下載中文檔名的檔案嗎
andowson

七段學員
[Avatar]

Joined: 2007-01-02 22:20:40
Messages: 705
Location: 台北
Offline
mylipton您好:
要下載中文檔名的檔案也很簡單,只要將傳數的參數加以轉換編碼即可。
<%@ page import= "java.io.*" %>
<%!
     private static final int BUFSIZE = 2048;
	  /**
     *  Sends a file to the ServletResponse output stream.  Typically
     *  you want the browser to receive a different name than the
     *  name the file has been saved in your local database, since
     *  your local names need to be unique.
     *
     *  @param request The request
     *  @param response The response
     *  @param filename The name of the file you want to download.
     *  @param original_filename The name the browser should receive.
     */
    private void doDownload( HttpServletRequest request, HttpServletResponse response,
                             String filename, String original_filename )
        throws IOException
    {
        File                f        = new File(filename);
        int                 length   = 0;
        ServletOutputStream op       = response.getOutputStream();
        ServletContext      context  = getServletConfig().getServletContext();
        String              mimetype = context.getMimeType( filename );

        //
        //  Set the response and go!
        //
        //
        response.setContentType( (mimetype != null) ? mimetype : "application/octet-stream" );
        response.setContentLength( (int)f.length() );
        response.setHeader( "Content-Disposition", "attachment; filename=\"" + original_filename + "\"" );

        //
        //  Stream to the requester.
        //
        byte[] bbuf = new byte[BUFSIZE];
        DataInputStream in = new DataInputStream(new FileInputStream(f));

        while ((in != null) && ((length = in.read(bbuf)) != -1))
        {
            op.write(bbuf,0,length);
        }

        in.close();
        op.flush();
        op.close();
    }
%>
<%
	String original_filename = request.getParameter("file");
    String target_filename = ""; 

    // Security Isuue: User can type file=../WEB-INF/web.xml
    //String filename = application.getRealPath(original_filename);
    boolean error = false;
    if (original_filename != null && !"".equals(original_filename.trim()) && !original_filename.startsWith("../")) {
    	target_filename = new String(original_filename.getBytes("ISO-8859-1"), "MS950");
    	String filename = application.getRealPath("/")+"WEB-INF/export/" + target_filename;
    	File file = new File(filename);	
    	if (file.exists()) {
    	    doDownload(request, response, filename, original_filename);
    	    // delete the file after download
    	    //boolean deleted = file.delete();
    	    //System.out.println("File " + target_filename + " deleted: " + deleted);
    	} else {
    		error = true;
    	}
    } else {
    	error = true;
    }
    if (error) {
    	response.setContentType("text/html; charset=MS950");
		out.println("File not found: " + target_filename);
    }
%>
 Filename download.jsp [Disk] Download
 Description 支援中文檔名下載
 Filesize 3 Kbytes
 Downloaded:  34 time(s)


分享經驗 累積智慧
[WWW] [MSN]
mylipton

十級學員

Joined: 2010-05-22 23:28:23
Messages: 6
Offline
andowson您好
我試過你放的程式了,還是會因為中文變亂碼的問題而找不到檔案
我也試過一些編碼的設定
也是無法成功
mylipton

十級學員

Joined: 2010-05-22 23:28:23
Messages: 6
Offline
andowson 您好
我把ISO-88591-1 後面的UTF-8 改成big5之後可以下載中文檔名的檔案了,
但很奇怪,如果我把路徑"/WEB-INF/export" 改成別的路徑,就又會出現File not found
andowson

七段學員
[Avatar]

Joined: 2007-01-02 22:20:40
Messages: 705
Location: 台北
Offline
mylipton您好:
我今天測試了一下,發現不同的瀏覽器對於中文檔名直接輸入在網址列的處理有點不同,Google Chrome會自動把中文字改為UTF-8編碼,IE和Firefox則不會。
以下是我的測試方法,供您參考:
1.在WEB-INF/export目錄下手動產生一個文字檔,名稱就叫做「新增文字文件.txt」
2.在網址列上直接輸入:
http://localhost:8080/examples/download.jsp?file=新增文字文件.txt
修改後的程式碼如下:
<%@ page import= "java.io.*" %>
<%!
     private static final int BUFSIZE = 2048;
	  /**
     *  Sends a file to the ServletResponse output stream.  Typically
     *  you want the browser to receive a different name than the
     *  name the file has been saved in your local database, since
     *  your local names need to be unique.
     *
     *  @param request The request
     *  @param response The response
     *  @param filename The name of the file you want to download.
     *  @param original_filename The name the browser should receive.
     */
    private void doDownload( HttpServletRequest request, HttpServletResponse response,
                             String filename, String original_filename )
        throws IOException
    {
        File                f        = new File(filename);
        int                 length   = 0;
        ServletOutputStream op       = response.getOutputStream();
        ServletContext      context  = getServletConfig().getServletContext();
        String              mimetype = context.getMimeType( filename );

        //
        //  Set the response and go!
        //
        //
        response.setContentType( (mimetype != null) ? mimetype : "application/octet-stream" );
        response.setContentLength( (int)f.length() );
        response.setHeader( "Content-Disposition", "attachment; filename=\"" + original_filename + "\"" );

        //
        //  Stream to the requester.
        //
        byte[] bbuf = new byte[BUFSIZE];
        DataInputStream in = new DataInputStream(new FileInputStream(f));

        while ((in != null) && ((length = in.read(bbuf)) != -1))
        {
            op.write(bbuf,0,length);
        }

        in.close();
        op.flush();
        op.close();
    }
%>
<%
	String original_filename = request.getParameter("file");
    String target_filename = "";
    
    // Chrome will auto escape the URL characters
    String userAgent = request.getHeader("user-agent");
    System.out.println(userAgent);
    String charset = "MS950";
    if (userAgent.indexOf("Chrome") != -1) {
    	charset = "UTF-8";
    }

    // Security Isuue: User can type file=../WEB-INF/web.xml
    //String filename = application.getRealPath(original_filename);
    boolean error = false;
    if (original_filename != null && !"".equals(original_filename.trim()) && !original_filename.startsWith("../")) {
    	target_filename = new String(original_filename.getBytes("ISO-8859-1"), charset);
    	String filename = application.getRealPath("/")+"WEB-INF/export/" + target_filename;
    	File file = new File(filename);	
    	if (file.exists()) {
    	    doDownload(request, response, filename, original_filename);
    	    // delete the file after download
    	    //boolean deleted = file.delete();
    	    //System.out.println("File " + target_filename + " deleted: " + deleted);
    	} else {
    		error = true;
    	}
    } else {
    	error = true;
    }
    if (error) {
    	response.setContentType("text/html; charset=UTF-8");
		out.println("File not found: " + target_filename);
    }
%>

 Filename download.jsp [Disk] Download
 Description 修正後的檔案下載程式
 Filesize 3 Kbytes
 Downloaded:  34 time(s)


分享經驗 累積智慧
[WWW] [MSN]
andowson

七段學員
[Avatar]

Joined: 2007-01-02 22:20:40
Messages: 705
Location: 台北
Offline
mylipton wrote:andowson 您好
我把ISO-88591-1 後面的UTF-8 改成big5之後可以下載中文檔名的檔案了,
但很奇怪,如果我把路徑"/WEB-INF/export" 改成別的路徑,就又會出現File not found

這是設計上的問題,這個下載程式為了安全起見,將所要要下載的檔案都放在WEB-INF/export目錄下,您可以配合需要更改設計。例如您可以改為將檔案都放在WEB-INF/download目錄下,則程式中就應改為WEB-INF/download。

分享經驗 累積智慧
[WWW] [MSN]
mylipton

十級學員

Joined: 2010-05-22 23:28:23
Messages: 6
Offline
andowson
感謝你的回答,之前的問題已經解決了
但想請問一下,如果我想把上傳檔案傳到別的槽的資料夾改怎麼作
比如我的網站是放在C槽,但傳上來的檔案我想放到D槽
andowson

七段學員
[Avatar]

Joined: 2007-01-02 22:20:40
Messages: 705
Location: 台北
Offline
要避免發生java.lang.IllegalStateException: getOutputStream() has already been called for this response,可以在doDownload()後面加上下面兩行
out.clear();
out = pageContext.pushBody();
修改後的原始程式碼如下:
download.jsp
<%@ page import="java.io.*"%>
<%!
    private static final int BUFSIZE = 2048;
    /**
     *  Sends a file to the ServletResponse output stream.  Typically
     *  you want the browser to receive a different name than the
     *  name the file has been saved in your local database, since
     *  your local names need to be unique.
     *
     *  @param request The request
     *  @param response The response
     *  @param filename The name of the file you want to download.
     *  @param original_filename The name the browser should receive.
     */
    private void doDownload( HttpServletRequest request, HttpServletResponse response,
                             String filename, String original_filename )
        throws IOException
    {
        File                f        = new File(filename);
        int                 length   = 0;
        ServletOutputStream op       = response.getOutputStream();
        ServletContext      context  = getServletConfig().getServletContext();
        String              mimetype = context.getMimeType( filename );

        //
        //  Set the response and go!
        //
        //
        response.setContentType( (mimetype != null) ? mimetype : "application/octet-stream" );
        response.setContentLength( (int)f.length() );
        response.setHeader( "Content-Disposition", "attachment; filename=\"" + original_filename + "\"" );

        //
        //  Stream to the requester.
        //
        byte[] bbuf = new byte[BUFSIZE];
        DataInputStream in = new DataInputStream(new FileInputStream(f));

        while ((in != null) && ((length = in.read(bbuf)) != -1))
        {
            op.write(bbuf,0,length);
        }

        in.close();
        op.flush();
        op.close();
    }
%>
<%
    String original_filename = request.getParameter("file");
    String target_filename = "";
    
    // Chrome will auto escape the URL characters
    String userAgent = request.getHeader("user-agent");
    System.out.println(userAgent);
    String charset = "MS950";
    if (userAgent.indexOf("Chrome") != -1) {
    	charset = "UTF-8";
    }

    // Security Isuue: User can type file=../WEB-INF/web.xml
    //String filename = application.getRealPath(original_filename);
    boolean error = true;
    if (original_filename != null && !"".equals(original_filename.trim()) && !original_filename.startsWith("../")) {
    	target_filename = new String(original_filename.getBytes("ISO-8859-1"), charset);
    	String filename = application.getRealPath("/")+"WEB-INF/export/" + target_filename;
    	File file = new File(filename);	
    	if (file.exists()) {
    	    doDownload(request, response, filename, original_filename);
    	    // aviod java.lang.IllegalStateException: getOutputStream() has already been called for this response
    	    out.clear();
            out = pageContext.pushBody();
    	    // delete the file after download
    	    //boolean deleted = file.delete();
    	    //System.out.println("File " + target_filename + " deleted: " + deleted);
    	    error = false;
    	} 
    } 
    if (error) {
    	response.setContentType("text/html; charset=UTF-8");
    	out.println("File not found: " + target_filename);
    }
%>
 Filename download.jsp [Disk] Download
 Description 避免產生java.lang.IllegalStateException: getOutputStream() has already been called for this response
 Filesize 3 Kbytes
 Downloaded:  89 time(s)


分享經驗 累積智慧
[WWW] [MSN]
kisskevin524

十級學員

Joined: 2010-05-25 10:23:59
Messages: 3
Offline
我想請問一下
我下載功能都正常
下載時會出現"開啟舊檔","儲存","取消"畫面
當我點選"開啟舊檔","儲存"都沒問題
但是我選擇"取消"時,tomcat後端會顯示
java.lang.IllegalStateException: getOutputStream() has already been called for this response

不知如何解決/
謝謝!!
andowson

七段學員
[Avatar]

Joined: 2007-01-02 22:20:40
Messages: 705
Location: 台北
Offline
kisskevin524您好:
我用#11那篇的download.jsp在Windows Vista上跑Tomcat 7.0.12上測試,沒遇到您所說的問題,請再確認一下環境跟您使用的版本。

分享經驗 累積智慧
[WWW] [MSN]
Siao

十級學員

Joined: 2013-01-24 14:39:56
Messages: 2
Offline
andowson 你好,我最近在寫jsp下載時遇到一個問題。點選連結之後他不會跳出下載視窗,而是直接在原本的頁面上顯示檔案內容。我該怎麼解決呢?
Siao

十級學員

Joined: 2013-01-24 14:39:56
Messages: 2
Offline
andowson你好我稍早所描述的問題已經解決了,不好意思打擾你了,感恩!!
 
Forum Index » 網頁程式設計 Web Development
Go to: