Trinidad中导出文件异常JSF导出文件异常getOutputStream() has already been called的解决方法...

使用Trinidad导出CSV/PDF文件时,会遇到getOutputStream() has already been called的异常,其原因是JSF在导出文件的时候会调用response.getWriter()方法,而在此之前backingbean中调用了response.getOutputStream()方法。两者会有冲突。看一下org.apache.catalina.connector.Response.java的源代码就知道,如果调用了getOutputStream() 方法,则usingOutputStream=true,再调用getWriter()方法就会抛异常,代码片断如下:

609 if (usingOutputStream)
610 throw new IllegalStateException
611 (sm.getString("coyoteResponse.getWriter.ise"));

出现这种异常的场景如下:

使用Trinidad中的控件导出CSV文件
==========.xhtml=======================================
...
<tr:commandButton icon="../images/csv.jpg" shortDesc="Export to CSV">
<tr:fileDownloadActionListener
contentType="text/csv; charset=utf-8" filename="content.csv"
method="#{backingBean.exportCsv}" />
</tr:commandButton>
...
==========================
=========backingBean===================================
public final void sendContent(final FacesContext context,
final OutputStream out,
final UIXTable table)
throws IOException {
OutputStreamWriter outw = new OutputStreamWriter(out, "UTF-8");

Object rowKey = table.getRowKey();
for (int i = 0; i < table.getRowCount(); i++) {
table.setRowIndex(i);
if (!table.isRowAvailable()) {
break;
}
exportRow(table, outw);
}

outw.flush();

table.setRowKey(rowKey);
}


====================================
after exporting csv file, there is an error on console like this:
=============================================================
SEVERE: Servlet.service() for servlet Spring Dispatcher Servlet threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:610)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)
at com.sun.facelets.FaceletViewHandler.createResponseWriter(FaceletViewHandler.java:400)
at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:557)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:189)
at org.apache.myfaces.trinidadinternal.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:176)
at org.springframework.faces.webflow.JsfView.render(JsfView.java:94)
at org.springframework.webflow.engine.ViewState.render(ViewState.java:245)
at org.springframework.webflow.engine.ViewState.resume(ViewState.java:204)
at org.springframework.webflow.engine.Flow.resume(Flow.java:545)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:262)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:163)
at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:173)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:807)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)
......

在导出CSV的时候,可以用一个替代方案,就是用Writer代替OuputStream,这样就不会有异常了。
但如果我们使用iText导出PDF文件,就必须使用OutputStream,所以也会有上述异常出现。
对于这种情况有一个折中的解决方案, that is, call servlet in webflow, then export pdf in servlet.
the sevlet is independant on Webflow and JSF, so the getWriter() mothod won't be called by the container.

the solution is like below
========================================
listView.xhtml
========================================
<tr:commandLink action="exportPdf">
<tr:image source="../images/pdf.jpg" shortDesc="Export to PDF"/>
</tr:commandLink>

===============================================
webflow.xml
===============================================

<view-state id="listView">

...
<transition on="exportPdf" to="pdfView" >
<evaluate expression="backingBean.saveTable()" />
</transition>
</view-state>
...
<view-state id="pdfView" view="externalRedirect:contextRelative:/exportPdfAction" >
<transition to="txnHistoryListView" />
</view-state>

================================================
in the saveTable() method of backingbean, we can put tabel value and related value into HTTPSession like this.
===========================================
backingbean.java
===========================================
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
attributes.setAttribute("key", tableValue, RequestAttributes.SCOPE_SESSION);

=================================================
"externalRedirect:contextRelative:/exportPdfAction" configured in webflow.xml will navigator to
the servlet configured in web.xml

in servlet, we can get data from session and invoke method to export pdf file.

you should pay attention to one thing, that is, you can't pass UIXTable object into the session.
Because out of JSF lifecycle, we can't get label value from the UIXTable object.
I guess this is related to the FacesContext issue.
so you should wrap the data in UIXTable into your own object and save it into session.


原帖地址:
http://jsfgroup.group.iteye.com/group/topic/9959?page=2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值