【问题标题】:Getting 'Cannot set header. Response already committed' with primefaces fileupload获取'无法设置标题。已使用 primefaces 文件上传响应已提交
【发布时间】:2014-02-27 13:49:46
【问题描述】:

我在 websphere 8.5.0.1 上使用 jsf 2 (javax.faces-2.0.10.jar) 和 primefaces 3.5,我我也在使用文件上传 jars:

  • commons-fileupload-1.3.1.jar
  • commons-io-2.4.jar

我正在尝试使用 primefaces 文件上传组件,如下所示:

<h:form  id="frm" enctype="multipart/form-data">
        <p:fileUpload id="fileUpload" value="#{uploadDocument.file}"
        fileUploadListener="#{uploadDocument.handleFileUpload}" mode="advanced" dragDropSupport="false"  
                   sizeLimit="10000000" fileLimit="3"  /> 
        </h:form>

web.xml 配置:

<filter>
       <filter-name>PrimeFaces FileUpload Filter</filter-name>
       <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
       <init-param>
            <param-name>uploadDirectory</param-name>
            <param-value>C:/uploadFolder</param-value>
       </init-param>
    </filter>

    <filter-mapping>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</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>*.jsf</url-pattern>
    </servlet-mapping>

选择文件并单击上传后,文件未上传,我在日志文件中收到以下警告:

com.ibm.ws.webcontainer.srt.SRTServletResponse addHeader SRVE8094W: WARNING: Cannot set header. Response already committed.

请告知为什么文件上传不起作用?

【问题讨论】:

  • 很可能是一个错误。尝试升级您的 JSF 和 PF 版本
  • 您在web.xml 中有另一个Servlet Filter 配置?

标签: jsf-2 primefaces websphere-8


【解决方案1】:

显然,您的响应在抛出异常的行之前已提交。我还没有看到您那里的具体示例,但是问题很常见,并且有一种通用的方法可以追踪这些问题。某些内容在您预期之前被写入您的响应,但您看到的堆栈跟踪是用于稍后尝试添加到该响应的事件。因此,解决方案是在提交响应时记录一个堆栈跟踪,然后在稍后抛出异常时,也打印第一个堆栈跟踪。

我手头没有用于执行此操作的代码,因为我是为以前的公司编写的。我在下面提供了一个大纲解决方案,它显示了它的要点 - 您需要包装响应,以及它返回的任何 servletoutputstream/printwriter 对象以检测提交事件。我已经展示了如何通过使用自省来做到这一点。但如果您愿意,可以为流/编写器使用 ServletResponseWrapper 和自定义包装器类。事实上,你不需要像我在这里包装那么多的方法。

在我们的“真实”代码中,我记录了堆栈跟踪并仅在问题发生时打印它,如上所述,但下面的代码仅记录响应提交时,您可以通过查看日志来确定哪个是哪个。

public class BadCommitFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res,
        FilterChain chain) throws IOException, ServletException {

        HttpServletResponse response = (HttpServletRequest) res;
        response=Proxy.newProxyInstance(this.getClass().getClassLoader(),
                new Class[] { HttpServletResponse.class },
                new ResponseInvocationHandler(response, response));
        chain.doFilter(req, response);
    }
    private static class ResponseInvocationHandler<T> implements InvocationHandler {
      final T proxied;
      final HttpServletResponse response;

      public ResponseInvocationHandler(T proxied, HttpServletResponse response) {
        this.proxied = proxied;
        this.response = response;
      }

      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // in the 'real' code, I checked against a threadlocal
        // at the end only, so there was no possibility that I'd
        // miss out methods.
        boolean isCommitted = res.isCommitted();
        try {
          Object ret = method.invoke(proxied, args);
          if (ret instanceof PrintWriter) {
            Proxy.newProxyInstance(ret.getClass().getClassLoader(),
                new Class[] { PrintWriter.class },
                new ResponseInvocationHandler(ret, response));
          } else if (ret instanceof ServletOutputStream) {
            Proxy.newProxyInstance(ret.getClass().getClassLoader(),
                new Class[] { ServletOutputStream.class },
                new ResponseInvocationHandler(ret, response));
          }
        } finally {
          if(!isCommitted && res.isCommitted()) {
            try { throw Exception("First Committed:"); }
            // or however you want to log this
            catch(Exception e){e.printStackTrace();}              
          }
        }
        return ret;
      }
   }
}

如果您在使用代码时遇到问题,可以寻找一些常见的嫌疑人。请记住,提交可以显式触发,但更多情况下是因为您已将足够多的数据写入缓冲区,因此必须刷新:

  • 使用 jsp 分派到 servlet。 jsps 中的空白和内容类型设置很容易让你超过缓冲区限制,你可能没有意识到你写了任何东西。例如,特别是像&lt;%@ page contentType="text/html; charset=UTF-8"%&gt; 这样的jsps,然后转发到另一个servlet 是错误的。
  • 设置大 cookie。 Cookie 是标头,因此计入缓冲区大小
  • servlet 过滤器。一些应用程序有大量的这些,可能过于急于编写标头。

【讨论】:

    【解决方案2】:

    问题是因为我的类路径中有一个名为 appbase.jar 的 jar,当我删除它时一切正常。

    【讨论】:

      猜你喜欢
      • 2011-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-09
      • 2013-06-21
      • 1970-01-01
      • 1970-01-01
      • 2013-07-23
      相关资源
      最近更新 更多