'How to handle downloading external images using html2canvas?

I have a page with several book covers and a button to download them all.

Book covers are in String format and are a Google Books URL. I'm populating like this:

<section ref="capture" id="my-node">
  <figure v-for="book in books" :key="book.id">
    <img :src="book.thumbnail" :alt="`Book ${book.title}`" />
  </figure>
  <button type="button" @click="download">html2canvas</button>
</section>

To download the book covers I'm calling the following function:

download() {
  html2canvas(this.$refs["capture"], { allowTaint: true })
    .then((canvas) => {
      let link = document.createElement("a");
      link.download = "Livrero.png";
      link.href = canvas.toDataURL("image/png");
      link.click();
    })
},

Books is an object that contains a thumbnail property with values like this: https://books.google.com/books/content?id=_i6bDeoCQzsC&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api

What makes me more confused is that if I were using values similar to this: https://images-na.ssl-images-amazon.com/images/I/41xShlnTZTL._SX218_BO1,204,203,200_QL40_FMwebp_.jpg everything would be working, but unfortunately the project does not allow this at the moment.


Previously I was using the property useCORS: true in html2canvas however it was returning a CORS policy error. So, when I removed it, I stopped having this error and started getting the following error:

DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement':
Tainted canvases may not be exported.

It was suggested that I use http://html2canvas.hertzen.com/proxy/ to succeed in the mission, but because I'm using Vue.js I don't know how to apply this.



Solution 1:[1]

This is a problem with browser's content policy. There is no simple solution.

The browser won't let you use content (images) from different origins which don't explicitly allow it.

With useCORS: true, it's expected that the server (with the remote resource) responds with the appropriate access-control-allow-origin headers. Your Amazon link does, that's why it works.

With allowTaint: true, it accepts the image, but then refuses to let you do anything with the resulting canvas image as it's now considered tainted.

Since you can't make 3rd party server send appropriate CORS headers, you will have to run your own CORS proxy.

To run html2canvas-proxy mentioned in the official documentation you need to install it

npm install html2canvas-proxy --save

make a start JavaScript file

var proxy = require('html2canvas-proxy');
var express = require('express');
const port = 8000;

var app = express();
app.use('/', proxy());
app.listen(port, () => {
  console.log(`html2canvas-proxy listening on port ${port}`)
})

and then use the proxy option in html2canvas

html2canvas(this.$refs["capture"], {
    proxy: "http://localhost:8000",
})
.then((canvas) => {
  // ...
})

But for production, this simple proxy isn't good either as it allows anyone to use is as a CORS proxy and you might not want a separate server, if you aren't not already running an Express Node.js server.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 gre_gor