7

I am using PrimeFaces 5.3 <p:fileUpload> to upload a PNG image and I would like to show a preview of it in <p:graphicImage> before saving in database.

Here's a MCVE:

<h:form enctype="multipart/form-data">
    <p:fileUpload value="#{bean.uploadedFile}" mode="simple" />
    <p:graphicImage value="#{bean.image}" />
    <p:commandButton action="#{bean.preview}" ajax="false" value="Preview" />
</h:form>

private UploadedFile uploadedFile;

public UploadedFile getUploadedFile() {
    return uploadedFile;
}

public void setUploadedFile(UploadedFile uploadedFile) {
    this.uploadedFile = uploadedFile;
}

public void preview() {
    // NOOP for now.
}

public StreamedContent getImage() {
    if (uploadedFile == null) {
        return new DefaultStreamedContent(); 
    } else {
        return new DefaultStreamedContent(new ByteArrayInputStream(uploadedFile.getContents()), "image/png"); 
    }
}

No error occurring on the backing bean, and the image won't be load and display at front-end. The client mentions that the image returned a 404 not found error.

BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
Lance Zhao Zai
  • 365
  • 3
  • 6
  • 16
  • You say the upload works so how is this issue [tag:file-upload] related and not [tag:graphic-image] – Kukeltje Aug 07 '16 at 19:23
  • because the `byte[]` source is come from the upload content, so i had obtained the upload content `byte[]`, but when i use the upload content to be display on the web page, its not gonna display and saying that the resource is not found – Lance Zhao Zai Aug 07 '16 at 19:37
  • So you mean that if you manually put a byte[] in the database, not via the file-upload, it works? – Kukeltje Aug 07 '16 at 19:38
  • Sorry sir, you misunderstood my question, I have not tried database yet, as it is not working via the file-upload – Lance Zhao Zai Aug 07 '16 at 19:41
  • The flow are suppose to be like "Upload Image -> Preview Image -> Store into Database" in expecting, but for now I cannot Preview Image because the content from Image Upload cannot be display – Lance Zhao Zai Aug 07 '16 at 19:44
  • @BalusC please review my updated question, the issue is exactly same – Lance Zhao Zai Aug 07 '16 at 19:55
  • I did not save the uploaded file, I stored in variable and reuse as soon as I submitting to display for preview – Lance Zhao Zai Aug 07 '16 at 19:57
  • yes correct, i wonder it is an "Unsaved" uploaded file, as I had assign into a variable for temporary use, only perform permanent saving after a final confirmation for user. – Lance Zhao Zai Aug 07 '16 at 20:07
  • So I must perform storing the file somewhere else in the file system or database? there are no way to display the image without saving? – Lance Zhao Zai Aug 07 '16 at 20:16
  • sure there is, in a longer scoped bean... But would you want to? – Kukeltje Aug 07 '16 at 20:19
  • longer scoped bean? Pardon me, I am not well understanding in scoped bean concept, please guide me – Lance Zhao Zai Aug 07 '16 at 20:23
  • 1
    http://stackoverflow.com/questions/7031885/how-to-choose-the-right-bean-scope – Kukeltje Aug 07 '16 at 22:36

1 Answers1

11

Your problem is two-fold. It failed because the uploaded file contents is request scoped and because the image is requested in a different HTTP request. To better understand the inner working, carefully read the answers on following closely related Q&A:

To solve the first problem, you need to read the uploaded file contents immediately in the action method associated with the form submit. In your specific case, that would look like:

private UploadedFile uploadedFile;
private byte[] fileContents;

public void preview() {
    fileContents = uploadedFile.getContents();
}

// ...

To solve the second problem, your best bet is to use the data URI scheme. This makes it possible to render the image directly in the very same response and therefore you can safely use a @ViewScoped bean without facing "context not active" issues or saving the byte[] in session or disk in order to enable serving the image in a different request. Browser support on data URI scheme is currently pretty good. Replace the entire <p:graphicImage> with below:

<ui:fragment rendered="#{not empty bean.uploadedFile}">
    <img src="data:image/png;base64,#{bean.imageContentsAsBase64}" />
</ui:fragment>

public String getImageContentsAsBase64() {
    return Base64.getEncoder().encodeToString(imageContents);
}

Note: I assume that Java 8 is available to you as java.util.Base64 was only introduced in that version. In case you're using an older Java version, use DatatypeConverter.printBase64Binary(imageContents) instead.

In case you happen to use JSF utility library OmniFaces, you can also just use its <o:graphicImage> component instead which is on contrary to <p:graphicImage> capable of directly referencing a byte[] and InputStream bean property and rendering a data URI.

<o:graphicImage value="#{bean.imageContents}" dataURI="true" rendered="#{not empty bean.imageContents}">
Community
  • 1
  • 1
BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
  • Thank you @BalusC the issue now has been totally verified and had resolved according to your solution approach, very thanks to your effort – Lance Zhao Zai Aug 09 '16 at 04:50
  • 1
    You are awesome @BalusC! – Dandelion Jan 01 '17 at 12:56
  • @BalusC why in my case didn't works? Repository https://github.com/zaknafein83/uploadimage-primefaces-tutorial.git When i start, io get a nullpointer exception on method to convert byte[] content into String (Base64). If i add a null pointer control, i never get the preview of the image – Zaknafein Jul 22 '20 at 10:34