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.

2
  • 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") ? Commented Jun 18, 2013 at 21:18
  • Also I would simplify the JS to $.post(screenshotCreateUrl, {imgBase64: imageDataURL }); Commented Jun 18, 2013 at 21:20

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

@Steph If this solved your problem you could mark it as best answer :-)
Thank you so much for this help. It solves my problem nicely. Would never be able to figure this out on my own.
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();
}

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.