-
Available since OmniFaces 2.5
The <o:inputFile>
is a component that extends the standard <h:inputFile>
and adds support for multiple
, directory
, accept
and maxsize
attributes, along with built-in server side validation on accept
and maxsize
attributes. Additionally, it makes sure that the value of HTML file input element is never rendered. The standard <h:inputFile>
renders Part#toString()
to it which is unnecessary.
Usage
You can use it the same way as <h:inputFile>
, you only need to change h:
into o:
to get the extra support for multiple
, directory
and accept
attributes. Here's are some usage examples.
Single file selection
It is basically not different from <h:inputFile>
. You might as good use it instead.
<h:form enctype="multipart/form-data">
<o:inputFile value="#{bean.file}" />
<h:commandButton value="Upload" action="#{bean.upload}" />
</h:form>
private Part file; // +getter+setter
public void upload() {
if (file != null) {
String name = Servlets.getSubmittedFileName(file);
String type = file.getContentType();
long size = file.getSize();
InputStream content = file.getInputStream();
// ...
}
}
Note that it's strongly recommended to use Servlets.getSubmittedFileName(Part)
to obtain the submitted file name to make sure that any path is stripped off. Some browsers are known to incorrectly include the client side path or even a fake path along with the file name.
Multiple file selection
The multiple
attribute can be set to true
to enable multiple file selection. With this setting the enduser can use control/command/shift keys to select multiple files.
<h:form enctype="multipart/form-data">
<o:inputFile value="#{bean.files}" multiple="true" />
<h:commandButton value="Upload" action="#{bean.upload}" />
</h:form>
private List<Part> files; // +getter+setter
public void upload() {
if (files != null) {
for (Part file : files) {
String name = Servlets.getSubmittedFileName(file);
String type = file.getContentType();
long size = file.getSize();
InputStream content = file.getInputStream();
// ...
}
}
}
Folder selection
The directory
attribute can be set to true
to enable folder selection. This implicitly also sets multiple
attribute to true
and renders an additional webkitdirectory
attribute to HTML for better browser compatibility.
<h:form enctype="multipart/form-data">
<o:inputFile value="#{bean.files}" directory="true" />
<h:commandButton value="Upload" action="#{bean.upload}" />
</h:form>
private List<Part> files; // +getter+setter
public void upload() {
if (files != null) {
for (Part file : files) {
String name = Servlets.getSubmittedFileName(file);
String type = file.getContentType();
long size = file.getSize();
InputStream content = file.getInputStream();
// ...
}
}
}
Do note that this does not send physical folders, but only files contained in those folders.
Media type filtering
The accept
attribute can be set with a comma separated string of media types of files to filter in browse dialog. An overview of all registered media types can be found at IANA.
<h:form enctype="multipart/form-data">
<o:inputFile id="file" value="#{bean.losslessImageFile}" accept="image/png,image/gif" />
<h:commandButton value="Upload" action="#{bean.upload}" />
<h:message for="file" />
</h:form>
<h:form enctype="multipart/form-data">
<o:inputFile id="file" value="#{bean.anyImageFile}" accept="image/*" />
<h:commandButton value="Upload" action="#{bean.upload}" />
<h:message for="file" />
</h:form>
<h:form enctype="multipart/form-data">
<o:inputFile id="file" value="#{bean.anyMediaFile}" accept="audio/*,image/*,video/*" />
<h:commandButton value="Upload" action="#{bean.upload}" />
<h:message for="file" />
</h:form>
This will also be validated in the server side using a built-in validator. Do note that the accept
attribute only filters in client side and validates in server side based on the file extension, and this does thus not strictly validate the file's actual content. To cover that as well, you should in the bean's action method parse the file's actual content using the tool suited for the specific media type, such as ImageIO#read()
for image files, and then checking if it returns the expected result.
The default message for server side validation of accept
attribute is:
{0}: Media type of file ''{1}'' does not match ''{2}''
Where {0}
is the component's label and {1}
is the submitted file name and {2}
is the value of accept
attribute.
You can override the default message by the acceptMessage
attribute:
<h:form enctype="multipart/form-data">
<o:inputFile id="file" value="#{bean.anyImageFile}" accept="image/*" acceptMessage="File {1} is unacceptable!" />
<h:commandButton value="Upload" action="#{bean.upload}" />
<h:message for="file" />
</h:form>
Or by the custom message bundle file as identified by <application><message-bundle>
in faces-config.xml
. The message key is org.omnifaces.component.input.InputFile.accept
.
org.omnifaces.component.input.InputFile.accept = File {1} is unacceptable!
File size validation
The maxsize
attribute can be set with the maximum file size in bytes which will be validated on each selected file in the client side if the client supports HTML5 File API. This validation will be performed by custom JavaScript in client side instead of by Faces in server side. This only requires that there is a <h:message>
or <h:messages>
component and that it has its id
set.
<o:inputFile id="file" ... />
<h:message id="messageForFile" for="file" /> <!-- This must have 'id' attribute set! -->
This way the client side can trigger Faces via an ajax request to update the message component with the client side validation message. Noted should be that the file(s) will not be sent, hereby saving network bandwidth.
<h:form enctype="multipart/form-data">
<o:inputFile id="file" value="#{bean.file}" maxsize="#{10 * 1024 * 1024}" /> <!-- 10MiB -->
<h:commandButton value="Upload" action="#{bean.upload}" />
<h:message id="messageForFile" for="file" />
</h:form>
This will also be validated in the server side using a built-in validator.
The default message for both client side and server side validation of maxsize
attribute is:
{0}: Size of file ''{1}'' is larger than maximum of {2}
Where {0}
is the component's label and {1}
is the submitted file name and {2}
is the value of maxsize
attribute.
You can override the default message by the maxsizeMessage
attribute:
<h:form enctype="multipart/form-data">
<o:inputFile id="file" value="#{bean.file}" maxsize="#{10 * 1024 * 1024}" maxsizeMessage="File {1} is too big!" />
<h:commandButton value="Upload" action="#{bean.upload}" />
<h:message id="messageForFile" for="file" />
</h:form>
Or by the custom message bundle file as identified by <application><message-bundle>
in faces-config.xml
. The message key is org.omnifaces.component.input.InputFile.maxsize
.
org.omnifaces.component.input.InputFile.maxsize = File {1} is too big!
Upload an image of max 1MiB and display a preview
<h3>Upload an image of max 1MiB and display a preview</h3>
<h:form enctype="multipart/form-data">
<h:panelGrid columns="3">
<o:outputLabel for="file" value="Image" />
<o:inputFile id="file" value="#{uploadImageBean.file}"
accept="image/*" acceptMessage="Wrong file type! Should match {2}"
maxsize="#{1024 * 1024}" maxsizeMessage="File too large! Should be no more than {2}">
<f:ajax listener="#{uploadImageBean.read()}" render="@form" />
</o:inputFile>
<h:message id="file_m" for="file" />
</h:panelGrid>
<o:graphicImage value="#{uploadImageBean.content}" dataURI="true" width="600"
rendered="#{not empty uploadImageBean.content}" />
</h:form>
package org.omnifaces.showcase.components;
import java.io.IOException;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Named;
import jakarta.servlet.http.Part;
import org.omnifaces.util.Utils;
@Named
@RequestScoped // Can be @ViewScoped, but caution should be taken with byte[] property. You don't want to save it in session.
public class UploadImageBean {
private Part file;
private byte[] content;
public void read() throws IOException {
content = Utils.toByteArray(file.getInputStream());
}
public Part getFile() {
return file;
}
public void setFile(Part file) {
this.file = file;
}
public byte[] getContent() {
return content;
}
}