3

I know there are a lot of StackOverflow questions about this already, but I've searched through as many as I could find and have yet to get my code working, so I am finally posting my own question.

My goal is to save an image that is on an HTML5 <canvas> in my webpage to a file on my server. I was hoping to accomplish this using a Java servlet.

My JavasScript grabs the canvas image data like this:

var canvas = document.getElementById("screenshotCanvas");
var context = canvas.getContext("2d");                    
var imageDataURL = canvas.toDataURL('image/png');
// I'm not if I need to do this, I've tried several different ways to no avail
//imageDataURL = imageDataURL.replace("image/png", "image/octet-stream");
//imageDataURL = imageDataURL.replace(/^data:image\/(png|jpeg);base64,/,"");

$.ajax({
    url: screenshotCreateUrl,
    type: "POST",
    data: { imgBase64: imageDataURL },
    error: function(jqXHR, textStatus, errorThrown) {
        // Handle errors
    },
    success: function(data, textStatus, jqXHR) {
        // Do some stuff
    }
});

My Java servlet tries to save the image like so:

try {
    HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request);
    HttpServletRequestWrapper requestWithWrapper = (HttpServletRequestWrapper) wrappedRequest.getRequest();
    byte[] contentData = requestWithWrapper.getContentData();
    byte[] decodedData = Base64.decodeBase64(contentData);          
    FileOutputStream fos = new FileOutputStream("testOutput.png");
    fos.write(decodedData);
    fos.close();
} catch(Exception e) {
    // Handle exceptions
}

The servlet successfully writes out an image file, but it does not open properly and does not contain all the image data in it. My Javascript successfully grabs the <canvas> image data, which looks like this:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAACWCAYAAABkW7XSAAAgAElEQVR4nJTa51NcaZ7ge+QBIZBAwgoEkvDeJt577733njRk4r3PhDRAJkkmiTdCXqWqUlVXtZl209OzvWN27t17/5rvvoCu7t6ZmN158YnfcyJOnIjz4vnGEyeOWdRmAP+RaI3fT2I0PiTtBJF9KKD8TTKVb5IpPosl+zCMdFMgKQY/otTPCFl3IljhSPiWB5E7XkToPAnVexNq8CVyP5Cwg0CCDwII2vfHx+TDC6MXz3df8mznGc7bTthu2PJwxRrrufvcnzbn4bQFjnNWuK3a4r3hQrDmBSGbLwlRvyR0w5OQ9ZeErHsRsuFLyIYvQeu+BCh88Zf74K/wxU8ZgL8qEH9VEAHq0L8RqAkjeCuCsO1IwrVRhGujCNNdCdVFEayNJEQXdUUfTYg+mmC9AH99BL47YfjuhOK7G4KvIRhfQzDee4F47QXibQrCe98f730/vPf98N3zw9/kT+B+IG . . . [and so on]

Any ideas what I am missing here? I feel like i am making some tiny mistake that I just can't spot.

Steph
  • 2,135
  • 6
  • 31
  • 44
  • Up to now I even didn´t know that JavaScript was able to encode the canvas as image.... Why don´t you try to pass the data as request-parameter and just get it with request.getParameter("imgBase64") ? – cljk Jun 18 '13 at 21:18
  • Also I would simplify the JS to $.post(screenshotCreateUrl, {imgBase64: imageDataURL }); – cljk Jun 18 '13 at 21:20

2 Answers2

3

Had the same task and was able to make it work (without jQuery and with the help of maclema's reply), by using multipart/form-data content type:

var xhr = new XMLHttpRequest();
xhr.open("post", "AddressOfYourServlet", false);
var boundary = Math.random().toString().substr(2);
xhr.setRequestHeader("content-type", 
    "multipart/form-data; charset=utf-8; boundary=" + boundary);
var multipart = "--" + boundary + "\r\n" +
    "Content-Disposition: form-data; name=myImg\r\n" +
    "Content-type: image/png\r\n\r\n" +
    canvas.toDataURL("image/png") + "\r\n" +
    "--" + boundary + "--\r\n";
xhr.send(multipart);

To go asynchronously or if you have more parts to send (e.g. multiple images) or if you want to work with the response, see How to send multipart/form-data form content by ajax (no jquery)?

Your servlet's doPost method would look something like:

protected void doPost(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {
  Part part = request.getPart("myImg");
  BufferedReader br = new BufferedReader(new InputStreamReader(part.getInputStream(),
      Charset.forName("utf-8")));
  String sImg = br.readLine();
  sImg = sImg.substring("data:image/png;base64,".length());
  byte[] bImg64 = sImg.getBytes();
  byte[] bImg = Base64.decodeBase64(bImg64); // apache-commons-codec
  FileOutputStream fos = new FileOutputStream("img.png");
  fos.write(bImg);
}

Hope this helps.

Community
  • 1
  • 1
legolas108
  • 199
  • 1
  • 5
1

You want to get the post parameter and not the content data of the request. As well you will also need to strip the encoding information.

Try this:

try {
    HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request);
    HttpServletRequestWrapper requestWithWrapper = (HttpServletRequestWrapper) wrappedRequest.getRequest();
    String imageString = wrappedRequest.getParameter("imgBase64");
    imageString = imageString.substring("data:image/png;base64,".length);
    byte[] contentData = imageString.getBytes();
    byte[] decodedData = Base64.decodeBase64( contentData );          
    FileOutputStream fos = new FileOutputStream("testOutput.png");
    fos.write(decodedData);
    fos.close();
} catch(Exception e) {
    // Handle exceptions
    e.printStackTrace();
}
Matt MacLean
  • 19,410
  • 7
  • 50
  • 53
  • Thanks for your help, I tried this, but it's still not working fully. The `testOutput.png` file is being written out and it opens in an image viewer, but the image appears to be blank. If I open the file in a text editor, I can see that there's a lot of data in it, but it must not be formatted entirely correctly or something. – Steph Jun 21 '13 at 16:37