Search code examples
javascripthtmlimagebase64data-uri

create data url from fetched image


TL;DR

I'm trying to fetch and image, convert it to base64, and put the data url into an img's src attribute, but it's not working:

async function ajax(id) {
  const tag = document.getElementById(id);
  const path = tag.getAttribute("data-src");
  const response = await fetch(path);
  const blob = await response.blob();
  const base64 = window.btoa(blob);
  const content = `data:image/jpeg;base64,${base64}`;
  tag.setAttribute("src", content);
}

The details, as well as some other methods, which do work follow.


I have been experimenting with different ways to lazy load:

$ mkdir lazy
$ cd lazy
$ wget https://upload.wikimedia.org/wikipedia/commons/7/7a/Lone_Ranger_and_Silver_1956.jpg # any other example image

now create a file called index.html with this in it:

<script>
  // this works
  function setAttribute(id) {
    const tag = document.getElementById(id);
    const path = tag.getAttribute("data-src");
    tag.setAttribute("src", path);
  }

  // this doesn't work for some reason
  async function ajax(id) {
    const tag = document.getElementById(id);
    const path = tag.getAttribute("data-src");
    const response = await fetch(path);
    const blob = await response.blob();
    const base64 = window.btoa(blob);
    const content = `data:image/jpeg;base64,${base64}`;
    tag.setAttribute("src", content);
  }

  // this works too
  async function works(id) {
    const tag = document.getElementById(id);
    const path = tag.getAttribute("data-src");
    const response = await fetch(path);
    const blob = await response.blob();
    const content = URL.createObjectURL(blob);
    tag.setAttribute("src", content);
  }

</script>
<a href="javascript: setAttribute('example');">set attribute</a><br />
<a href="javascript: ajax('example');">data url</a><br />
<a href="javascript: works('example');">object url</a><br />
<img id="example" data-src="Lone_Ranger_and_Silver_1956.jpg"></img><br />

and start a server in that folder:

$ python -m SimpleHTTPServer # or whichever local webserver

and then when I look at it in chrome I get this:

enter image description here

The first and third links both work:

enter image description here

However, the middle link does not:

enter image description here

Here is what the three links do to the tag respectively:

works:

<img id="example" data-src="Lone_Ranger_and_Silver_1956.jpg" src="Lone_Ranger_and_Silver_1956.jpg">

does not work:

<img id="example" data-src="Lone_Ranger_and_Silver_1956.jpg" src="data:image/jpeg;base64,W29iamVjdCBCbG9iXQ==">

works:

<img id="example" data-src="Lone_Ranger_and_Silver_1956.jpg" src="blob:http://localhost:8000/736a9e18-c30d-4e39-ac2e-b5246105c178">

That data url in the non working example also looks too short. So what am I doing wrong?


Solution

  • Thanks for the suggestion @dolpsdw. window.btoa doesn't do what I thought it would. If anybody is trying to do the same thing, instructions for reading a blob into a data url are here: https://stackoverflow.com/a/18650249/5203563

    I have created this wrapper that fits right into my program as follows:

    (it even adds in the data:image/jpeg;base64, part for you and works out the mime type from the blob)

    
      function readBlob(b) {
        return new Promise(function(resolve, reject) {
          const reader = new FileReader();
    
          reader.onloadend = function() {
            resolve(reader.result);
          };
    
          // TODO: hook up reject to reader.onerror somehow and try it
    
          reader.readAsDataURL(b);
        });
      }
    
      async function ajax(id) {
        const tag = document.getElementById(id);
        const path = tag.getAttribute("data-src");
        const response = await fetch(path);
        const blob = await response.blob();
        // const base64 = window.btoa(blob);
        // const content = `data:image/jpeg;base64,${base64}`;
        const content = await readBlob(blob);
        tag.setAttribute("src", content);
      }
    

    this gives me the much longer data url that I expected:

    enter image description here