1

I'd like to upload an image generated by a jquery cropper to a bean field.

Client side I ve found this:

<p:fileUpload
    id="imgInp"
    mode="simple" allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>

    <img id="blah" src="#" alt="your image" />
    <p:imageCropper image="" />

<script>
    var reader = new FileReader();
    reader.onload = function (e) {
        $('#blah').attr('src', e.target.result);
    }

       function readURL(input) {
            if (input.files &amp;&amp; input.files[0]) {
                reader.readAsDataURL(input.files[0]);
            }
        }

        $("#imgInp").change(function(){
            readURL(this);
        });
</script>

It displays the image without uploading it but I can't get it in the cropper. So I use a jquery cropper but then I'm not sure how to get it in the bean (without going through servlets, because it's not a one time use). In other words I need to send an img through ajax to the bean.


Otherwise I'd use primeface but it has to go through the wire which I'd like to avoid if possible. From the example I've seen the image is static content on the server. Do I really have to save the image on the server ? Can't I keep it as object and convert the UploadedFile to something the cropper will accept ?

Soomething like this:

<p:fileUpload
        mode="advanced" allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
        fileUploadListener="#{bean.uploadPicListenner}"
        update="cropper"/>                  

<h:panelGroup id="cropper" >
    <p:imageCropper  image="#{bean.img}"/>
</h:panelGroup>

public void uploadPicListenner(FileUploadEvent e) {
    img = e.getFile();
    RequestContext.getCurrentInstance().update("ptform:cropper");
}
Community
  • 1
  • 1
Ced
  • 12,657
  • 11
  • 62
  • 124
  • Break down your problem to manageble parts. You **know** you can send an input to a bean. And what then remains is how can I read an image and put it as data in an input (totally non jsf) – Kukeltje Jan 06 '16 at 18:38
  • @Kukeltje yeah I'm almost done. I thought about deleting my question in the main time... – Ced Jan 06 '16 at 18:45
  • Don't, just improve the question and create an answer to it yourself – Kukeltje Jan 06 '16 at 18:52
  • @Kukeltje yeah, I meant in the meantime till I finish then I post an answer and undelete it. That way people don't spend time thinking about it. I don't know. Anyway I'll have an answer after dinner. – Ced Jan 06 '16 at 18:57

1 Answers1

2

It's actually quite simple once the process is understood despite some misleading answers that can be found online. I hope this is gonna help someone in the future.

The technique I used is to :

  1. Pick image

  2. Once image is picked, display it in a cropper without sending it through the wire.

  3. Here there are a few option and I choosed to : When user moves the cropper rectangle around, the coordinates of the rectangle populate an hidden input field.

  4. Send the coordinate to bean and crop it on server side.

    I did it this way because the cropper jquery lib I wanted to use didn't transform the image to base 64 and just gave the coordinates of a rectangle. However if someone want to send the cropped image directly in the future I figured it would be really easy. Just like I did except you have to put the cropped image as string base 64 in a hidden input text (instead of rectangle coordinates - this is explained under-) and transform it back on the server side, that's all. (I don't know how efficient / secure that is however). At least that resolved my issue I had with primefaces, which was not wanting to send unnecessary data over the wire multiple times.

1. First let's display the image without sending it to the server.

At this point when the image is displayed if you check inside the src tag of the img you will see that it is the data of the image as base 64:

src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAAQABAAD/2wCEAAYEBQYFBAYGBQ...

<h:form id="lolo"  enctype="multipart/form-data">

    <p:fileUpload
        value="#{adminCreateTeam.teamImg}"
        mode="simple" allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>
    <img id="blah" src="#" alt="your image" />

</h:form> 


<script>
    var reader = new FileReader();

    reader.onload = function (e) {
        $('#blah').attr('src', e.target.result);
    }

   function readURL(input) {
        if (input.files &amp;&amp; input.files[0]) {
            reader.readAsDataURL(input.files[0]);      
        }
    }

    $("#lolo\\:imgInp").change(function(){
        readURL(this);
    });
</script>
  1. Once we have done that it becomes a bit dependent on the jquery cropping library used. I used the cropper lib. With that one we want to have the coordinate of the cropped rectangle. With the coordinates data we populate an hidden input and send it back to the bean to then recrop it on java side.

Alternatively a better solution (imo) would be to use a library that crop the image and give the data client side as base 64, populate an hidden field and send it back to the bean, to then convert base 64 to an image. Each of these steps is quiet easy and can be found on stackoverflow.

Since I wanted to use the cropper library I did it the first way:

This is added inside the form:

<h:inputHidden value="#{adminCreateTeam.rect}"/>
<p:commandButton value="submit" action="#{adminCreateTeam.picTest}" ajax="false"/>

This is the updated onload:

    // with this the hidden field is gonna be populated by the 
    // cropping rectangle data.
       var $imageCrop = $('#blah').cropper({
          aspectRatio: 1/1,
          viewMode: 1,
          crop: function(e) {
            // Output the result data for cropping image.
            // string with all the data delimited by /
            $('#lolo\\:hiddenB64').val(e.x + '/' + e.y + '/' + e.width + '/' + e.height);

          }
        });

 //So the image changes in the cropper when a new image is picked
    reader.onload = function (e) {
        $imageCrop.cropper('replace',e.target.result);  
    }

We crop the image on with java

   public void picTest() {
    //getting coord.
    String data[] = rect.split("/");
    try (InputStream in = new ByteArrayInputStream(teamImg.getContents())) {
        BufferedImage bImageFromConvert = ImageIO.read(in);
        // line under this crops. It's possible there is a zoom to figure out, I didn't check yet. Seemed correct on first and only try. In any case you'll figure it out
        //  surely the parsing shouldn't be here but I need to sleep real bad.
        BufferedImage dest = bImageFromConvert.getSubimage((int)(Double.parseDouble(data[0])), (int)(Double.parseDouble(data[1])),
                (int)(Double.parseDouble(data[2])), (int)(Double.parseDouble(data[3])));
        // path to the folder
        Path folder = Paths.get(dirs.getString("imgTeams"));
        String filename = "team_pic";
        String extension = FilenameUtils.getExtension(teamImg.getFileName());
        Path file = Files.createTempFile(folder, filename + "-", "." + extension);
        ImageIO.write(dest, "jpeg", file.toFile());

    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
FSRezende
  • 53
  • 1
  • 6
Ced
  • 12,657
  • 11
  • 62
  • 124