Display uploaded image in JSF

Viewed 28783

I have a view scoped bean where I create a person. A person can have a picture. This picture is uploaded the same page the person is created. The picture is not stored in a database or on disk (since the person isn't created yet). The bean has to be view scoped since a person can be created elsewhere and this uses the same bean. If the bean is session scoped and a user uploads a picture but does not save the person, the picture will be displayed next time the user tries to create a person.

I solved this by using two beans; one view scoped bean to create the person and a session scoped bean to upload the picture and to get the picture as a stream. This however causes the problem noted above.

How can I solve this in a better way?

The upload bean:

@ManagedBean(name = "uploadBean")
@SessionScoped
public class UploadBean
{
    private UploadedFile    uploadedFile;

    public UploadedFile getUploadedFile()
    {
        return uploadedFile;
    }

    public StreamedContent getUploadedFileAsStream()
    {
        if (uploadedFile != null)
        {
            return new DefaultStreamedContent(new ByteArrayInputStream(uploadedFile.getContents()));
        }
        return null;
    }

    public void uploadFile(FileUploadEvent event)
    {
        uploadedFile = event.getFile();
    }
}

The create-a-person bean:

@ManagedBean(name = "personBean")
@ViewScoped
public class PersonBean
{
    private Person newPerson = new Person();

    public Person getNewPerson()
    {
        return newPerson;
    }

    private UploadedFile getUploadedPicture()
    {
        FacesContext context = FacesContext.getCurrentInstance();
        ELContext elContext = context.getELContext();
        UploadBean uploadBean = (UploadBean) elContext.getELResolver().getValue(elContext, null, "uploadBean");
        return uploadBean.getUploadedFile();
    }

    public void createPerson()
    {
        UploadedFile uploadedPicture = getUploadedPicture();
        // Create person with picture;
    }
}

The relevant JSF page part:

<h:form enctype="multipart/form-data">
    <p:outputPanel layout="block" id="personPicture">
        <p:graphicImage height="150"
            value="#{uploadBean.uploadedFileAsStream}"
            rendered="#{uploadBean.uploadedFileAsStream != null}" />
    </p:outputPanel>
        <p:fileUpload auto="true" allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
            fileUploadListener="#{uploadBean.uploadedFile}"
            update="personPicture" />
    <p:commandButton value="Save" actionListener="#{personBean.createPerson()}"/>
</h:form>
3 Answers

I managed to do that by simply encoding uploaded image to base64 and then showing it normally via html <img> tag.

Here's my managed bean:

@ManagedBean
@ViewScoped
public class ImageMB {

private String base64Image;

public void onUploadImage(FileUploadEvent event) {
    String fileName = event.getFile().getFileName();
    //Get file extension.
    String extension = "png";
    int i = fileName.lastIndexOf('.');
    if (i > 0) {
        extension = fileName.substring(i + 1).toLowerCase();
    }

    String encodedImage = java.util.Base64.getEncoder().encodeToString(event.getFile().getContents());
    this.base64Image = String.format("data:image/%s;base64, %s", 
         extension, encodedImage));
}

And here's JSF part:

<p:fileUpload id="imageFileUploader"
              fileUploadListener="#{imageMB.onUploadImage}"
              mode="advanced"    
              multiple="false"
              fileLimit="1"
              allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
              update="@form"/>

<div>
    <img src="#{toolAddEditMB.base64Image}" 
         style="#{toolAddEditMB.base64Image eq null ? 'display: none' : ''}"/>
</div>
Related