310

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

This commented part works and 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 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 the Blob file as in the code above?

1
  • use btoa to convert bytes directly to base64; not sure what's up with the intermediate data urls in the answers.
    – Jason C
    Commented Oct 25, 2022 at 13:04

17 Answers 17

526
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.

13
  • 3
    The output String doesn't seem to look like base64?
    – quarks
    Commented Sep 9, 2013 at 1:07
  • 1
    @xybrek If you print read.result, you will see base64 in the string itself. Commented Apr 26, 2014 at 5:29
  • 46
    console.log( base64data.substr(base64data.indexOf(',')+1) );
    – palota
    Commented Aug 3, 2015 at 18:59
  • 19
    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
    Commented Dec 16, 2015 at 6:00
  • 8
    This answer is incorrect, it appends none-base64 characters to the front of the string. Commented Jan 19, 2016 at 23:41
59

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);
  });
0
54

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);
};
6
  • 1
    What is the argument cb? Commented May 14, 2017 at 20:42
  • 1
    @FellowStranger commonly callback, used like blobToBase64(myBlob, function(base64String) {/*use the base64String*/}), because it's async Commented May 15, 2017 at 20:03
  • @yeahdixon, Seems I need your help. Look at this : stackoverflow.com/questions/46192069/…
    – moses toh
    Commented Sep 13, 2017 at 8:33
  • How do you wait for the execution? I always get undefined
    – MauriR
    Commented Oct 19, 2020 at 22:52
  • the callback is meant to be a function that gets passed that fires when complete
    – yeahdixon
    Commented Oct 22, 2020 at 7:10
11
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);
}
1
  • Nice, @Vemonus because FileReader expose into result attribute as base64, works perfectly. Commented Aug 14, 2017 at 17:07
9
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.

2
  • .text() decodes using UTF8, what happens if the response has binary code? I believe this will fail for non text data
    – Ralph
    Commented Jan 28, 2020 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
    Commented Jan 29, 2020 at 5:41
9

The answer from @Arun Killu is a good snippet if you know what is going on, but nobody has explained what was the error on the original code. For people using async and Promise calls this is error is soo obvious but for people learning or without experience it's not so clear.

Here a simple explanation.

The Bad code enter image description here

var blob = items[i].getAsFile();

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

Above code is trying to capture a binary string on source variable, however, FileReader.readAsBinaryString() returns undefined. This is because the result will be available whenever the event onload will be triggered. As we can see, he was trying to console.log the event.target.result value, which is a wrong approach.

The Good code enter image description here

Here is a step by step implementation:

// 1. Create a FileReader instance
const reader = new FileReader()
// 2. Add a handler for the 'onload' event
reader.onload = (e) => {
    // 5. Get the result when the 'onload' event is triggered.
    const base64data = reader.result               
    console.log({base64data})
}
// 3. Add a handler for the 'onerror' event
reader.onerror = () => {                
    console.log('error')
}
// 4. Call 'readAsDataURL' method
reader.readAsDataURL(imageBlob) 

As you can see the last step is 5 and it is because it is an asynchronous call.

Here is a working example:

const url = 'https://i.sstatic.net/RRuCp.png'

const fetchImage = async url => {
  const response = await fetch(url, {mode: 'no-cors',})
  const blob = await response.blob()
  
  return blob
}

const loadImage = async () => {
  const imageBlob = await fetchImage(url)

  const reader = new FileReader()
  reader.onload = () => {
    const base64data = reader.result               
    console.log({base64data})
  }
  reader.onerror = () => {                
    console.log('error')
  }
  reader.readAsDataURL(imageBlob) 
}

loadImage()

Teo, what means asynchronous?

Well young Padawan, asynchronous means that we don't know when the result will be ready, it can be different in each system and depends on how heavy or complex is the process and also it can find some errors that will not produce any result at all.

So if a process is asynchronous is a good practice to encapsulate it using an async method and returning a Promise like this:

const blobToBase64 = async blob => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result)
    reader.onerror = err => reject(err)
    reader.readAsDataURL(blob)
  })
}

Ah okay Teo, but what is a Promise?

Good question my young fella. A Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. In other words, will tell us if the result is ready and will give us its value, otherwise will return an error.

Above code shows a function blobToBase64 that will return a Promise This means that this function will return reader.result when it ready.

How can we integrate it into our code?

Super easy, just replace all the FileReader with the function blobToBase64 defined above and call it like this imageBase64 = await blobToBase64(imageBlob)

Check this snippet:

const url = 'https://i.sstatic.net/RRuCp.png'

const fetchImage = async url => {
  const response = await fetch(url, {
    mode: 'no-cors',
  })
  const blob = await response.blob()

  return blob
}

const blobToBase64 = async blob => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result)
    reader.onerror = err => reject(err)
    reader.readAsDataURL(blob)
  })
}

const loadImage = async() => {
  const imageBlob = await fetchImage(url)
  const imageBase64 = await blobToBase64(imageBlob)
  
  console.log({imageBase64})
}

loadImage()

3
  • I don't understand your question, usually you get octet-stream from ajax calls or $http service from angular. can you give an example of your code or share the image file you are using?
    – Teocci
    Commented Oct 11, 2022 at 4:26
  • stackoverflow.com/a/74026755/5372008 check this answer I make about converting svg into a png format
    – Teocci
    Commented Oct 11, 2022 at 10:40
  • 1
    Neither of your two examples appear to work, no image is displayed, and "imageBase64" is just "data:". I have tested in my environment as well with the same, if not worse, results. Am I missing something ?
    – Metric Rat
    Commented Feb 23, 2023 at 10:27
7
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:

7

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
1
  • 1
    You should use load instead of loadend because loadend fires in case of both success and error. That is, you have 2 error handlers, which is ambiguous (it's unclear who will handle the error first).
    – Finesse
    Commented Feb 6 at 10:12
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);}
     });
 }
0
5

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

5

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));
3

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))
1
  • The onerror callback argument is not an error. You should use reader.onerror = () => reject(reader.error) instead.
    – Finesse
    Commented Feb 6 at 10:15
2

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)])

2
  • 1
    May fail when the blob has special "chars": DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range. Commented Oct 28, 2022 at 17:20
  • 2
    @RoyShilkrot Addressed in docs, with solutions (also mentioned here in btoa doc link in post).
    – Jason C
    Commented Oct 28, 2022 at 21:58
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

0

If your "blob" is an actual Blob Object and not a blob url, the conversion is pretty simple:

const reader = new FileReader()
reader.readAsDataURL(blob)
reader.onload = () => resolve(reader.result)

example of Blob Object:

console.log(blob)

output:

Blob {lastModified: 1658039931443, lastModifiedDate: Sun Jul 17 2022 08:38:51 GMT+0200 (Central European Summer Time), name: '1.jpg', size: 35493, type: 'image/jpeg'}
    lastModified: 1658039931443
    lastModifiedDate: Sun Jul 17 2022 08:38:51 GMT+0200 (Central European Summer Time) {}
    name: "1.jpg"
    size: 35493
    type: "image/jpeg"
    [[Prototype]]: Blob

In my case, the blob was produced by Compressorjs (should you need image compression).

0

In angular you can request the file as an array buffer then map it to base64

this.http.get('./101.jpg', {responseType: 'arraybuffer'}).pipe(
    map(q => String.fromCharCode(...new Uint8Array(q))),
    map(q => btoa(q)),
).subscribe(q => {
    console.log(q);
})
0

the simplest method, cross platform ie browser/node.js, that doesnt require filereaders, or fetchs is the following :

(async()=>{


      async function blob_b64(blob){
      
            var buf     = await blob.arrayBuffer();
            var bytes   = new Uint8Array(buf);
            var bin     = bytes.reduce((acc,byte)=>acc+=String.fromCharCode(byte),'');
            var b64     = btoa(bin);
            return b64;
      
      }//blob_b64


      function b64_blob(b64,type='text/plain'){
      
            var bin     = atob(b64);
            var bytes   = [...bin].map(c=>c.charCodeAt(0));
            var buf     = new Uint8Array(bytes);
            var blob    = new Blob([buf],{type});
            return blob;
            
      }//b64_blob
      



  //test
  
      var blob    = new Blob(['helloworld']);

      
      var b64     = await blob_b64(blob);
      
      var blob    = b64_blob(b64);      


      console.log(b64);                     //  aGVsbG93b3JsZA==
      console.log(await blob.text());       //  helloworld


})();

  • this will be good upto the limit on the size of javascript strings
  • there are also improvements that can be made in terms memory footprint, i just keep it simple here, since it quite rare to base64 encode large files


edit : i misread the question somewhat, i got here from googling blob to base64 myself

it seems you are trying to do blob to dataurl

the above code is still used, just a dataurl is constructed as follows

mdn : Data URLs

(async()=>{

      async function blob_b64(blob){
      
            var buf     = await blob.arrayBuffer();
            var bytes   = new Uint8Array(buf);
            var bin     = [...bytes].reduce((acc,byte)=>acc+=String.fromCharCode(byte),'');
            var b64     = btoa(bin);
            return b64;
      
      }//blob_b64

      function b64_blob(b64,type='text/plain'){
      
            var bin     = atob(b64);
            var bytes   = [...bin].map(c=>c.charCodeAt(0));
            var buf     = new Uint8Array(bytes);
            var blob    = new Blob([buf],{type});
            return blob;
            
      }//b64_blob

      function b64_dataurl(b64,type){
      
            var dataurl   = `data:${type};base64,${b64}`;
            return dataurl;
            
      }//b64_datauri
      
      function dataurl_b64(datauri){
      
            var i     = dataurl.indexOf(',')+1;
            var b64   = dataurl.slice(i);
            return b64;
            
      }//datauri_b64


  //test
  
      var dataurl   =
      'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAiZJREFUOE9jZK'+
      'AAMML0Xuro0P34+jWbbW/vOQYGhv+4zOwUYeDV0bMz3Ljv0Bmw5mdrV9aI8XE3MTy+x/j6zMn9B/cdz4i4ce8WsgFr5eUlVczNa7'+
      '7wcFuefvKos2DX3rVgzfsqS63VhXgni319Y/jv9mWGT9evfzx5/320z/ufWxsYGJgcxMTS+ZUUmq/8+zcv/9SZlncMDJ9A+uDOZm'+
      'BgYN1lrhmh8etTK8v7j7KfP//8dfnrvxyJf4wBjJycNruZmVIb3n1cw8DA8A/mImTNYLEEBgaBGD7mVpHfDBn/fv9j+viP4e0apv'+
      '9BU/8wHEYPCwzNIAO2MDOEivxnXMT0/z/HP2bGb0cYWexKfv8+ix6IGJp3MjFkCvxnmPKdkfHjQUamQi9upmZmdqYvDa9/mm9iYP'+
      'iMbACK5q0MDCmijAyzGJgZ/23+/z+m+S/DinY+dpUoCZZ9D37+W2n/8HsZstPhmtcyMFjIMjEeYmdlZL3IyDgr7sffLAYGhr8gm+'+
      'aIsho68bHvXvvyl0Ppl19XMALsmCDvCf7f383fMjPeTfv42/wGA8NbZCfuFOVI4mFm9rB+8TUcZjvY5lZubvFoV5sXrLcvMZx7/6'+
      '7X99nPEiwpjOmYMOeGSW+/Z6xgYHgGj+ckBgbe9pbKl3xPb3Ku33M4L+r268nYkucCdnblt//+8RX//n0eJZFsTY0JY/322Tpj6c'+
      'a6ewwMH4nJL1jjmRiNIDUADZXPhFJZPA0AAAAASUVORK5CYII='
      ;

      
      var img             = document.createElement('img');
      img.style.cssText   = 'border:1px solid lightgray;border-radius:5px;margin-right:20px;cursor:pointer;';
      var nimg;
      
      nimg                = img.cloneNode();
      nimg.src            = dataurl;
      document.body.append(nimg);

      
      var b64             = dataurl_b64(dataurl);
      var blob            = b64_blob(b64,'image/png');
      var type            = blob.type;    //  image/png

      nimg                = img.cloneNode(true);
      nimg.src            = window.URL.createObjectURL(blob);
      document.body.append(nimg);

      
      var b64             = await blob_b64(blob);
      var dataurl         = b64_dataurl(b64,'image/png');
      
      nimg                = img.cloneNode();
      nimg.src            = dataurl;
      document.body.append(nimg);
      
})();

Not the answer you're looking for? Browse other questions tagged or ask your own question.