Desistreus
Desistreus

Reputation: 361

Requesting blob images and transforming to base64 with fetch API

I have some images that will be displayed in a React app. I perform a GET request to a server, which returns images in BLOB format. Then I transform these images to base64. Finally, i'm setting these base64 strings inside the src attribute of an image tag.

Recently I've started using the Fetch API. I was wondering if there is a way to do the transforming in 'one' go.

Below an example to explain my idea so far and/or if this is even possible with the Fetch API. I haven't found anything online yet.

  let reader = new window.FileReader();
  fetch('http://localhost:3000/whatever')
  .then(response => response.blob())
  .then(myBlob => reader.readAsDataURL(myBlob))
  .then(myBase64 => {
    imagesString = myBase64
  }).catch(error => {
    //Lalala
  })

Upvotes: 30

Views: 43147

Answers (5)

James Wong
James Wong

Reputation: 121

Here's the logic for converting a blob to an image URI, derived from @Augustin Riedinger's answer:

async function imageBlobToBase64(blob: Blob) {
  return new Promise((onSuccess, onError) => {
    try {
      const reader = new FileReader();
      reader.onload = function () {
        onSuccess(this.result);
      };
      reader.readAsDataURL(blob);
    } catch (e) {
      onError(e);
    }
  });
}

Usage:

const res = await fetch(...);
const blob = await res.blob();
const uri = await imageBlobToBase64(blob);

Upvotes: 2

Robin Métral
Robin Métral

Reputation: 3209

In case someone is working in a V8 environment (such as Cloudflare Workers).

There's no FileReader like in the browser, and there's probably1 no Buffer like in Node.js. So instead, we can use the standard Uint8Array, which is used under the hood by Buffer:

/**
 * Fetches an image from URL and encodes it into a base64 string
 * Adapted from this thread: https://community.cloudflare.com/t/convert-request-body-to-base64-encoded-string-solved/99341/5
 * @param {string} url
 * @returns {Promise<string>}
 */
async function imageUrlToBase64(url) {
  let string = "";
  const response = await fetch(url);
  const buffer = await response.arrayBuffer();
  (new Uint8Array(buffer)).forEach(
    (byte) => { string += String.fromCharCode(byte) }
  )
  return btoa(string);
}

1 In Cloudflare Workers, you could also opt into Buffer compatibility by adding the node_compat flag.

Upvotes: 0

Augustin Riedinger
Augustin Riedinger

Reputation: 22170

Thanks to @GetFree, here's the async/await version of it, with promise error handling:

const imageUrlToBase64 = async url => {
  const response = await fetch(url);
  const blob = await response.blob();
  return new Promise((onSuccess, onError) => {
    try {
      const reader = new FileReader() ;
      reader.onload = function(){ onSuccess(this.result) } ;
      reader.readAsDataURL(blob) ;
    } catch(e) {
      onError(e);
    }
  });
};

Usage:

const base64 = await imageUrlToBase64('https://via.placeholder.com/150');

Upvotes: 15

Sergey Geron
Sergey Geron

Reputation: 10162

If somebody gonna need to do it in Node.js:

const fetch = require('cross-fetch');
const response  = await fetch(url);
const base64_body = (await response.buffer()).toString('base64');

Upvotes: 3

GetFree
GetFree

Reputation: 42434

The return of FileReader.readAsDataURL is not a promise. You have to do it the old way.

fetch('http://localhost:3000/whatever')
.then( response => response.blob() )
.then( blob =>{
    var reader = new FileReader() ;
    reader.onload = function(){ console.log(this.result) } ; // <--- `this.result` contains a base64 data URI
    reader.readAsDataURL(blob) ;
}) ;

General purpose function:

function urlContentToDataUri(url){
    return  fetch(url)
            .then( response => response.blob() )
            .then( blob => new Promise( callback =>{
                let reader = new FileReader() ;
                reader.onload = function(){ callback(this.result) } ;
                reader.readAsDataURL(blob) ;
            }) ) ;
}

//Usage example:
urlContentToDataUri('http://example.com').then( dataUri => console.log(dataUri) ) ;

//Usage example using await:
let dataUri = await urlContentToDataUri('http://example.com') ;
console.log(dataUri) ;

Upvotes: 36

Related Questions