I would like to be able to upload files in my JSF2.2 web application, so I started using the new <h:inputFile>
component.
My only question is, how can I specify the location, where the files will be saved in the server? I would like to get hold of them as java.io.File
instances. This has to be implemented in the backing bean, but I don't clearly understand how.
The Formidable Module There is a very good module for working with file uploads, called "Formidable".
JSF won't save the file in any predefined location. It will basically just offer you the uploaded file in flavor of a javax.servlet.http.Part
instance which is behind the scenes temporarily stored in server's memory and/or temporary disk storage location which you shouldn't worry about.
Important is that you need to read the Part
as soon as possible when the bean action (listener) method is invoked. The temporary storage may be cleared out when the HTTP response associated with the HTTP request is completed. In other words, the uploaded file won't necessarily be available in a subsequent request.
So, given a
<h:form enctype="multipart/form-data">
<h:inputFile value="#{bean.uploadedFile}">
<f:ajax listener="#{bean.upload}" />
</h:inputFile>
</h:form>
You have basically 2 options to save it:
byte[]
You can use InputStream#readAllBytes()
for this.
private Part uploadedFile; // +getter+setter
private String fileName;
private byte[] fileContents;
public void upload() {
fileName = Paths.get(uploadedFile.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
try (InputStream input = uploadedFile.getInputStream()) {
fileContents = input.readAllBytes();
}
catch (IOException e) {
// Show faces message?
}
}
Note the Path#getFileName()
. This is a MSIE fix as to obtaining the submitted file name. This browser incorrectly sends the full file path along the name instead of only the file name.
In case you're not on Java 9 yet and therefore can't use InputStream#readAllBytes()
, then head to Convert InputStream to byte array in Java for all other ways to convert InputStream
to byte[]
.
Keep in mind that each byte of an uploaded file costs one byte of server memory. Be careful that your server don't exhaust of memory when users do this too often or can easily abuse your system in this way. If you want to avoid this, better use (temporary) files on local disk file system instead.
In order to save it to the desired location, you need to get the content by Part#getInputStream()
and then copy it to the Path
representing the location.
private Part uploadedFile; // +getter+setter
private File savedFile;
public void upload() {
String fileName = Paths.get(uploadedFile.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
savedFile = new File(uploads, fileName);
try (InputStream input = file.getInputStream()) {
Files.copy(input, savedFile.toPath());
}
catch (IOException e) {
// Show faces message?
}
}
Note the Path#getFileName()
. This is a MSIE fix as to obtaining the submitted file name. This browser incorrectly sends the full file path along the name instead of only the file name.
The uploads
folder and the filename
is fully under your control. E.g. "/path/to/uploads"
and Part#getSubmittedFileName()
respectively. Keep in mind that any existing file would be overwritten, you might want to use File#createTempFile()
to autogenerate a filename. You can find an elaborate example in this answer.
Do not use Part#write()
as some prople may suggest. It will basically rename the file in the temporary storage location as identified by @MultipartConfig(location)
. Also do not use ExternalContext#getRealPath()
in order to save the uploaded file in deploy folder. The file will get lost when the WAR is redeployed for the simple reason that the file is not contained in the original WAR. Always save it on an absolute path outside the deploy folder.
For a live demo of upload-and-preview feature, check the demo section of the <o:inputFile>
page on OmniFaces showcase.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With