THpubs
THpubs

Reputation: 8162

How to convert a base64 string into a file?

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

Upvotes: 4

Views: 20801

Answers (3)

Pepe Alvarez
Pepe Alvarez

Reputation: 1624

The base64ToFile function (.ts) converts the base64 string into a File. The codeUnits and charCodes part make sure you can read Unicode text as ASCII by converting the string such that each 16-bit unit occupies only one byte.
Finally the download function (.ts) downloads the converted file from your browser to your local machine.

function base64ToFile(base64data: string, myFileNameWithdotExtention: string,
  fileType: string): File {
  let content = decodeURIComponent(escape(window.atob(base64data)));
  let fileName = myFileNameWithdotExtention;
  const codeUnits = Uint16Array.from(
    { length: content.length },
    ( _, index) => content.charCodeAt(index)
  );
  const charCodes = new Uint8Array(codeUnits.buffer);
  const type = fileType; // 'text/csv' for instance
  const blob = new Blob([charCodes], { type });
  return new File([blob], fileName, { lastModified: new Date().getTime(), type });

}

download(){
    let res: string = getMyDataFromSomewhere(); // base64 string
    let data = base64ToFile(res);
    let element = document.createElement('a');
    window.URL = window.URL || window.webkitURL;
    element.setAttribute('href', window.URL.createObjectURL(data));
    element.setAttribute('download', data.name);
    element.style.display = 'none';
    document.body.appendChild(element);
    element.click();
    document.body.removeChild(element);
}

Upvotes: 1

Arnauld
Arnauld

Reputation: 6110

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;
}

Upvotes: 13

IronAces
IronAces

Reputation: 1883

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;
}

Upvotes: 3

Related Questions