导出到Excel中使用JSF 2和XSLT(Export to Excel using JSF 2

2019-10-18 12:55发布

我们使用Java 5中,Tomcat的5,的Xalan和JSF 1构建使用XSLT,XML和Tomcat的过滤器可以使用户将他们的数据导出为Excel格式的应用程序。 我们最近升级到Java 1.7.0_07,Tomcat的7.022和2.1 JSF(JSF-API-2.1.0-b03.jar)。 由于涉及我们还没有升级到小面的努力; 我们还是用JSP的。 我们使用的标签,以显示自己的弹出窗口中的Excel报表。 问题是,在升级后弹出现在显示在IE原始XML,而不是直接在Excel中的弹出孔。 原始的XML可以从浏览器保存到一个文件,如果保存的文件被双击,但它在Excel中正确地打开,但它是最好的,如果用户可以避开变通。

我认为,问题是,在JSF 2的响应现在正在致力于较早比它在JSF 1.我们的web.xml文件中定义了以下过滤器为Tomcat:

  <filter>
    <filter-name>XSLT Processor</filter-name>
    <filter-class>com.cs.common.jsf.util.XsltProcessorFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>XSLT Processor</filter-name>
    <url-pattern>*.xml</url-pattern>
  </filter-mapping>

  <filter>
    <filter-name>Hibernate Session Manager</filter-name>
    <filter-class>com.cs.common.hibernate.HibernateSessionServletFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>Hibernate Session Manager</filter-name>
    <url-pattern>*.faces</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>Hibernate Session Manager</filter-name>
    <url-pattern>*.xml</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.faces</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xml</url-pattern>
  </servlet-mapping>

而我们XsltProcesserFilter类包含以下几行:

fChain.doFilter(request, wrapper);
response.setContentType("application/vnd.ms-excel");

通过使用sysouts ,我确定contentType没有被JSF 2下设定,大概是因为响应已经提交。 我已经尝试设置contentType在输出XML中的JSP,JSF却抛出然后很多错误,所以可能我需要(在过滤器上面的一样),将其设在这个过程中。 我曾尝试response.setBufferSize(6400000)之前doFilterXsltProcessorFilter ,因为我已阅读,这样做可能会延迟提交,但这并不解决任何问题。

我如何设置contentType为application / vnd.ms-Excel的面孔已经完成了它的处理,但之前提交,以便浏览器将打开到Excel后?

Answer 1:

上述问题的解决方案涉及到两个问题。 第一个问题是,Tomcat的被刷新缓冲区并承诺回报XSLTProcessorFilter之前的响应。 这是通过在XSLTProcessorFilter移交控制权交给面之前的缓冲器大小设置为大的值克服。 接下来,面类JspViewHandlingStrategy在两个点刷新输出。 这通过将isExcelXML的请求属性与“真”从XSLTProcessorFilter值克服。 然后,在JspViewHandlingStrategy编码加入以检查isXML属性,如果它的价值是真实的,冲洗被绕过。 这些更改后,Excel窗口现在呈现给所需的格式用户。 当然,人们可以简单地注释掉JspViewHandlingStrategy的两次冲洗,但想必他们服务于某种目的,所以这里的解决方案只有绕过他们,如果他们造成手头的问题(当XSLTProcessorFilter被调用)。

我们XSLTProcessorFilter的doFilter方法现在包含的修补程序:

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain fChain) throws IOException, ServletException {
        String xslTemplatePath = request.getParameter(XSLT_REQUEST_PARAM);
        File xslTemplate = (xslTemplatePath == null) ? null : new File(servletPath, xslTemplatePath);

        if ((xslTemplatePath != null) && xslTemplate.exists()) {
            // Run the standard request processing to obtain the XML output
            CharacterResponseWrapper wrapper =  new CharacterResponseWrapper((HttpServletResponse) response);
            response.setBufferSize(6400000); // This line overcomes Tomcat buffer flushing
            request.setAttribute("isExcelXML", "true"); // This line signals to JSF to bypass the flushing
            fChain.doFilter(request, wrapper);
            response.setContentType("application/vnd.ms-excel");

// Transform the XML using the XSL stylesheet specified on the URL
            Source xmlSource = new StreamSource(new StringReader(wrapper.toString()));
            StreamSource xslSource = new StreamSource(xslTemplate);

            try {
                Transformer transformer = tFactory.newTransformer(xslSource);
                StreamResult out = new StreamResult(response.getWriter());              
                transformer.transform(xmlSource, out);

            } catch (Throwable t) {
                t.printStackTrace(response.getWriter());
            }

        } else { // standard processing
            fChain.doFilter(request, response);
        }
    }

JspViewHandlingStrategy类的改变的部分是在它的端部的公共无效的RenderView(FacesContext的上下文中,UIViewRoot视图)方法:

//For XML output to Excel, bypass later flushings
boolean bypassFlush = false;
if (((ServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getAttribute("isExcelXML")!=null
&& ((ServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getAttribute("isExcelXML").toString().equals("true")) {
    bypassFlush = true;
}

    // write any AFTER_VIEW_CONTENT to the response (This comment in original JSF file)
    // side effect: AFTER_VIEW_CONTENT removed (This comment in original JSF file)
    ViewHandlerResponseWrapper wrapper = (ViewHandlerResponseWrapper)
          RequestStateManager.remove(context, RequestStateManager.AFTER_VIEW_CONTENT);
    if (null != wrapper && !bypassFlush) { //fix to Excel issue involved bypassing flush if isExcelXML set to true
        wrapper.flushToWriter(extContext.getResponseOutputWriter(),
                              extContext.getResponseCharacterEncoding());
    }

    if (!bypassFlush) { //fix to Excel issue involved bypassing flush if isExcelXML set to true
        extContext.responseFlushBuffer();
}

由于这需要几周时间来解决,它可能是值得包括如何在其他情况下可调试对使用的技术中使用的一些细节。

调试开始下载JSF的源代码。 的System.out,放入每个源代码文件的println()语句在其服务方法开始FacesServlet.java。 调试的目的是看看那里的isCommitted布尔从“假”到“真”切换,因为那是什么导致了问题。 类似于以下语句贯穿每个分析源代码文件被使用:

System.out.println("someClass someLineId - " + someObjectReference.getClass().getName() +
    ((ServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).isCommitted() +
    ((ServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getContentType());

(请注意,“进口javax.servlet.ServletResponse;”通常不得不被添加到源文件中的import语句)一旦行被发现,其中isCommitted值后,才假的,真实的,那么调试努力切换到被称为由该行的实例化类。 该过程持续进行,直至有问题的缓冲冲洗管线被发现。

当然,刚才描述的变化必须从项目运行发现问题。 每个源文件必须编译(与载有最终项目的许多罐类路径)。 一旦类被编译,原来的脸部罐子改名为zip文件并通过覆盖该文件已经在那里的版本,新编译的类把zip文件里面。 然后将压缩文件改名回到罐子。 然后将罐子投入Eclipse中,重新编译该项目,该项目运行。 观察Tomcat的输出窗口上输出。 当多个类是由于单个编译(如可以用内部类发生),那么所有新编译的类放入自己的位置。 (虽然这可能没有必需的。)



文章来源: Export to Excel using JSF 2 and XSLT