Search code examples
jsfjsf-2primefaces

Primefaces, p:imageCropper could not display image after upload


My admin dashboard is built on spring with JSF 2.2 + Primefaces 5.3.

I am developing form for creating new entities, which should work as follows:

  1. User fill in all input fields and upload image (using <p:fileUpload).
  2. Then, the <p:imageCropper should be refreshed and displayed uploaded image to be cropped.
  3. User cropped image, and press submit button to create new entity and store image on the disk.

Now, I stuck on the 2 step. Uploaded image is not displayed by <p:imageCropper.

I have doubts if it's possible to get image to the <p:imageCropper image attribute from the StreamedContent obj from the managed bean, like for <p:graphicImage?

So, please let me know how it could be solved?

Here is my form:

<h:form prependId="false" id="createForm" enctype="multipart/form-data">
            <p:separator />
                <p:growl id="messages" showDetail="true" />
                <p:outputLabel value="Create card:" />
                <h:panelGrid columns="2" cellpadding="5">
                    <p:outputLabel value="Fill in Name:" for="cardName" />
                    <p:inputText id="cardName" value="#{cardController.name}"
                        required="false" requiredMessage="Please, enter new card Name" />
                    <p:outputLabel value="Fill in Description:" for="cardDescription" />
                    <p:inputTextarea id="cardDescription" rows="3" cols="33"
                        value="#{cardController.description}" />
                    <p:outputLabel value="Upload image:" for="cardImage" />
                    <p:fileUpload id="cardImage"
                        fileUploadListener="#{cardController.handleFileUpload}"
                        auto="false"
                        mode="advanced" dragDropSupport="false" update="messages cropped uploaded"
                        sizeLimit="100000" fileLimit="3"
                        allowTypes="/(\.|\/)(gif|jpe?g|png)$/" />

                    <p:outputLabel value="change size of the image:" for="cropped" />
                    <p:imageCropper id="cropped" value="#{cropperView.croppedImage}" image="#{cardController.image}" initialCoords="225,75,300,125"/>
                    <p:graphicImage id = "uploaded" value="#{cardController.image}"/>
                    <p:commandButton value="Create"
                        action="#{categoryController.create()}"
                        update=" createForm" />
                    <p:message for="cardName" />


                </h:panelGrid>
        </h:form>

Controller:

@Controller
@Scope("session")
public class CardController {

    private CroppedImage croppedImage;
    private UploadedFile uploadedFile;
    private String name;
    private String description;

    public void handleFileUpload(FileUploadEvent event) throws IOException {
        System.out.println("!!! inside file upload");
        FacesMessage message = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
        FacesContext.getCurrentInstance().addMessage(null, message);
        uploadedFile = event.getFile();

    }

    public StreamedContent getImage() {
        FacesContext context = FacesContext.getCurrentInstance();

        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            // So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
            return new DefaultStreamedContent();
        }
        else {
            // So, browser is requesting the image. Return a real StreamedContent with the image bytes.
            return new DefaultStreamedContent(new ByteArrayInputStream(uploadedFile.getContents()));
        }
    }

    public void create() {
        // shoul be logic for creating new entities
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public CroppedImage getCroppedImage() {
        return croppedImage;
    }

    public void setCroppedImage(CroppedImage croppedImage) {
        this.croppedImage = croppedImage;
    }

    public UploadedFile getUploadedFile() {
        return uploadedFile;
    }

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

}

Solution

  • So, I have solved my issue this way:

    1. After image was uploaded, store it in temp directory on disk.
    2. Update component where <p:imageCropper is located. The image attribute of <p:imageCropper should equals to the path created above(e.g: value="http://localhost:8080/temp/#{bean.imageName}"). Note, that directory on the disk, should be visible to your web container ( My app is build on Spring boot, therefore it uses embedded Tomcat. So, I have installed additional apache server and save my images there).
    3. Crop the image and store it on the disk.

    Here is some code:

    <p:fileUpload id="cardImage"
                        fileUploadListener="#{cardController.handleFileUpload}"
                        auto="true" mode="advanced" dragDropSupport="true"
                        update="messages cropped cropButton" sizeLimit="5000000"
                        allowTypes="/(\.|\/)(gif|jpe?g|png)$/" />
        <h:panelGrid columns="2" cellpadding="5" id="cropped">
                        <p:outputLabel value="Crop image:" for="cropper"
                            rendered="#{not empty cardController.imageFilePath}" />
                        <p:imageCropper id="cropper"
                            rendered="#{not empty cardController.imageFilePath}"
                            value="#{cardController.image}"
                            image="#{cardController.hostName}/#{cardController.imageFilePath}"
                            aspectRatio="1.0" initialCoords="225,75,300,125" minSize="210,210" />
                        <p:outputLabel value="Cropped image:" for="image"
                            rendered="#{not empty cardController.croppedImageFileName}" />
                        <p:graphicImage id="image"
                            rendered="#{not empty cardController.croppedImageFileName}"
                            value="#{cardController.hostName}/images/#{cardController.categoryId}/#{cardController.levelId}/#{cardController.croppedImageFileName}" />
                        <p:commandButton id="cropButton"
                            rendered="#{not empty cardController.uploadedFile}" value="Crop"
                            action="#{cardController.crop()}" update="messages cropped create"
                            icon="ui-icon-scissors" />
                        <p:commandButton value="Create" id="create"
                            rendered="#{not empty cardController.croppedImageFileName}"
                            action="#{cardController.create()}"
                            update=":cardForm:table createForm" />
                        <p:message for="cardName" />
                    </h:panelGrid> 
    

    Controller:

    public void handleFileUpload(FileUploadEvent event) throws IOException {
            uploadedFile = event.getFile();
            File directory = new File(rootPath + TEMP_DIRECTORY);
            directory.mkdirs();
            String filename = FilenameUtils.getBaseName(UUID.randomUUID().toString());
            String extension = FilenameUtils.getExtension(uploadedFile.getFileName());
            Path file = Files.createTempFile(directory.toPath(), filename + "-", "." + extension);
            Files.copy(uploadedFile.getInputstream(), file, StandardCopyOption.REPLACE_EXISTING);
    
            imageFilePath = TEMP_DIRECTORY + "/" + file.getFileName().toString();
            FacesMessage message = new FacesMessage("Succesful", imageFilePath + " is uploaded.");
            FacesContext.getCurrentInstance().addMessage(null, message);
        }
    public void crop() throws IOException {
            if (image == null) {
                return;
            }
            String imageName = URLUtils.slugify(StringUtils.transliterateRu2En(name));
            String croppedImagePath = IMAGES_ROOT_DIRECTORY + "/" + categoryId + "/" + levelId;
            File directory = new File(rootPath + croppedImagePath);
            directory.mkdirs();
    
            String filename = FilenameUtils.getBaseName(imageName);
            String extension = FilenameUtils.getExtension(image.getOriginalFilename());
            Path file = Files.createTempFile(directory.toPath(), filename + "-", "." + extension);
            InputStream is = new ByteArrayInputStream(image.getBytes());
            Files.copy(is, file, StandardCopyOption.REPLACE_EXISTING);
    
            croppedImageFileName = file.getFileName().toString();
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Success", "Cropping finished."));
        }