22

I am uploading a file in JSF. I am using Tomahawk's <t:inputFileUpload> for this, but the same question is applicable on e.g. PrimeFaces <p:fileUpload> and JSF 2.2 <h:inputFile>.

I have the below backing bean code:

private UploadedFile uploadedFile; // +getter+setter

public String save() throws IOException {
    String name = uploadedFile.getName();
    System.out.println("File name: " + name);

    String type = uploadedFile.getContentType();
    System.out.println("File type: " + type);

    long size = uploadedFile.getSize();
    System.out.println("File size: " + size);  

    InputStream stream = uploadedFile.getInputStream();
    byte[] buffer = new byte[(int) size];  
    stream.read(buffer, 0, (int) size);  
    stream.close();  
}

I am able to get the file name, type and size, but I am unable to save this file at a specific path. I can't figure out the proper way to save the uploaded file. How can I achieve this?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
V.Rohan
  • 945
  • 2
  • 14
  • 27

2 Answers2

60

The getInputStream() method of the uploaded file represents the file content.

InputStream input = uploadedFile.getInputStream();

You need to copy it to a file. You should first prepare a folder on the local disk file system where the uploaded files should be stored. For example, /path/to/uploads (on Windows, that would be on the same disk as where the server runs). Note that you should absolutely not store the files in expanded WAR folder or even the IDE project's folder by using a hardcoded path or a web-relative path or getRealPath() for the reasons mentioned here Uploaded image only available after refreshing the page.

Then, you need to autogenerate the filename. Otherwise, when someone else uploads a file with coincidentally the same name later, it would be overwritten. You could use Files#createTempFile() facility to get an autogenerated filename.

Path folder = Paths.get("/path/to/uploads");
String filename = FilenameUtils.getBaseName(uploadedFile.getName()); 
String extension = FilenameUtils.getExtension(uploadedFile.getName());
Path file = Files.createTempFile(folder, filename + "-", "." + extension);

The path to uploads could if necessary be parameterized based on one of several ways shown in this Q&A: Recommended way to save uploaded files in a servlet application. The FilenameUtils is part of Apache Commons IO which you should already have in your classpath as it's a dependency of the Tomahawk file upload component.

Finally, just stream the uploaded file to that file (assuming Java 7):

try (InputStream input = uploadedFile.getInputStream()) {
    Files.copy(input, file, StandardCopyOption.REPLACE_EXISTING);
}

System.out.println("Uploaded file successfully saved in " + file);

Then, to download it back, easiest would be to register /path/to/uploads as a new webapp context or a virtual host so that all those files are available by an URL. See also Load images from outside of webapps / webcontext / deploy folder using <h:graphicImage> or <img> tag.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • thanx, And if I want to select multiple files at once, then what i need to prefer? – V.Rohan Jan 08 '13 at 11:55
  • 2
    That's not possible with `` which generates a plain old HTML4 ``. That's only possible with Flash or HTML5 ``. RichFaces and PrimeFaces offer such components. E.g. http://showcase.richfaces.org/richfaces/component-sample.jsf?demo=fileUpload&skin=blueSky and http://www.primefaces.org/showcase/ui/fileUploadMultiple.jsf – BalusC Jan 08 '13 at 11:57
  • Thanx, sorry for questioning more and more , but any other way using ? – V.Rohan Jan 08 '13 at 11:59
  • 2
    Just use multiple `` components or use it in `` or `` backed by a collection of `UploadedFile`. – BalusC Jan 08 '13 at 12:00
  • @BalusC How can I save image in Maven `webapp/resources/imagens` ? I'm trying save an image in this folder but still can't do this works. – FernandoPaiva Sep 14 '14 at 15:04
  • @BalusC I tried your example and I have an exception: `SEVERE [http-nio-8084-exec-89] image.beans.ImgUpload.upload null java.nio.file.FileAlreadyExistsException: D:\cmsdata\test-2264165946506714423.jpg` at the line `Files.copy(input, tempFile.toPath())`. When I go to the folder, there is a file `test-2264165946506714423.jpg`, but its size is `0KB`. What could be wrong? – vtomic85 Feb 17 '15 at 10:44
  • @Alexander: when using PrimeFaces ``, yes. – BalusC Aug 04 '15 at 17:19
  • @BalusC why did you use `StandardCopyOption.REPLACE_EXISTING` ? You said earlier in the answer that `createTempFile()` would not allow duplicate names ence my confusion. – Ced Jan 18 '16 at 19:33
  • 1
    @Ced: because an empty temp file is physically created (and thus already exists) before writing. It works this way so the system can guarantee that no other temp file with coincidentally same name will be created by other thread between the creation and the writing. – BalusC Jan 18 '16 at 19:34
  • it's "getInputstream()" with lowercased "s" & not "getInputStream()", please edit – Mehdi Bouzidi Sep 04 '17 at 08:58
  • @BalusC when its a temp file why do you need to give it a path? There is no such constructor reference as well anyway – Ali Dec 01 '17 at 07:42
  • @AliAdnan: the answer consists of text and code. Read both and not only the code. – BalusC Dec 01 '17 at 07:48
1

PrimeFaces uses two file upload decoder for uploading content of p:fileupload

  1. NativeFileUploadDecoder
  2. CommonsFileUploadDecoder

NativeFileUploadDecoder is the default upload decoder and it requires servlet container 3.0. So if your servlet container is less than 3 then it will not work with you. Also some application servers make a restriction on using multi-part content

So if you have a problem with the native upload decoder for any reason you have to use the other decoder which is "CommonsFileUploadDecoder"

CommonsFileUploadDecoder depends on Apache common lib so you have to put the commons-fileupload and commons-io jars in your class-path the version depends on your primefaces version but 1.3 and 2.2 respectively works for me.

to use CommonsFileUploadDecoder you have to use filter

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

wait the filter only will not work because inside the filter it checks about the uploader if its not provided and it detect at least JSF 2.2 so it will bypass the request to the default decoder "Native" and then it will not work for your also

to force the filter to use the common decoder you have to put the following context param in your web.xml

<context-param>
<param-name>primefaces.UPLOADER</param-name>
   <param-value>commons</param-value>
</context-param>
Kukeltje
  • 12,223
  • 4
  • 24
  • 47
HMoussa
  • 11
  • 1