Alex028502
Alex028502

Reputation: 3814

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?

Upvotes: 1

Views: 5338

Answers (2)

Alex028502
Alex028502

Reputation: 3814

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

Upvotes: 5

dolpsdw
dolpsdw

Reputation: 78

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.

Upvotes: 2

Related Questions