Struts 1.2.9下载CSV后触发Response already committed异常求助
问题:Struts 1.2.9 CSV下载后触发Response already committed异常
场景
基于JSP和Struts 1.2.9开发的Java应用中添加CSV下载功能,流程为从数据库取数生成CSV,UI按钮已映射到自定义DownloadCSVAction类。当前CSV下载可正常完成,但下载后点击UI其他元素会抛出java.lang.IllegalStateException: UT010019: Response already committed异常。
相关代码
public class DownloadCSVAction extends AbstractAction { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception{ // 从数据库获取数据的逻辑 response.setContentType("text/csv"); DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); String currentTime = format.format(new Date()); String headerKey = "Content-Disposition"; String headerValue = "attachment; filename=report_" + currentTime + ".csv"; response.setHeader(headerKey, headerValue); PrintWriter printWriter = response.getWriter(); // 将内容转为CSV格式并写入的逻辑 printWriter.close(); // 尝试移除下载响应头,重置内容类型 response.setContentType("text/html"); response.setHeader(headerKey, ""); return mapping.findForward("success"); } }
报错栈信息
12:14:37,216 ERROR [io.undertow.request] (default task-13) UT005023: Exception handling request to /SecurityLibrary/DownloadCSVInit.do: java.lang.IllegalStateException: UT010019: Response already commited at io.undertow.servlet.spec.ServletOutputStreamImpl.resetBuffer(ServletOutputStreamImpl.java:732) at io.undertow.servlet.spec.HttpServletResponseImpl.resetBuffer(HttpServletResponseImpl.java:495) at io.undertow.servlet.spec.RequestDispatcherImpl.forwardImpl(RequestDispatcherImpl.java:167) at io.undertow.servlet.spec.RequestDispatcherImpl.forwardImplSetup(RequestDispatcherImpl.java:147) at io.undertow.servlet.spec.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:111) at org.apache.struts.action.RequestProcessor.doForward(RequestProcessor.java:1085) at ...MainRequestProcessor.processForwardConfig(MainRequestProcessor.java:345) at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:241) at ...MainRequestProcessor.process(MainRequestProcessor.java:78) at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196) at ...MainServlet.process(MainServlet.java:101) at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131) at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50) at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292) at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81) at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138) at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135) at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105) at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508) at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508) at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508) at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508) at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272) at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81) at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104) at io.undertow.server.Connectors.executeRootHandler(Connectors.java:326) at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:812)
已尝试但无效的方案
- 尝试解决“响应提交后无法转发”的常规方案
- 尝试下载后跳转页面的相关方案
- 参考JSP文件下载的常规教程
解决方案
问题核心:调用printWriter.close()后,HTTP响应已完全提交,此时修改响应头、返回success转发都会触发异常——因为Struts会尝试对已提交的响应进行后续处理(比如转发到JSP),这违反了Servlet规范。
修改后代码:
public class DownloadCSVAction extends AbstractAction { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception{ // 从数据库获取数据的逻辑 response.setContentType("text/csv"); DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); String currentTime = format.format(new Date()); String headerKey = "Content-Disposition"; String headerValue = "attachment; filename=report_" + currentTime + ".csv"; response.setHeader(headerKey, headerValue); // 禁用缓存,避免浏览器缓存旧文件 response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); PrintWriter printWriter = response.getWriter(); // 将内容转为CSV格式并写入的逻辑 printWriter.flush(); printWriter.close(); // 关键:返回null,告知Struts无需执行后续转发/重定向操作 return null; } }
修改原因
- 返回null跳过后续处理:返回
null会让Struts的RequestProcessor跳过转发逻辑,避免对已提交的响应进行无效操作。 - 移除无效的响应头修改:响应提交后修改
Content-Type或清空Content-Disposition完全无效,直接删除此类代码。 - 添加缓存控制头:确保每次下载的都是最新数据,避免浏览器缓存旧CSV文件。
额外注意:UI端的下载按钮请使用独立请求(如普通表单提交、新窗口打开),不要用AJAX——AJAX处理文件下载本身存在局限性,还可能导致后续请求状态异常。
内容的提问来源于stack exchange,提问作者Anirudh Giran
相关产品推荐
相关产品推荐

