<p:fileUpload> 的配置和故障排除方法取决于 PrimeFaces 和 JSF 版本。
所有 PrimeFaces 版本
以下要求适用于所有 PrimeFaces 版本:
-
<h:form> 的enctype 属性需要设置为multipart/form-data。如果不存在,ajax 上传可能会正常工作,但一般浏览器行为是未指定的,并且取决于表单组合和 webbrowser make/version。只需始终指定它以确保安全。
-
使用mode="advanced"(即ajax 上传,这是默认设置)时,请确保您在(主)模板中有<h:head>。这将确保正确包含必要的 JavaScript 文件。 mode="simple"(非 ajax 上传)不需要这样做,但这会破坏所有其他 PrimeFaces 组件的外观和功能,所以无论如何你都不想错过。
-
使用mode="simple"(即非ajax 上传)时,必须通过ajax="false" 在任何PrimeFaces 命令按钮/链接上禁用ajax,并且您必须使用<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!
}
请注意 auto、allowTypes、update、onstart、oncomplete 等与 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 的<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()。另见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 配置无关的另一个可能原因:
-
仅当您使用 PrimeFaces 文件上传过滤器时:您的 web 应用中还有另一个 Filter,它在 PrimeFaces 文件上传过滤器之前运行,并且已经消耗了请求正文,例如致电getParameter()、getParameterMap()、getReader() 等。请求正文只能解析一次。当您在文件上传过滤器完成其工作之前调用其中一种方法时,文件上传过滤器将获得一个空的请求正文。
要解决此问题,您需要将文件上传过滤器的<filter-mapping> 放在web.xml 中的另一个过滤器之前。如果请求不是multipart/form-data 请求,则文件上传过滤器将继续执行,就好像什么都没发生一样。如果您使用自动添加的过滤器,因为它们使用注释(例如 PrettyFaces),您可能需要通过 web.xml 添加显式排序。见How to define servlet filter order of execution using annotations in WAR
-
仅当您使用 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>
-
有一个嵌套的<h:form>。这在 HTML 中是非法的,并且未指定浏览器行为。通常,浏览器不会在提交时发送预期的数据。确保您没有嵌套<h:form>。这完全与表单的enctype 无关。根本不要嵌套表单。
如果您仍然遇到问题,那么请调试 HTTP 流量。打开 webbrowser 的开发工具集(在 Chrome/Firebug23+/IE9+ 中按 F12)并检查 Net/Network 部分。如果 HTTP 部分看起来不错,则调试 JSF 代码。在FileUploadRenderer#decode() 上放置一个断点并从那里继续前进。
保存上传的文件
在你终于让它工作之后,你的下一个问题可能是“我如何/在哪里保存上传的文件?”。好吧,在这里继续:How to save uploaded file in JSF。