239

This is a snippet for the code that I want to do Blob to Base64 string:

This commented part works and that when the URL generated by this is set to img src it displays the image:

var blob = items[i].getAsFile();
//var URLObj = window.URL || window.webkitURL;
//var source = URLObj.createObjectURL(blob);
//console.log("image source=" + source);

var reader = new FileReader();
reader.onload = function(event){
console.log(event.target.result)
}; // data url!
var source = reader.readAsBinaryString(blob);

The problem is with the the lower code, the source variable generated is null

Update:

Is there an easier way to do this with JQuery to be able to create Base64 String from Blob file as in the code above?

quarks
  • 31,298
  • 67
  • 266
  • 478

13 Answers13

431
var reader = new FileReader();
reader.readAsDataURL(blob); 
reader.onloadend = function() {
  var base64data = reader.result;                
  console.log(base64data);
}

Form the docs readAsDataURL encodes to base64

As an awaitable function:

function blobToBase64(blob) {
  return new Promise((resolve, _) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result);
    reader.readAsDataURL(blob);
  });
}

Note: The blob's result cannot be directly decoded as Base64 without first removing the Data-URL declaration preceding the Base64-encoded data. To retrieve only the Base64 encoded string, first remove data:/;base64, from the result.

patelarpan
  • 5,988
  • 2
  • 18
  • 23
Arun Killu
  • 12,647
  • 5
  • 31
  • 57
  • 2
    The output String doesn't seem to look like base64? – quarks Sep 09 '13 at 01:07
  • 1
    @xybrek If you print read.result, you will see base64 in the string itself. – Nawaf Alsulami Apr 26 '14 at 05:29
  • But, Concern is how to get just Base64 String from that instead of those other tags, e.g. I am getting `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAYAAADo08FDAAAKoWlDQ…ApOxJ4m+EDQbZE3wR+j7XSz2unTElvZ+s/1Vw/8B6sw4fipgOycAAAAASUVORK5CYII=` but I want just : `iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAYAAADo08FDAAAKoWlDQ…ApOxJ4m+EDQbZE3wR+j7XSz2unTElvZ+s/1Vw/8B6sw4fipgOycAAAAASUVORK5CYII=` – Mrug Jun 12 '15 at 06:10
  • 41
    console.log( base64data.substr(base64data.indexOf(',')+1) ); – palota Aug 03 '15 at 18:59
  • 16
    `onloadend` should come before `readAsDataURL` just in case something weird happens and it finishes loading before it reaches the next line of code. Obviously this would never happen but it's still good practice. – 3ocene Dec 16 '15 at 06:00
  • 7
    This answer is incorrect, it appends none-base64 characters to the front of the string. – Joshua Smith Jan 19 '16 at 23:41
  • @Arun Killu, Seems I need your help. Look at this : https://stackoverflow.com/questions/46192069/how-can-i-solve-uncaught-referenceerror-blob-is-not-defined – moses toh Sep 13 '17 at 08:32
  • combined with https://stackoverflow.com/a/11901662/2860309 for reading from a blob url, this really helped! – therightstuff Mar 15 '20 at 21:23
  • @3ocene that can't happen. JavaScript is single-threaded, nothing weird can happen in the while. – Cristian Traìna Jun 23 '20 at 13:57
  • @CristianTraìna `readAsDataURL` is a function call, and could, therefore, complete the entire load (including calling the event listeners) before the function returns. This is not how the function is implemented in every environment I know of, but JavaScript being single-threaded doesn't have anything to do with it. Furthermore, all it takes is for some junior developer to come along and unwittingly add an await statement or something in there, and suddenly you have a bug that is incredibly difficult to troubleshoot. As I said, "obviously this would never happen but it's still good practice." – 3ocene Jun 23 '20 at 20:13
  • @JoshuaSmith Yeah; I dunno, I mean, [you could just use `btoa(await blob.text())` and skip all this extra strangeness](https://stackoverflow.com/a/72437954/616460). Maybe btoa wasn't around in 2013? – Jason C May 30 '22 at 17:34
  • @3ocene No, it can't. The [File API spec](https://www.w3.org/TR/FileAPI/#dfn-readAsDataURL) defines [read ops](https://www.w3.org/TR/FileAPI/#readOperation); and `loadend` is *never* fired directly, as all paths to `loadend` [queue the event task](https://html.spec.whatwg.org/multipage/webappapis.html#queue-a-task) on the [file reading task source](https://www.w3.org/TR/FileAPI/#blobreader-task-source), whose [implied event loop](https://html.spec.whatwg.org/multipage/webappapis.html#implied-event-loop) is the same one that's [executing the script](https://stackoverflow.com/a/72420662). – Jason C May 30 '22 at 17:52
  • I.e. it will never fire "between lines of code"; the only opportunities it has is either during async awaits, or after the script has been fully executed. Also, on a higher level, W3C went to great lengths to build determinism into the web specs; and if it wasn't there in this case, most things just... wouldn't work -- in particular, API objects that begin firing events as soon as they are constructed would be essentially unusable. – Jason C May 30 '22 at 17:58
46

this worked for me:

var blobToBase64 = function(blob, callback) {
    var reader = new FileReader();
    reader.onload = function() {
        var dataUrl = reader.result;
        var base64 = dataUrl.split(',')[1];
        callback(base64);
    };
    reader.readAsDataURL(blob);
};
Community
  • 1
  • 1
yeahdixon
  • 6,390
  • 1
  • 38
  • 42
35

There is a pure JavaScript way that is not depended on any stacks:

const blobToBase64 = blob => {
  const reader = new FileReader();
  reader.readAsDataURL(blob);
  return new Promise(resolve => {
    reader.onloadend = () => {
      resolve(reader.result);
    };
  });
};

For using this helper function you should set a callback, example:

blobToBase64(blobData).then(res => {
  // do what you wanna do
  console.log(res); // res is base64 now
});

I write this helper function for my problem on React Native project, I wanted to download an image and then store it as a cached image:

fetch(imageAddressAsStringValue)
  .then(res => res.blob())
  .then(blobToBase64)
  .then(finalResult => { 
    storeOnMyLocalDatabase(finalResult);
  });
danronmoon
  • 3,682
  • 5
  • 33
  • 56
AmerllicA
  • 23,670
  • 12
  • 111
  • 138
9
var audioURL = window.URL.createObjectURL(blob);
audio.src = audioURL;

var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function () {
     base64data = reader.result;
     console.log(base64data);
}
Kishan Bharda
  • 4,851
  • 3
  • 25
  • 48
zinsat
  • 91
  • 1
  • 4
6

So the problem is that you want to upload a base 64 image and you have a blob url. Now the answer that will work on all html 5 browsers is: Do:

  var fileInput = document.getElementById('myFileInputTag');
  var preview = document.getElementById('myImgTag');

  fileInput.addEventListener('change', function (e) {
      var url = URL.createObjectURL(e.target.files[0]);
      preview.setAttribute('src', url);
  });
function Upload()
{
     // preview can be image object or image element
     var myCanvas = document.getElementById('MyCanvas');
     var ctx = myCanvas.getContext('2d');
     ctx.drawImage(preview, 0,0);
     var base64Str = myCanvas.toDataURL();
     $.ajax({
         url: '/PathToServer',
         method: 'POST',
         data: {
             imageString: base64Str
         },
     success: function(data) { if(data && data.Success) {}},
     error: function(a,b,c){alert(c);}
     });
 }
Cybersupernova
  • 1,735
  • 1
  • 17
  • 34
A W
  • 137
  • 1
  • 4
6
function bufferToBinaryString(arrayBuffer){
    return String.fromCharCode(...new Uint8Array(arrayBuffer));
}
(async () => console.log(btoa(bufferToBinaryString(await new Response(blob).arrayBuffer()))))();

or

function bufferToBinaryString(arrayBuffer){
    return String.fromCharCode(...new Uint8Array(arrayBuffer));
}
new Response(blob).arrayBuffer().then(arr_buf => console.log(btoa(bufferToBinaryString(arr_buf)))))

see Response's constructor, you can turn [blob, buffer source form data, readable stream, etc.] into Response, which can then be turned into [json, text, array buffer, blob] with async method/callbacks.

edit: as @Ralph mentioned, turning everything into utf-8 string causes problems (unfortunately Response API doesn't provide a way converting to binary string), so array buffer is use as intermediate instead, which requires two more steps (converting it to byte array THEN to binary string), if you insist on using native btoa method.

Valen
  • 1,503
  • 1
  • 19
  • 15
  • .text() decodes using UTF8, what happens if the response has binary code? I believe this will fail for non text data – Ralph Jan 28 '20 at 22:49
  • I see your point, amended the answer (it becomes quite lengthy though). At this point it would be better to not insist on `btoa` – Valen Jan 29 '20 at 05:41
5
async function blobToBase64(blob) {
  return new Promise((resolve, _) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result);
    reader.readAsDataURL(blob);
  });
}

let blob = null; // <= your blob object goes here

blobToBase64(blob)
  .then(base64String => console.log(base64String));

See also:

Thomas
  • 7,643
  • 14
  • 43
  • 74
4

you can fix problem by:

var canvas = $('#canvas'); 
var b64Text = canvas.toDataURL();
b64Text = b64Text.replace('data&colon;image/png;base64,','');
var base64Data = b64Text;

I hope this help you

Ali Sadri
  • 1,442
  • 11
  • 14
4

Another way is to use a simple wrapper around FileReader returning Observable (snippet is in TypeScript):

  function toBase64(blob: Blob): Observable<string> {
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    return fromEvent(reader, 'load')
      .pipe(map(() => (reader.result as string).split(',')[1]));
  }

Usage:

toBase64(blob).subscribe(base64 => console.log(base64));
Vitaliy Ulantikov
  • 9,629
  • 3
  • 56
  • 53
2

Typescript version :

const blob2Base64 = (blob:Blob):Promise<string> => {
      return new Promise<string> ((resolve,reject)=> {
           const reader = new FileReader();
           reader.readAsDataURL(blob);
           reader.onload = () => resolve(reader.result.toString());
           reader.onerror = error => reject(error);
       })
      }

usage:

blob2Base64(blob).then(res=>console.log(res))
Cyber Progs
  • 3,240
  • 3
  • 27
  • 36
0

I wanted something where I have access to base64 value to store into a list and for me adding event listener worked. You just need the FileReader which will read the image blob and return the base64 in the result.

createImageFromBlob(image: Blob) {
    const reader = new FileReader();
    const supportedImages = []; // you can also refer to some global variable
    reader.addEventListener(
      'load',
      () => {
        // reader.result will have the required base64 image
        const base64data = reader.result;
        supportedImages.push(base64data); // this can be a reference to global variable and store the value into that global list so as to use it in the other part
      },
      false
    );
    // The readAsDataURL method is used to read the contents of the specified Blob or File.
    if (image) {
      reader.readAsDataURL(image);
    }
 }

Final part is the readAsDataURL which is very important is being used to read the content of the specified Blob

Asif Karim Bherani
  • 935
  • 1
  • 11
  • 25
0

Maybe I'm missing something but

let encoded = btoa(await myblob.text());

... is all you need to do to encode a Blob's data to base64. See Blob.text() and btoa().

Or if you want the whole thing as a promise:

let encode = myblob.text().then(btoa);

PS: To decode back to a Blob: new Blob([atob(encoded)])

Jason C
  • 35,986
  • 14
  • 111
  • 162
-1

async TypeScript variation:

async function blobToBase64Async(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.onerror = (e) => reject(fileReader.error);
    fileReader.onloadend = (e) => {
      const dataUrl = fileReader.result as string;
      // remove "data:mime/type;base64," prefix from data url
      const base64 = dataUrl.substring(dataUrl.indexOf(',') + 1);
      resolve(base64);
    };
    fileReader.readAsDataURL(blob);
  });
}

Sample usage:

async function fetchToBase64Async(url: string, init?: RequestInit): Promise<string> {
  try {
    const response = await fetch(url, init);
    if (!response.ok) {
      const responseText = await response.text();
      throw new Error("server status: " + response.status + "\n" + "server response:" + "\n" + responseText);
    }
    const blob = await response.blob();
    const base64 = await blobToBase64Async(blob);
    return base64;
  } catch (e) {
    throw new Error("failed to fetch: " + url + "\n" + "caused by: " + e);
  }
}

async function demoUsage() {
  const base64 = await fetchToBase64Async("https://httpstat.us/200", {
    method: "POST",
    headers: {
      "Accept": "*/*",
      "Authorization": "Bearer ...",
    }
  });
  console.log(base64);
}

Notes:

  • I don't understand why some answers use the load instead of the loadend event
  • I don't understand why some answers call readAsDataURL before setting the event handler
Reto Höhener
  • 4,747
  • 4
  • 32
  • 67