繁体   English   中英

如何使用 PrimeFaces p:fileUpload? 侦听器方法从未被调用或 UploadedFile 为空/引发错误/不可用

[英]How to use PrimeFaces p:fileUpload? Listener method is never invoked or UploadedFile is null / throws an error / not usable

我正在尝试使用 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。

如何配置和排除<p:fileUpload>故障取决于 PrimeFaces 和 JSF 版本。

所有 PrimeFaces 版本

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

  1. <h:form>enctype属性需要设置为multipart/form-data 如果不存在,ajax 上传可能会正常工作,但一般浏览器行为未指定并取决于表单组合和 webbrowser 制作/版本。 只是总是指定它是安全的。

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

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

所以,如果你想要(自动)带有 ajax 支持的文件上传(注意<h:head> !):

<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!
}

请注意,在mode="simple"忽略与 ajax 相关的属性,例如autoallowTypesupdateonstartoncomplete等。 所以在这种情况下不需要指定它们。

另请注意,您应该立即在上述方法中读取文件内容,而不是在稍后的 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文件上传过滤器在所有的,你也并不需要primefaces.UPLOADER在上下文参数web.xml 如果您不清楚如何根据所使用的目标服务器正确安装和配置 JSF ,请前往如何通过 Maven 正确安装和配置 JSF 库? 我们 JSF wiki 页面的“安装 JSF”部分

但是,如果您还没有使用 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<servlet-name>值必须与同一web.xml javax.faces.webapp.FacesServlet<servlet>条目中的值完全匹配。 因此,如果它是例如Faces Servlet ,那么您需要相应地编辑它以匹配。


PrimeFaces 4.x

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

通过UploadedFile#getContents()获取上传的文件内容只有一个潜在的问题。 当使用本机 API 而不是 Apache Commons FileUpload 时,这将返回null 您需要改用UploadedFile#getInputStream() 另请参阅如何在 MySQL 中将 p:fileUpload 上传的图像插入为 BLOB?

本机 API 的另一个潜在问题是当上传组件以不同的“常规”ajax 请求被触发的形式存在时,该请求不处理上传组件。 另请参阅文件上传不适用于 PrimeFaces 4.0/JSF 2.2.x 中的 AJAX - 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中还有另一个Filter ,它PrimeFaces 文件上传过滤器之前运行并且已经通过调用getParameter()getParameterMap()getReader()等消耗了请求正文。 一个请求体只能被解析一次。 当您在文件上传过滤器完成其工作之前调用这些方法之一时,文件上传过滤器将获得一个空的请求正文。

    要解决此问题,您需要将文件上传过滤器的<filter-mapping>放在web.xml的其他过滤器之前 如果请求不是multipart/form-data请求,则文件上传过滤器将继续,好像什么也没发生。 如果您使用自动添加的过滤器,因为它们使用注释(例如 PrettyFaces),您可能需要通过 web.xml 添加显式排序。 请参阅如何使用 WAR 中的注释定义 servlet 过滤器执行顺序

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

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

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

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


保存上传的文件

在你最终让它工作后,你的下一个问题可能类似于“我如何/在哪里保存上传的文件?”。 好吧,继续在这里: 如何在 JSF 中保存上传的文件

你也在用漂亮脸蛋吗? 然后将调度程序设置为 FORWARD:

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

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

删除函数handleFileUpload ie(事件)的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>

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

我在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 个,但不是全部!! 可以删除整个上下文参数部分,默认值为 auto

豆.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);

}

}

网页.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>

这里的任何建议都对我没有帮助。 所以我不得不调试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>

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

请参阅 Primefaces Jquery 冲突

对于使用 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>

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM