【问题标题】:How to use PrimeFaces p:fileUpload? Listener method is never invoked or UploadedFile is null / throws an error / not usable如何使用 PrimeFaces p:fileUpload?侦听器方法从未被调用或 UploadedFile 为空/抛出错误/不可用
【发布时间】:2016-08-31 23:49:05
【问题描述】:

我正在尝试使用 PrimeFaces 上传文件,但上传完成后没有调用 fileUploadListener 方法。

这里是视图:

<h:form>
    <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
        mode="advanced" 
        update="messages"
        sizeLimit="100000" 
        allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>

    <p:growl id="messages" showDetail="true"/>
</h:form>

还有豆子:

@ManagedBean
@RequestScoped
public class FileUploadController {

    public void handleFileUpload(FileUploadEvent event) {
        FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }

}

我在该方法上放置了一个断点,但它从未被调用过。当使用mode="simple"ajax="false" 时,它被调用,但我希望它在高级模式下工作。我正在使用 Netbeans 和 Glassfish 3.1。

【问题讨论】:

    标签: jsf file-upload jsf-2 primefaces


    【解决方案1】:

    &lt;p:fileUpload&gt; 的配置和故障排除方法取决于 PrimeFaces 和 JSF 版本。

    所有 PrimeFaces 版本

    以下要求适用于所有 PrimeFaces 版本:

    1. &lt;h:form&gt;enctype 属性需要设置为multipart/form-data。如果不存在,ajax 上传可能会正常工作,但一般浏览器行为是未指定的,并且取决于表单组合和 webbrowser make/version。只需始终指定它以确保安全。

    2. 使用mode="advanced"(即ajax 上传,这是默认设置)时,请确保您在(主)模板中有&lt;h:head&gt;。这将确保正确包含必要的 JavaScript 文件。 mode="simple"(非 ajax 上传)不需要这样做,但这会破坏所有其他 PrimeFaces 组件的外观和功能,所以无论如何你都不想错过。

    3. 使用mode="simple"(即非ajax 上传)时,必须通过ajax="false" 在任何PrimeFaces 命令按钮/链接上禁用ajax,并且您必须使用&lt;p:fileUpload value&gt;&lt;p:commandButton action&gt; 而不是&lt;p:fileUpload listener&gt;

    所以,如果您想(自动)上传支持 ajax 的文件(注意&lt;h:head&gt;!):

    <h:form enctype="multipart/form-data">
        <p:fileUpload listener="#{bean.upload}" auto="true" /> // For PrimeFaces version older than 8.x this should be fileUploadListener instead of listener.
    </h:form>
    
    public void upload(FileUploadEvent event) {
        UploadedFile uploadedFile = event.getFile();
        String fileName = uploadedFile.getFileName();
        String contentType = uploadedFile.getContentType();
        byte[] contents = uploadedFile.getContents(); // Or getInputStream()
        // ... Save it, now!
    }
    

    或者如果你想要非 ajax 文件上传:

    <h:form enctype="multipart/form-data">
        <p:fileUpload mode="simple" value="#{bean.uploadedFile}" />
        <p:commandButton value="Upload" action="#{bean.upload}" ajax="false" />
    </h:form>
    
    private transient UploadedFile uploadedFile; // +getter+setter
    
    public void upload() {
        String fileName = uploadedFile.getFileName();
        String contentType = uploadedFile.getContentType();
        byte[] contents = uploadedFile.getContents(); // Or getInputStream()
        // ... Save it, now!
    }
    

    请注意 autoallowTypesupdateonstartoncomplete 等与 ajax 相关的属性在 mode="simple" 中被忽略。因此在这种情况下无需指定它们。

    还请注意,您应该在上述方法中立即读取文件内容,而不是在稍后的 HTTP 请求调用的其他 bean 方法中。这是因为上传的文件内容是请求范围的,因此在以后/不同的 HTTP 请求中不可用。在以后的请求中读取它的任何尝试很可能会在临时文件中以java.io.FileNotFoundException 结束。


    PrimeFaces 8.x

    配置与下面的 5.x 版本信息相同,但如果未调用您的侦听器,请检查方法属性是否被称为 listener 而不是(如 8.x 之前的版本)fileUploadListener


    PrimeFaces 5.x

    如果您使用的是 JSF 2.2 并且您的 faces-config.xml 也被声明为符合 JSF 2.2 版本,那么这确实不需要需要任何额外的配置。您确实不需要根本不需要 PrimeFaces 文件上传过滤器,而且您也确实不需要需要 web.xml 中的 primefaces.UPLOADER 上下文参数。如果您不清楚如何根据使用的目标服务器正确安装和配置 JSF,请前往 How to properly install and configure JSF libraries via Maven?"Installing JSF" section of our JSF wiki page

    如果您还没有使用 JSF 2.2 并且您无法升级它(当已经在 Servlet 3.0 兼容的容器上时应该毫不费力),那么您需要在 web.xml 中手动注册以下 PrimeFaces 文件上传过滤器(它将解析多部分请求并填充常规请求参数映射,以便FacesServlet 可以继续照常工作):

    <filter>
        <filter-name>primeFacesFileUploadFilter</filter-name>
        <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>primeFacesFileUploadFilter</filter-name>
        <servlet-name>facesServlet</servlet-name>
    </filter-mapping>
    

    facesServlet&lt;servlet-name&gt; 值必须与同一web.xmljavax.faces.webapp.FacesServlet&lt;servlet&gt; 条目中的值完全匹配。所以如果它是例如Faces Servlet,那么你需要相应地编辑它以匹配。


    PrimeFaces 4.x

    与 PrimeFaces 5.x 相同的故事也适用于 4.x。

    UploadedFile#getContents() 获取上传的文件内容只有一个潜在的问题。当使用本机 API 而不是 Apache Commons FileUpload 时,这将返回 null。您需要改用UploadedFile#getInputStream()。另见How to insert uploaded image from p:fileUpload as BLOB in MySQL?

    本机 API 的另一个潜在问题是,当上传组件出现在表单中时,会触发一个不处理上传组件的不同“常规”ajax 请求。另见File upload doesn't work with AJAX in PrimeFaces 4.0/JSF 2.2.x - javax.servlet.ServletException: The request content-type is not a multipart/form-data

    这两个问题也可以通过切换到 Apache Commons FileUpload 来解决。有关详细信息,请参阅 PrimeFaces 3.x 部分。


    PrimeFaces 3.x

    此版本不支持 JSF 2.2 / Servlet 3.0 原生文件上传。您需要手动安装 Apache Commons FileUpload 并在web.xml 中显式注册文件上传过滤器。

    您需要以下库:

    这些必须存在于 webapp 的运行时类路径中。使用 Maven 时,请确保它们至少是运行时范围的(默认的编译范围也很好)。手动携带 JAR 时,请确保它们最终位于 /WEB-INF/lib 文件夹中。

    可以在上面的 PrimeFaces 5.x 部分中找到文件上传过滤器注册详细信息。如果您使用 PrimeFaces 4+ 并且您想显式使用 Apache Commons FileUpload 而不是 JSF 2.2 / Servlet 3.0 本机文件上传,那么您需要在提到的库旁边并过滤 web.xml 中的以下上下文参数:

    <context-param>
        <param-name>primefaces.UPLOADER</param-name>
        <param-value>commons</param-value><!-- Allowed values: auto, native and commons. -->
    </context-param>
    

    疑难解答

    如果仍然无法正常工作,以下是与 PrimeFaces 配置无关的另一个可能原因:

    1. 仅当您使用 PrimeFaces 文件上传过滤器时:您的 web 应用中还有另一个 Filter,它在 PrimeFaces 文件上传过滤器之前运行,并且已经消耗了请求正文,例如致电getParameter()getParameterMap()getReader() 等。请求正文只能解析一次。当您在文件上传过滤器完成其工作之前调用其中一种方法时,文件上传过滤器将获得一个空的请求正文。

      要解决此问题,您需要将文件上传过滤器的&lt;filter-mapping&gt; 放在web.xml 中的另一个过滤器之前。如果请求不是multipart/form-data 请求,则文件上传过滤器将继续执行,就好像什么都没发生一样。如果您使用自动添加的过滤器,因为它们使用注释(例如 PrettyFaces),您可能需要通过 web.xml 添加显式排序。见How to define servlet filter order of execution using annotations in WAR

    2. 仅当您使用 PrimeFaces 文件上传过滤器时:您的 web 应用中还有另一个 Filter,它在 PrimeFaces 文件上传过滤器之前运行 并执行了 RequestDispatcher#forward() 调用。通常,URL 重写过滤器(例如 PrettyFaces)会执行此操作。这会触发 FORWARD 调度程序,但过滤器默认只侦听 REQUEST 调度程序。

      要解决此问题,您需要将 PrimeFaces 文件上传过滤器放在转发过滤器之前,或者重新配置 PrimeFaces 文件上传过滤器以监听 FORWARD 调度程序: p>

       <filter-mapping>
           <filter-name>primeFacesFileUploadFilter</filter-name>
           <servlet-name>facesServlet</servlet-name>
           <dispatcher>REQUEST</dispatcher>
           <dispatcher>FORWARD</dispatcher>
       </filter-mapping>
      
    3. 有一个嵌套的&lt;h:form&gt;。这在 HTML 中是非法的,并且未指定浏览器行为。通常,浏览器不会在提交时发送预期的数据。确保您没有嵌套&lt;h:form&gt;。这完全与表单的enctype 无关。根本不要嵌套表单。

    如果您仍然遇到问题,那么请调试 HTTP 流量。打开 webbrowser 的开发工具集(在 Chrome/Firebug23+/IE9+ 中按 F12)并检查 Net/Network 部分。如果 HTTP 部分看起来不错,则调试 JSF 代码。在FileUploadRenderer#decode() 上放置一个断点并从那里继续前进。


    保存上传的文件

    在你终于让它工作之后,你的下一个问题可能是“我如何/在哪里保存上传的文件?”。好吧,在这里继续:How to save uploaded file in JSF

    【讨论】:

      【解决方案2】:

      你也在使用漂亮的面孔吗?然后将 dispatcher 设置为 FORWARD:

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

      【讨论】:

      • 与 OCP Rewrite 一起使用时,这仍然是一个问题。我欠你一杯啤酒:)
      【解决方案3】:

      我注意到 Primefaces 3.4 和 Netbeans 7.2 的一点:

      删除函数 handleFileUpload 的 Netbeans 自动填充参数,即(事件),否则事件可能为空。

      <h:form>
          <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload(event)}"
              mode="advanced" 
              update="messages"
              sizeLimit="100000" 
              allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>
      
          <p:growl id="messages" showDetail="true"/>
      </h:form>
      

      【讨论】:

        【解决方案4】:

        看起来 javax.faces.SEPARATOR_CHAR 不能等于 _

        【讨论】:

        • 您能详细说明一下吗?!
        【解决方案5】:

        我在 primefaces 5.3 上遇到了同样的问题,我经历了 BalusC 描述的所有要点,但没有结果。我听从了他调试 FileUploadRenderer#decode() 的建议,发现我的 web.xml 设置不正确

        <context-param>
          <param-name>primefaces.UPLOADER</param-name>
          <param-value>auto|native|commons</param-value>
        </context-param>
        

        param-value 必须是这 3 个值中的 1 个,但不是全部! 整个 context-param 部分可以删除,默认为 auto

        【讨论】:

          【解决方案6】:

          bean.xhtml

              <h:form enctype="multipart/form-data">    
          <p:outputLabel value="Choose your file" for="submissionFile" />
                          <p:fileUpload id="submissionFile"
                              value="#{bean.file}"
                              fileUploadListener="#{bean.uploadFile}" mode="advanced"
                              auto="true" dragDropSupport="false" update="messages"
                              sizeLimit="100000" fileLimit="1" allowTypes="/(\.|\/)(pdf)$/" />
          
          </h:form>
          

          Bean.java

          @ManagedBean
          

          @ViewScoped 公共类提交实现可序列化{

          private UploadedFile file;
          
          //Gets
          //Sets
          
          public void uploadFasta(FileUploadEvent event) throws FileNotFoundException, IOException, InterruptedException {
          
              String content = IOUtils.toString(event.getFile().getInputstream(), "UTF-8");
          
              String filePath = PATH + "resources/submissions/" + nameOfMyFile + ".pdf";
          
              MyFileWriter.writeFile(filePath, content);
          
              FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO,
                      event.getFile().getFileName() + " is uploaded.", null);
              FacesContext.getCurrentInstance().addMessage(null, message);
          
          }
          

          }

          web.xml

              <servlet-mapping>
              <servlet-name>Faces Servlet</servlet-name>
              <url-pattern>*.xhtml</url-pattern>
          </servlet-mapping>
          <filter>
              <filter-name>PrimeFaces FileUpload Filter</filter-name>
              <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
          </filter>
          <filter-mapping>
              <filter-name>PrimeFaces FileUpload Filter</filter-name>
              <servlet-name>Faces Servlet</servlet-name>
          </filter-mapping>
          

          【讨论】:

          • 你能解释一下为什么这是一个答案吗?它只是代码,而不是解释或其他任何东西
          • "#{bean.uploadFile}" vs "#{bean.uploadFasta}" ,删除 update="messages" 它将(仅)对我有用!
          【解决方案7】:

          这里的建议对我都没有帮助。于是只好调试primefaces,发现问题的原因是:

          java.lang.IllegalStateException: No multipart config for servlet fileUpload
          

          然后我在 web.xml 中的 faces servlet 中添加了部分。这样就解决了问题:

          <servlet>
              <servlet-name>main</servlet-name>
          
                  <servlet-class>org.apache.myfaces.webapp.MyFacesServlet</servlet-class>
                  <load-on-startup>1</load-on-startup>
                  <multipart-config>
                      <location>/tmp</location>
                      <max-file-size>20848820</max-file-size>
                      <max-request-size>418018841</max-request-size>
                      <file-size-threshold>1048576</file-size-threshold>
                  </multipart-config>
              </servlet>
          

          【讨论】:

            【解决方案8】:

            我遇到了同样的问题,因为我拥有这篇文章中描述的所有配置,但在我的情况下是因为我有两个 jquery 导入(其中一个是 primefaces 的查询),这导致上传文件发生冲突.

            See Primefaces Jquery conflict

            【讨论】:

            • 那您在浏览器开发者控制台中没有收到特定错误吗?
            • @Kukeltje 这是控制台显示的内容:Uncaught TypeError: Object [object Object] has no method 'fileupload'
            【解决方案9】:

            对于使用 Tomee 或 Tomcat 并且无法使其正常工作的人,请尝试在 META-INF 中创建 context.xml 并添加 allowCasualMultipartParsing="true "

            <?xml version="1.0" encoding="UTF-8"?>
            <Context allowCasualMultipartParsing="true">
              <!-- empty or not depending your project -->
            </Context>
            

            【讨论】:

            • 这是针对错误过滤器配置/排序的解决方法。
            • 嗨@BalusC,你能给我们更多解释吗?有没有比这种解决方法更好的方法?
            • 查看我在这个问题中的回答。
            【解决方案10】:

            对于 JBoss 7.2(Undertow) 和 PrimeFaces 6.0,应该从 web.xml 中删除 org.primefaces.webapp.filter.FileUploadFilter 并且应该将上下文参数文件上传器设置为原生:

            <context-param>
                <param-name>primefaces.UPLOADER</param-name>
                <param-value>native</param-value>
            </context-param>
            

            【讨论】:

            • 应该吗?如果没有,您是否会收到特定错误
            • 是的,如果没有这些更改,我的 FileUploadEvent 不会调用。
            • 这不是显式错误,而是意外行为
            【解决方案11】:

            p:fileUpload 设置为h:form 解决了我的问题。

            【讨论】:

              猜你喜欢
              • 2012-12-10
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多