How to use PrimeFaces p:fileUpload? Listener method is never invoked or UploadedFile is null / throws an error / not usable
How to configure and troubleshoot <p:fileUpload>
depends on PrimeFaces and JSF version.
All PrimeFaces versions
The below requirements apply to all PrimeFaces versions:
-
The
enctype
attribute of the<h:form>
needs to be set tomultipart/form-data
. When this is absent, the ajax upload may just work, but the general browser behavior is unspecified and dependent on form composition and webbrowser make/version. Just always specify it to be on the safe side. -
When using
mode="advanced"
(i.e. ajax upload, this is the default), then make sure that you've a<h:head>
in the (master) template. This will ensure that the necessary JavaScript files are properly included. This is not required formode="simple"
(non-ajax upload), but this would break look'n'feel and functionality of all other PrimeFaces components, so you don't want to miss that anyway. -
When using
mode="simple"
(i.e. non-ajax upload), then ajax must be disabled on any PrimeFaces command buttons/links byajax="false"
, and you must use<p:fileUpload value>
with<p:commandButton action>
instead of<p:fileUpload listener>
.
So, if you want (auto) file upload with ajax support (mind the <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!
}
Or if you want non-ajax file upload:
<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!
}
Do note that ajax-related attributes such as auto
, allowTypes
, update
, onstart
, oncomplete
, etc are ignored in mode="simple"
. So it's needless to specify them in such case.
Also note that you should read the file contents immediately inside the abovementioned methods and not in a different bean method invoked by a later HTTP request. This is because the uploaded file contents is request scoped and thus unavailable in a later/different HTTP request. Any attempt to read it in a later request will most likely end up with java.io.FileNotFoundException
on the temporary file.
PrimeFaces 8.x
Configuration is identical to the 5.x version info below, but if your listener is not called, check if the method attribute is called listener
and not (like with pre 8.x versions) fileUploadListener
.
PrimeFaces 5.x
This does not require any additional configuration if you're using JSF 2.2 and your faces-config.xml
is also declared conform JSF 2.2 version. You do not need the PrimeFaces file upload filter at all and you also do not need the primefaces.UPLOADER
context parameter in web.xml
. In case it's unclear to you how to properly install and configure JSF depending on the target server used, head to How to properly install and configure JSF libraries via Maven? and "Installing JSF" section of our JSF wiki page.
If you're however not using JSF 2.2 yet and you can't upgrade it (should be effortless when already on a Servlet 3.0 compatible container), then you need to manually register the below PrimeFaces file upload filter in web.xml
(it will parse the multi part request and fill the regular request parameter map so that FacesServlet
can continue working as usual):
<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>
The <servlet-name>
value of facesServlet
must match exactly the value in the <servlet>
entry of the javax.faces.webapp.FacesServlet
in the same web.xml
. So if it's e.g. Faces Servlet
, then you need to edit it accordingly to match.
PrimeFaces 4.x
The same story as PrimeFaces 5.x applies on 4.x as well.
There's only a potential problem in getting the uploaded file content by UploadedFile#getContents()
. This will return null
when native API is used instead of Apache Commons FileUpload. You need to use UploadedFile#getInputStream()
instead. See also How to insert uploaded image from p:fileUpload as BLOB in MySQL?
Another potential problem with native API will manifest is when the upload component is present in a form on which a different "regular" ajax request is fired which does not process the upload component. See also 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.
Both problems can also be solved by switching to Apache Commons FileUpload. See PrimeFaces 3.x section for detail.
PrimeFaces 3.x
This version does not support JSF 2.2 / Servlet 3.0 native file upload. You need to manually install Apache Commons FileUpload and explicitly register the file upload filter in web.xml
.
You need the following libraries:
commons-fileupload.jar
commons-io.jar
Those must be present in the webapp's runtime classpath. When using Maven, make sure they are at least runtime scoped (default scope of compile is also good). When manually carrying around JARs, make sure they end up in /WEB-INF/lib
folder.
The file upload filter registration detail can be found in PrimeFaces 5.x section here above. In case you're using PrimeFaces 4+ and you'd like to explicitly use Apache Commons FileUpload instead of JSF 2.2 / Servlet 3.0 native file upload, then you need next to the mentioned libraries and filter also the below context param in web.xml
:
<context-param>
<param-name>primefaces.UPLOADER</param-name>
<param-value>commons</param-value><!-- Allowed values: auto, native and commons. -->
</context-param>
Troubleshooting
In case it still doesn't work, here are another possible causes unrelated to PrimeFaces configuration:
-
Only if you're using the PrimeFaces file upload filter: There's another
Filter
in your webapp which runs before the PrimeFaces file upload filter and has already consumed the request body by e.g. callinggetParameter()
,getParameterMap()
,getReader()
, etcetera. A request body can be parsed only once. When you call one of those methods before the file upload filter does its job, then the file upload filter will get an empty request body.To fix this, you'd need to put the
<filter-mapping>
of the file upload filter before the other filter inweb.xml
. If the request is not amultipart/form-data
request, then the file upload filter will just continue as if nothing happened. If you use filters that are automagically added because they use annotations (e.g. PrettyFaces), you might need to add explicit ordering via web.xml. See How to define servlet filter order of execution using annotations in WAR -
Only if you're using the PrimeFaces file upload filter: There's another
Filter
in your webapp which runs before the PrimeFaces file upload filter and has performed aRequestDispatcher#forward()
call. Usually, URL rewrite filters such as PrettyFaces do this. This triggers theFORWARD
dispatcher, but filters listen by default onREQUEST
dispatcher only.To fix this, you'd need to either put the PrimeFaces file upload filter before the forwarding filter, or to reconfigure the PrimeFaces file upload filter to listen on
FORWARD
dispatcher too:<filter-mapping> <filter-name>primeFacesFileUploadFilter</filter-name> <servlet-name>facesServlet</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
-
There's a nested
<h:form>
. This is illegal in HTML and the browser behavior is unspecified. More than often, the browser won't send the expected data on submit. Make sure that you are not nesting<h:form>
. This is completely regardless of the form'senctype
. Just do not nest forms at all.
If you're still having problems, well, debug the HTTP traffic. Open the webbrowser's developer toolset (press F12 in Chrome/Firebug23+/IE9+) and check the Net/Network section. If the HTTP part looks fine, then debug the JSF code. Put a breakpoint on FileUploadRenderer#decode()
and advance from there.
Saving uploaded file
After you finally got it to work, your next question shall probably be like "How/where do I save the uploaded file?". Well, continue here: How to save uploaded file in JSF.
You are using prettyfaces too? Then set dispatcher to FORWARD:
<filter-mapping>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
One point I noticed with Primefaces 3.4 and Netbeans 7.2:
Remove the Netbeans auto-filled parameters for function handleFileUpload i.e. (event) otherwise event could be null.
<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>
Looks like javax.faces.SEPARATOR_CHAR must not be equal to _