1

I looked around and just couldn't find what I'm looking for without additional plugins/libraries. I want to upload an image and process it serverside via JQuery AJAX, but I can't figure out how to pass and process it. Any help is much appreciated!

J S
  • 3,324
  • 4
  • 20
  • 27
  • does it have to be jQuery or AJAX? I would recommend node.js for that sort of thing. – Nilzone- Oct 09 '13 at 20:29
  • 1
    create a `
    `, give it the correct attributes for posting an image to the server, set it's target at an iframe that is hidden, and then submit the form. There, you uploaded an image without reloading the page, AND, it didn't require any javascript!
    – Kevin B Oct 09 '13 at 20:29
  • 1
    AJAX does not natively support binary file transfers. You'd need to base-64 encode it, send it as a text blob in an AJAX call, decode it on the server and save it back to binary. You want to write all that yourself - go ahead. – Diodeus - James MacFarlane Oct 09 '13 at 20:30
  • 1
    @Diodeus well, technically you could do it with `window.FormData` in browsers that support `window.FormData` – Kevin B Oct 09 '13 at 20:31
  • 1
    possible duplicate of [How can I upload files asynchronously with jQuery?](http://stackoverflow.com/questions/166221/how-can-i-upload-files-asynchronously-with-jquery) – Kevin B Oct 09 '13 at 20:31
  • Good call Kevin. Here's an interesting list of supported browsers: http://ahedg.es/webkit/formdata.html – Diodeus - James MacFarlane Oct 09 '13 at 20:33

1 Answers1

2

Though Diodeus is correct, it isn't quite that difficult. Just maddening.

HTML5 does expose what is called the FileReader API, which is still relatively new and unsupported on legacy browsers, but which will make your job easier. I have a small app which accepts images on the client side and, using the FileReader API, converts them to base-64 for uploading to the server.

The following is the function I call upon a user's uploading an image. App.FileReader is an instantiation of the HTML5 FileReader, which is declared simply like:

App.FileReader = window.FileReader ? new FileReader : null;

Upon upload, I read the image as a dataURL using the FileReader, and push the data into an unused tag. The FileReader itself retains the read data, which is why it is a good idea to only instantiate one FileReader at a time.

    if (input.files && input.files[0]) {

        if (App.FileReader) {

            App.FileReader.onload = function (e) {
                $('#createMomentImagePreview').attr('src', e.target.result);
            }

            App.FileReader.readAsDataURL(input.files[0]);

            $this.uploadedImage = true
        }

        else {
            $('#createMomentImagePreview').attr('src', 'http://d33w6ffaa49e6z.cloudfront.net/media/ImageLoaded.png');

            $this.uploadedImage = true
        }
    }

This is the AJAX call for uploading to the server, where data represents the read file, or "App.FileReader.result":

    $.ajax({
        url: '/image',
        type: 'POST',
        data: {
            image: App.FileReader.result
        }
    }).done(function(data){

        callback(data);

    }).fail(function() {
        console.log("Image upload failed!")
        alert("Sorry, there was an error uploading your image to the database.")
    })

Server-side (and I'm using Node with Express, so this might not apply), I can convert the base64 string to a Buffer (Blob) and send that up to S3 using Knox's putBuffer. This is waaaaaay simpler than actually authenticating with S3 AND trying to get it to play nice with your binary data.

if (req.body.image.match(/^data:image\/png;base64,/)) {
    var image = new Buffer(req.body.image.replace(/^data:image\/png;base64,/,""), "base64");
}

else if (req.body.image.match(/^data:image\/jpeg;base64,/)) {
    var image = new Buffer(req.body.image.replace(/^data:image\/jpeg;base64,/,""), "base64");
}

awsClient.putBuffer(image, '/' + imagePath + '.jpg', headers, function(err, stream) {

          if (err) {
            console.log(err);
            return false
          }

          res.send(200, imagePath + '.jpg')

          image = null;

        });

In either case, once you have the base64 data on your server you've made significant progress. You could use ImageMagick to handle processing as well. For what it's worth, I hope this helps you out in some way!

Ryan Miller
  • 1,053
  • 9
  • 22
  • Thanks so much for the response! I'm actually already using FileReader so this just might come in handy. Right now I'm trying a hidden IFrame that will pass a javascript variable back to the parent window to denote success (or lack thereof) and don't see why it wouldn't work. If it doesn't though, I will definitely take a closer look at your solution. Thanks again! – J S Oct 09 '13 at 22:36
  • No problem! Hmm... I should think that using a $.change() event on your file input might be a better way to listen. It will only trigger if the file upload is successful, and it's worked like a charm for kicking off my app above. – Ryan Miller Oct 09 '13 at 22:47