-1

I'm building a data visualization which relies on a lot of small raster images, delivered as AWS URLs via JSON API.

This works fairly well, until I try to implement my next step, which is rendering the data visualization as a PNG to download. In the PNG, the raster images are broken.

I've understood that to solve this, I need to embed images as Data URLs.

Here's what I've got so far:

const companies_base64 = companies.map(c => {
  var o = Object.assign({}, c)
  o.base64 = imageToBase64(c.mimetype, c.logo)
  return o
})

Where companies is an array of objects. Here's imageToBase64, the Heroku app being a clone of CORS anywhere:

function imageToBase64(mimetype, logo) {
  var url = 'https://example.herokuapp.com/' + logo
  return d3.blob(url)
           .then(blob => blobToBase64(blob))
           .then(base64 => mimetype + base64)
           .catch(error => console.error(error))
}

function blobToBase64(blob) {
  return new Promise((resolve, reject) => {
    let reader = new FileReader()
    reader.onload = () => {
      let dataUrl = reader.result
      let base64 = dataUrl.split(',')[1]
      resolve(base64)
    }
    reader.onerror = () => {
      reject("Error")
    }
    reader.readAsDataURL(blob)
  })
}

Which results in a Promise being returned when calling base64 on any of the objects in companies_base64, the [[PromiseValue]] being of course what I'm after. How am I supposed to make sure it is what gets returned so I can, ultimately, place it inside the xlink:href attributes of the <image>s in my <svg>?

I think that once it works and I can call imageToBase64 wherever, it's something I want to do only when the user presses Download. I imagine I can do this using D3, iterating over the <image>s and swapping out their xlink:href. Or should I go about it another way?

I have also tried getting the images as objects and then converting them to base64 in my RoR backend so they come packaged with the JSON, via an Image#to_base64 method. This does work, but it A) feels very wrong and B) is obviously very slow on initial load.

Thank you for your time and please bear with me as I am a beginner.

bumcode
  • 362
  • 4
  • 13

1 Answers1

1

Your imageToBase64 function returns a promise, not the resolved data URL. That means you have to wait before you can attach them to the companies_base64 members. It is your choice if you do that as soon as the individual base64 string is ready, or if you wait for them all:

Promise.all(companies.map(c => {
     return imageToBase64(c.mimetype, c.logo)
         .then(u => Object.assign({ base64: u }, c))
         .then(/* change the image reference here one by one... */)
}))
.then(companies_base64 => /* ...or here, in a loop over the array */)
.catch(error => console.error(error))
ccprog
  • 20,308
  • 4
  • 27
  • 44