3

I used a jquery plugin to crop images. The plugin will crop the image and give it to me as a base64 encoded string. In order to upload it to S3, I need to convert this into a file and pass the file into the upload function. How can I do this? I tried a lot of things including decoding the string using atob. None worked.

Here's the code of the cropping plugin ('croppie') which gives me the encoded string :

imageCropper.croppie('result', {
    type: 'canvas',
    size: 'viewport',
    format: 'jpeg'
}).then(function (resp) {
  updateAvatar(resp);
});

I pass it to a function called updateAvatar. Here's the updateAvatar function :

updateAvatar({Meteor, Slingshot}, avatar) {
  const uploader = new Slingshot.Upload('userAvatarUpload');

  uploader.send(avatar, function (error, downloadUrl) {
    if (error) {
      // Log service detailed response.
      console.error('Error uploading', uploader.xhr.response);
      console.log(error);
    }
    else {
      console.log('uploaded', downloadUrl);
    }
  });
}

The uploader.send function expects a file or a url. It won't accept my encoded string.

The plugin which I use to upload files to S3 : https://github.com/CulturalMe/meteor-slingshot

THpubs
  • 7,154
  • 13
  • 60
  • 133
  • Possible duplicate [http://stackoverflow.com/questions/21227078/convert-base64-to-image-in-javascript-jquery](http://stackoverflow.com/questions/21227078/convert-base64-to-image-in-javascript-jquery) – IronAces Jul 29 '16 at 12:19
  • 1
    @DanielShillcock That answer render the image in the dom. I want the image to be converted to a file and passed into a server function. – THpubs Jul 29 '16 at 12:20
  • @THpubs It does render the image in the DOM, but the principle may be the same where you create an `Image` object and use that to communicate with S3. – IronAces Jul 29 '16 at 12:23
  • 1
    @THpubs Can you share code, or documentation on what S3 is expecting to receive in this function – IronAces Jul 29 '16 at 12:23
  • @Ionut I just added the sample code :-) – THpubs Jul 29 '16 at 12:25
  • @DanielShillcock Thanks. I just added some sample code and also a link to the file upload plugin i'm using. – THpubs Jul 29 '16 at 12:25
  • 1
    @THpubs Does this help? https://github.com/CulturalMe/meteor-slingshot/issues/114 – IronAces Jul 29 '16 at 12:30
  • @DanielShillcock Thanks but unfortunately, the cropping tool give me the cropped image in an encoded string. If I try to get the blob from the canvas I might get the un cropped image. I can however render the image to the dom as you have shown earlier and get the blob from there. I will try it as a last resort if I didn't find a better way. Thanks! :-) – THpubs Jul 29 '16 at 12:37

2 Answers2

8

It seems like the missing 'brick' in your code is a function that would take a base64-encoded image and convert it to a Blob.

So, I'm going to focus on that part exclusively with a short comment for each step.

The following function expects a string such as:

data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICA...

function base64ImageToBlob(str) {
  // extract content type and base64 payload from original string
  var pos = str.indexOf(';base64,');
  var type = str.substring(5, pos);
  var b64 = str.substr(pos + 8);

  // decode base64
  var imageContent = atob(b64);

  // create an ArrayBuffer and a view (as unsigned 8-bit)
  var buffer = new ArrayBuffer(imageContent.length);
  var view = new Uint8Array(buffer);

  // fill the view, using the decoded base64
  for(var n = 0; n < imageContent.length; n++) {
    view[n] = imageContent.charCodeAt(n);
  }

  // convert ArrayBuffer to Blob
  var blob = new Blob([buffer], { type: type });

  return blob;
}
Arnauld
  • 5,485
  • 2
  • 14
  • 28
  • Thanks. Your explanations are very helpful. – THpubs Jul 29 '16 at 16:04
  • Glad it helped. (And just to be clear about it, I won't mind at all if you accept @DanielShillcock's answer as promised. ^^) – Arnauld Jul 29 '16 at 16:14
  • Actually im stuck in there. His answers and comments helped me and your answer made things even more clear. Hope I can select both :P But I might wait for others to vote and then pick a good one. Hope everyone agree to it. – THpubs Jul 29 '16 at 16:17
1

Convert the base64 string to blob, to be used in upload to S3. There are tidier ways of doing this of course! :)

Original SO Answer here: https://stackoverflow.com/a/16245768/1350913

  imageCropper.croppie('result', {
  type: 'canvas',
  size: 'viewport',
  format: 'jpeg'
}).then(function(resp) {
  var contentType = 'image/png';
  var s3Blob = b64toBlob(resp, contentType);
  updateAvatar(s3Blob);
});

updateAvatar({
  Meteor,
  Slingshot
}, avatar) {
  const uploader = new Slingshot.Upload('userAvatarUpload');

  uploader.send(avatar, function(error, downloadUrl) {
    if (error) {
      // Log service detailed response.
      console.error('Error uploading', uploader.xhr.response);
      console.log(error);
    } else {
      console.log('uploaded', downloadUrl);
    }
  });
}

function b64toBlob(b64Data, contentType, sliceSize) {
  contentType = contentType || '';
  sliceSize = sliceSize || 512;

  var b64DataString = b64Data.substr(b64Data.indexOf(',') + 1);      
  var byteCharacters = atob(b64DataString);
  var byteArrays = [];

  for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
    var slice = byteCharacters.slice(offset, offset + sliceSize);

    var byteNumbers = new Array(slice.length);
    for (var i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }

    var byteArray = new Uint8Array(byteNumbers);

    byteArrays.push(byteArray);
  }

  var blob = new Blob(byteArrays, {
    type: contentType
  });
  return blob;
}
Community
  • 1
  • 1
IronAces
  • 1,743
  • 1
  • 25
  • 34
  • Great thanks a lot! It worked. But a small correction. The line `var byteCharacters = atob(b64Data);` gives an error because the encoded string have the file type and other data. We need to remove them like this : `var b64DataString = b64Data.substr(b64Data.indexOf(',') + 1);` Ref : http://stackoverflow.com/questions/24289182/how-to-strip-type-from-javascript-filereader-base64-string Can you please correct is so I can accept the answer? – THpubs Jul 29 '16 at 12:56