1

I'm trying to figure out how to get an image from an image URL from other domains to then post to my server. But I'm running into issues with cores.

I'm making a wishlist page where the user can enter a url for a product from any site. My server scrapes the URL for product information and product images and sends it to the client. The user refines the information and selects the desired image. This information is sent to the backend to create a wishlist item with product information and an image of the item.

Scraping product image & product info

enter image description here

Image and information goes on wishlist

enter image description here

Tainted Canvas

I tried to use the canvas to grab and crop the image and send it to my backend. But when you draw into a canvas an image loaded from another origin without CORS approval, the canvas becomes tainted.

Calling any of the following on a tainted canvas will result in an error:

  • Calling getImageData() on the canvas's context
  • Calling toBlob() on the element itself
  • Calling toDataURL() on the canvas

source

      export async function toDataURLCanvas(src, outputFormat) {
        return new Promise((resolve) => {
          const img = new Image();
          img.onload = async function () {
            console.log(this);
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            canvas.width = 200;
            canvas.height = 200;
            ctx.drawImage(this, 0, 0); //this is where you would crop the image
            const blob = await new Promise((resolve) => {
              // results in "Failed to execute 'toBlob' on 'HTMLCanvasElement': Tainted canvases may not be exported.""
              canvas.toBlob((blob) => {
                resolve(blob);
              }, 'image/jpg');
            });
            resolve(blob);
          };
          img.src = src;
        });
      }

This gives an error:

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

So I cannot use canvas to convert to image URL into a format to send to my backend such as a blob.

XHR

I tried to convert the URL to a data URL with XMLHttpRequest()

   async function toDataURLXHR(url) {
        return new Promise((resolve) => {
          var xhr = new XMLHttpRequest();
          xhr.onload = function () {
            var reader = new FileReader();
            reader.onloadend = function () {
              resolve(reader.result);
            };
            reader.readAsDataURL(xhr.response);
          };
          xhr.open('GET', url);
          xhr.responseType = 'blob';
          xhr.send();
        });
      }

But then CORS can be an issue. I got this in the browser console:

Access to XMLHttpRequest at 'https://cdn-images.farfetch-contents.com/15/40/82/30/15408230_28680969_600.jpg' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Not Possible?

It seems like it's not possible to grab an image from one site and send it to my server. But I've seen other sites do this so there must be a way.

Dashiell Rose Bark-Huss
  • 2,173
  • 3
  • 28
  • 48

1 Answers1

0

Scrape On The Backend

If there is no Access-Control-Allow-Origin header from the place you’re scraping, you can run into problems.

The bad news is, you need to run these sorts of requests server-side to get around this issue.

source

This suggests it is possible but only on the backend.

I found this solution to download the image to the backend. We could get the URL of the image the user wants from the client and download the image using this solution

const imgURL = 'https://cdn-images.farfetch-contents.com/15/40/82/30/15408230_28680969_600.jpg';
const fs = require('fs');
const https = require('https');

/* ============================================================
  Promise-Based Download Function
============================================================ */

const download = (url, destination) =>
  new Promise((resolve, reject) => {
    const file = fs.createWriteStream(destination);

    https
      .get(url, (response) => {
        response.pipe(file);

        file.on('finish', () => {
          file.close(resolve(true));
        });
      })
      .on('error', (error) => {
        fs.unlink(destination);

        reject(error.message);
      });
  });

/* ============================================================
  Download Image
============================================================ */

(async () => {
  download(imgURL, `${__dirname}/public/newimg.jpg`);
})();

Cropping

If you want to add cropping, you can have the client POST the image URL to the server along with crop information and can use this backend canvas library to crop on the backend.

const imgURL = 'https://cdn-images.farfetch-contents.com/15/40/82/30/15408230_28680969_600.jpg';
const fs = require('fs');

const { createCanvas, loadImage } = require('canvas');

const canvas = createCanvas(400, 400);
const ctx = canvas.getContext('2d');

loadImage(imgURL).then((image) => {
  const srcSqrDim = image.height > image.width ? image.width : image.width;

  ctx.drawImage(
    image,
    -(srcSqrDim - image.width) / 2,
    -(srcSqrDim - image.height) / 2,
    srcSqrDim,
    srcSqrDim,
    0,
    0,
    400,
    400
  );
  console.log(canvas.toDataURL());
  fs.writeFile(
    `${__dirname}/public/new1.png`,
    canvas.toDataURL().replace(/^data:image\/png;base64,/, ''),
    'base64',
    console.log
  );
});
Dashiell Rose Bark-Huss
  • 2,173
  • 3
  • 28
  • 48