<%-- 有些朋友询问使用 JSP Smart 下载文件的时候报错, 这里给出一个测试过的不 需要使用 JSP Smart 的 JSP 页面中进行文件下载的代码(改 Servlet 或者 JavaBean 的话自己改吧), 支持中文附件名(做了转内码处理). 事实上只要向 out 输出字节就被认为是附件内容, 不一定非要从文件读取原始数据, 从数据 库中读取也可以的. 测试结果: Tomcat 5.0, 5.5, Resin 3.0.18 , Weblogic 8.1, 9.2 测试通过, 无异常产生 --%> <% @ page contentType = " text/html; charset=GBK " pageEncoding = " GBK " %> <% @ page import = " java.io.*, java.util.*, java.text.* " %> <%! /** * If returns true, then should return a 304 (HTTP_NOT_MODIFIED) */ public static boolean checkFor304( HttpServletRequest req, File file ) { // // We'll do some handling for CONDITIONAL GET (and return a 304) // If the client has set the following headers, do not try for a 304. // // pragma: no-cache // cache-control: no-cache // if ( " no-cache " .equalsIgnoreCase(req.getHeader( " Pragma " )) || " no-cache " .equalsIgnoreCase(req.getHeader( " cache-control " ))) { // Wants specifically a fresh copy } else { // // HTTP 1.1 ETags go first // String thisTag = Long.toString(file.lastModified()); String eTag = req.getHeader( " If-None-Match " ); if ( eTag != null ) { if ( eTag.equals(thisTag) ) { return true ; } } // // Next, try if-modified-since // DateFormat rfcDateFormat = new SimpleDateFormat( " EEE, dd MMM yyyy HH:mm:ss z " ); Date lastModified = new Date(file.lastModified()); try { long ifModifiedSince = req.getDateHeader( " If-Modified-Since " ); // log.info("ifModifiedSince:"+ifModifiedSince); if ( ifModifiedSince != - 1 ) { long lastModifiedTime = lastModified.getTime(); // log.info("lastModifiedTime:" + lastModifiedTime); if ( lastModifiedTime <= ifModifiedSince ) { return true ; } } else { try { String s = req.getHeader( " If-Modified-Since " ); if ( s != null ) { Date ifModifiedSinceDate = rfcDateFormat.parse(s); // log.info("ifModifiedSinceDate:" + ifModifiedSinceDate); if ( lastModified.before(ifModifiedSinceDate) ) { return true ; } } } catch (ParseException e) { // log.warn(e.getLocalizedMessage(), e); } } } catch ( IllegalArgumentException e ) { // Illegal date/time header format. // We fail quietly, and return false. // FIXME: Should really move to ETags. } } return false ; } %> <% // String filePath = "c:/文档.doc"; // 如果是 WEB APP 下的相对路径文件, 请使用下列代码: String filePath = application.getRealPath( " 测试文档.htm " ); boolean isInline = false ; // 是否允许直接在浏览器内打开(如果浏览器能够预览此文件内容, // 那么文件将被打开, 否则会提示下载) // 清空缓冲区, 防止页面中的空行, 空格添加到要下载的文件内容中去 // 如果不清空的话在调用 response.reset() 的时候 Tomcat 会报错 // java.lang.IllegalStateException: getOutputStream() has already been called for // this response, out.clear(); // {{{ BEA Weblogic 必读 // 修正 Bea Weblogic 出现 "getOutputStream() has already been called for this response"错误的问题 // 关于文件下载时采用文件流输出的方式处理: // 加上response.reset(),并且所有的%>后面不要换行,包括最后一个; // 因为Application Server在处理编译jsp时对于%>和<%之间的内容一般是原样输出,而且默认是PrintWriter, // 而你却要进行流输出:ServletOutputStream,这样做相当于试图在Servlet中使用两种输出机制, // 就会发生:getOutputStream() has already been called for this response的错误 // 详细请见《More Java Pitfill》一书的第二部分 Web层Item 33:试图在Servlet中使用两种输出机制 270 // 而且如果有换行,对于文本文件没有什么问题,但是对于其它格式,比如AutoCAD、Word、Excel等文件 // 下载下来的文件中就会多出一些换行符0x0d和0x0a,这样可能导致某些格式的文件无法打开,有些也可以正常打开。 // 同时这种方式也能清空缓冲区, 防止页面中的空行等输出到下载内容里去 response.reset(); // }}} try { java.io.File f = new java.io.File(filePath); if (f.exists() && f.canRead()) { // 我们要检查客户端的缓存中是否已经有了此文件的最新版本, 这时候就告诉 // 客户端无需重新下载了, 当然如果不想检查也没有关系 if ( checkFor304( request, f ) ) { // 客户端已经有了最新版本, 返回 304 response.sendError( HttpServletResponse.SC_NOT_MODIFIED ); return ; } // 从服务器的配置来读取文件的 contentType 并设置此contentType, 不推荐设置为 // application/x-download, 因为有时候我们的客户可能会希望在浏览器里直接打开, // 如 Excel 报表, 而且 application/x-download 也不是一个标准的 mime type, // 似乎 FireFox 就不认识这种格式的 mime type String mimetype = null ; mimetype = application.getMimeType( filePath ); if ( mimetype == null ) { mimetype = " application/octet-stream;charset=ISO8859-1 " ; } response.setContentType( mimetype ); // IE 的话就只能用 IE 才认识的头才能下载 HTML 文件, 否则 IE 必定要打开此文件! String ua = request.getHeader( " User-Agent " ); // 获取终端类型 if (ua == null ) ua = " User-Agent: Mozilla/4.0 (compatible; MSIE 6.0;) " ; boolean isIE = ua.toLowerCase().indexOf( " msie " ) != - 1 ; // 是否为 IE if (isIE && ! isInline) { mimetype = " application/x-msdownload " ; } // 下面我们将设法让客户端保存文件的时候显示正确的文件名, 具体就是将文件名 // 转换为 ISO8859-1 编码 String downFileName = new String(f.getName().getBytes(), " ISO8859-1 " ); String inlineType = isInline ? " inline " : " attachment " ; // 是否内联附件 // or using this, but this header might not supported by FireFox // response.setContentType("application/x-download"); response.setHeader ( " Content-Disposition " , inlineType + " ;filename=\ "" + downFileName + " \ "" ); response.setContentLength(( int ) f.length()); // 设置下载内容大小 byte [] buffer = new byte [ 4096 ]; // 缓冲区 BufferedOutputStream output = null ; BufferedInputStream input = null ; // try { output = new BufferedOutputStream(response.getOutputStream()); input = new BufferedInputStream( new FileInputStream(f)); int n = ( - 1 ); while ((n = input.read(buffer, 0 , 4096 )) > - 1 ) { output.write(buffer, 0 , n); } response.flushBuffer(); } catch (Exception e) { } // 用户可能取消了下载 finally { if (input != null ) input.close(); if (output != null ) output.close(); } } return ; } catch (Exception ex) { // ex.printStackTrace(); } // 如果下载失败了就告诉用户此文件不存在 response.sendError( 404 ); %> 来自:http://www./beansoft/archive/2007/02/01/97294.html
|