1

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?

Alex028502
  • 3,486
  • 2
  • 23
  • 50

2 Answers2

5

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

Alex028502
  • 3,486
  • 2
  • 23
  • 50
2

When you have the inmemory blob Just generate a url for that blob

var url = urlCreator.createObjectURL(blob)

Then create a new IMG with JavaScript and invoke decode method

const img = new Image();
img.src = url;
img.decode()
.then(() => {
  document.body.appendChild(img);
})
.catch((encodingError) => {
  // Do something with the error.
})

Maybe you want also to revoke URL after load with

URL.revokeObjectURL(objectURL)


About why the window.btoa does not work, its because is for string to base64 only.

Read about blob to base64 conversión here. But is a more elegant solution createObjectURL.

Community
  • 1
  • 1
dolpsdw
  • 78
  • 5
  • I think this is the same as what I already put for my 3rd example, but I am wondering how to make the data url work (my second example) – Alex028502 Oct 25 '19 at 14:12
  • 1
    Seems like the [btoa](https://www.w3schools.com/jsref/met_win_btoa.asp) only encode string to base64. To encode a blob to base64 checkout [FileReader](https://stackoverflow.com/questions/18650168/convert-blob-to-base64). Hoever i think is more optime to not process the img to extract the base64 and then make the browser to re-decode base64 and then decode the image. Instead just use the createUrl. – dolpsdw Oct 25 '19 at 14:21