練功房推薦書單

  • 猛虎出柙雙劍合璧版--最新 OCA / OCP Java SE 7 Programmer 專業認證 (電子書)
  • 流浪教師存零股存到3000萬(全新增修版)(書+DVD)
  • 開始在關西自助旅行(京都‧大阪‧神戶‧奈良)(全新增訂版)
  • 不敗教主的300張股票存股術

JSP精選實用範例(三):檔案下載 RSS feed
Forum Index » 網頁程式設計 Web Development
Author Message
andowson

七段學員
[Avatar]

Joined: 2007/1/2
Messages: 710
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:  185 time(s)


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

十級學員

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

但是好像找不到檔案

出現"File not found: null"

是怎麼回事?
andowson

七段學員
[Avatar]

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

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

十級學員

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

七段學員
[Avatar]

Joined: 2007/1/2
Messages: 710
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:  36 time(s)


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

十級學員

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

十級學員

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

七段學員
[Avatar]

Joined: 2007/1/2
Messages: 710
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:  35 time(s)


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

七段學員
[Avatar]

Joined: 2007/1/2
Messages: 710
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]
mylipton

十級學員

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

七段學員
[Avatar]

Joined: 2007/1/2
Messages: 710
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:  92 time(s)


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

十級學員

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

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

七段學員
[Avatar]

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

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

十級學員

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

十級學員

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