0

SOLUTION: simply store the base64 as the image source on an HTML image tag and then hide that image. Thanks, Kaiido!

I need to store an image as pixel data in the most performant way possible at it's highest possible resolution. To accomplish I have been fetching the image from imgur and converting to base64 like so:

async function toBase64(file) {
   return new Promise((resolve, reject) => {
   const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = error => reject(error);
   });
}

async function getBlob(){
   const blob = fetch("https://i.imgur.com/sOmEuRl.jpg")
      .then(function (response) {
         return response.blob();
      })
      .catch(function(err){
          console.log(err)
      })
          
   return blob
}

const blob = await getBlob()
const base64 = await toBase64(blob);
const src = base64

It works tremendously but I need the project I'm working on to be usable offline. Naturally I'm encountering CORS errors when trying to use a local file URL so I'm completely out of ideas at this point.

I've tried loading the image using an img tag in the html but that gives the same CORS error, of course.

Is there any way I can retrieve and store the data locally instead of retrieving it from a server? I've tried reading the pixel data and then writing it to a new file as an array using node, but the resulting file is so massive I can't do anything with it...

Thanks for your time.

  • I'm afraid your question is a bit unclear. Why does it have to be base64 encoded? What is stoping your from loading the jpeg using node (and eventually converting this to base64 and store it)? – obscure Jul 07 '22 at 18:42
  • @obscure hi again! It's because I can't use node to run the final project. It needs to be simply run by running the index.html – EdelweissPirate Jul 08 '22 at 06:52
  • To go further, the base64 can't be stored as a string as far as I can tell. I can literally save it but I can't then read it as a string from a variable – EdelweissPirate Jul 08 '22 at 08:00
  • Hey obscure, I have it working now but thanks for having a look! – EdelweissPirate Jul 08 '22 at 08:49

1 Answers1

0

Base64 in LocalStorage is definitely not "the most performant way" to store an image, nor will it allow the storage of big files.

If I understand your case correctly (the need to be able to navigate your site even when offline), then the correct solution is to

  • register a ServiceWorker,
  • make this ServiceWorker store all the required resources, including your images, in the browser's cache through the Cache API,
  • make it retrieve the resources from the cache when your page fetches it again (e.g at next reload).

More info available at https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps and particularily for this matter at https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers

Now, you may be in a rush and not wanting to build a full PWA setup. In that case, the best solution to store binary data (image files included) in a semi-persistent way is to use the IndexedDB API (IDB). I invite you to read this previous answer of mine showing how to use Mozilla's library localforage which helps a lot dealing with IDB.



And... turns out I didn't understand the case correctly. (I leave the first section anyway because who knows, it may help others).

So what OP actually wants is a standalone page, and for this, there is no need to "store" the image anywhere, it suffices to make it part of the markup directly, and this is actually one of the only cases where a data: URL is useful.

So you'd keep your code that generates the data: URL, but only as a tool for building your HTML file. The HTML file would not contain this code but only the produced data: URL:

async function toBase64(file) {
   return new Promise((resolve, reject) => {
   const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = error => reject(error);
   });
}

function getBlob(){
   return fetch("https://i.imgur.com/sOmEuRl.jpg")
      .then(function (response) {
         return response.blob();
      })
      .catch(function(err){
          console.log(err);
          return Promise.reject(err);
      });
}

(async () => {
  const blob = await getBlob()
  const base64 = await toBase64(blob);
  const src = base64;
  const HTMLMarkup = `<!DOCTYPE HTML>
<html>
  <body>
    <h1>Your HTML page</h1>
    <img src="${src}" alt="an image">
    <!-- your normal HTML content -->
  </body>
</html>`;
  const HTMLFile = new Blob([HTMLMarkup], { type: "text/html" });
  const HTMLSrc = URL.createObjectURL(HTMLFile);
  document.querySelector("iframe").src = HTMLSrc;
  document.querySelector("pre").textContent = HTMLMarkup;
})();
<iframe></iframe>
<pre></pre>

But maybe, I'm still not understanding your situation completely and all you need is actually to save that file on your hard-drive, in the same directory as your file (or in a sub-folder of the one where your index.html file is). Even if served through file:// all modern browsers accept to load an <img> if it meets this "same/sub-folder" condition.

Ps: If it's only for your usage, you may also consider starting a local server on your machine. If you continue doing some web-development you'll very soon find out that you need one.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • but this requires an initial download doesn't it? I need it to just work offline off the bat. I don't mind doing an insane amount of prep, but are you really telling me thanks to CORS there's no way of me doing this offline/using purely local resources? – EdelweissPirate Jul 08 '22 at 06:54
  • Oh! you want this to never face the web nor a local server? Then just append a data: URI in the HTML markup e.g ` – Kaiido Jul 08 '22 at 07:09
  • This is what I get man https://imgur.com/a/ejNfvqv Doesnt work :( – EdelweissPirate Jul 08 '22 at 07:55
  • Please don't "man" me, it's better we stay on polite terms, moreover when you are the one being helped. Besides, your image doesn't show how it "doesn't work". My live snippet clearly works, so if it doesn't work for you, you are doing something wrong. – Kaiido Jul 08 '22 at 07:59
  • Terribly sorry, it's just a term of endearment where I'm from! It's not being read as a string, doesn't that show that it's not working? – EdelweissPirate Jul 08 '22 at 08:03
  • Thank you from the bottom of my heart, I have it working. You've saved my butt and I apologise for any offence I caused in trying to be amiable. Have a great day! – EdelweissPirate Jul 08 '22 at 08:19