解决springmvc中文件下载功能中使用javax.servlet.ServletOutputStream out = response.getOutputStream();后运行出异常但结果正确的问题
问题描述:
在springmvc中实现文件下载功能一般都会使用javax.servlet.ServletOutputStream out = response.getOutputStream();封装下载的工具类,稍后我会分享该工具类。
当使用了response.getOutputStream()后,由于在同一个请求中JSP或Servlet中同时调用了Response的getWriter和getOutputStream就会抛此异常,异常部分代码如下:
严重: Servlet.service() for servlet jsp threw exception java.lang.IllegalStateException: getOutputStream() has already been called for this response at org.apache.catalina.connector.Response.getWriter(Response.java:678) at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:213) at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:104) at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:125) at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:118) at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:186) at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:126) at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:80) at org.apache.jsp.download_jsp._jspService(download_jsp.java:103) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:395) at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:339) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
网上有的解决办法都是基于jsp中有java代码的,但是如果想在springmvc的controller中使用时难免不太对应,经过我的摸索,由于下载文件大多数情况况下都是留在原页面,那么我试了一下将返回值设为void,结果巧妙解决了以上问题,特以此记录。
测试的下载工具类如下:;
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class DownloadFile { /** * 下载文件,file 为文件位置 */ public static void downloadFile(String file, HttpServletRequest request, HttpServletResponse response) { try { File tempFile = new File(file.trim()); String fileName = tempFile.getName(); InputStream is = new FileInputStream(file); response.reset(); // 必要地清除response中的缓存信息 request.setCharacterEncoding("UTF-8"); response.setContentType("application/octet-stream; charset=utf-8"); response.setHeader("Content-disposition", "attachment;" + UserAgentUtil.encodeFileName(request, fileName)); //response.setHeader("Content-Length", String.valueOf(fileLength)); /*response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));*/ javax.servlet.ServletOutputStream out = response.getOutputStream(); byte[] content = new byte[1024]; int length = 0; while ((length = is.read(content)) != -1) { out.write(content, 0, length); } out.flush(); out.close();
is.close(); } catch (Exception e) { e.printStackTrace(); } } }
测试的controller层代码如下:
@RequestMapping(value = "/download.do") public void downloadDetailPrice(//@RequestParam String id, HttpServletRequest request, HttpServletResponse response) { String file = "D:\a.txt"; DownloadFile.downloadFile(file,request,response); }
浏览器客户端防乱码工具类:
import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import javax.mail.internet.MimeUtility; import javax.servlet.http.HttpServletRequest; /** @author pangchao E-mail: pangchao620@163.com * @date : 2016年2月24日 上午11:27:26 * @Description : 文件下载时浏览器端防乱码编码工具类 * @version 1.0 */ public class UserAgentUtil { public static String encodeFileName(HttpServletRequest request, String fileName) { String userAgent = request.getHeader("User-Agent"); String rtn = ""; try { String new_filename = URLEncoder.encode(fileName, "UTF8"); // 如果没有UA,则默认使用IE的方式进行编码,因为毕竟IE还是占多数的 rtn = "filename="" + new_filename + """; if (userAgent != null) { userAgent = userAgent.toLowerCase(); // IE浏览器,只能采用URLEncoder编码 if (userAgent.indexOf("msie") != -1) { rtn = "filename="" + new_filename + """; } // Opera浏览器只能采用filename* else if (userAgent.indexOf("opera") != -1) { rtn = "filename*=UTF-8''" + new_filename; } // Safari浏览器,只能采用ISO编码的中文输出 else if (userAgent.indexOf("safari") != -1) { rtn = "filename="" + new String(fileName.getBytes("UTF-8"), "ISO8859-1") + """; } // Chrome浏览器,只能采用MimeUtility编码或ISO编码的中文输出 else if (userAgent.indexOf("applewebkit") != -1) { new_filename = MimeUtility.encodeText(fileName, "UTF8", "B"); rtn = "filename="" + new_filename + """; } // FireFox浏览器,可以使用MimeUtility或filename*或ISO编码的中文输出 else if (userAgent.indexOf("mozilla") != -1) { rtn = "filename*=UTF-8''" + new_filename; } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return rtn; } }