Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CORS errors trying to convert remote image to base64 data [duplicate]

I need to convert a remote image into base64 given its URL, but I am running in to CORS errors and not sure how to work around.

I've followed some of the solutions on this question: How to convert image into base64 string using javascript

My example image is: https://blog.xenproject.org/wp-content/uploads/2014/10/Testing.jpg

Approach 1 (FileReader):

function toDataUrl(url, callback) {
  var xhr = new XMLHttpRequest();
  xhr.onload = function() {
    var reader = new FileReader();
    reader.onloadend = function() {
      callback(reader.result);
    }
    reader.readAsDataURL(xhr.response);
  };
  xhr.open('GET', url);
  xhr.responseType = 'blob';
  xhr.send();
}

toDataUrl('https://blog.xenproject.org/wp-content/uploads/2014/10/Testing.jpg', function(data) { console.log(data)} );

This produces the error:

XMLHttpRequest cannot load https://blog.xenproject.org/wp-content/uploads/2014/10/Testing.jpg. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://stackoverflow.com' is therefore not allowed access.

Approach 2 (Canvas)

function toDataUrl(src, callback, outputFormat) {
  var img = new Image();
  img.crossOrigin = 'use-credentials';
  img.onload = function() {
    var canvas = document.createElement('CANVAS');
    var ctx = canvas.getContext('2d');
    var dataURL;
    canvas.height = this.height;
    canvas.width = this.width;
    ctx.drawImage(this, 0, 0);
    dataURL = canvas.toDataURL(outputFormat);
    callback(dataURL);
  };
  img.src = src;
  if (img.complete || img.complete === undefined) {
    img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
    img.src = src;
  }
}
toDataUrl('https://blog.xenproject.org/wp-content/uploads/2014/10/Testing.jpg', function(data) { console.log(data)} );

Produces a similar error when trying to load the image:

Access to Image at 'https://blog.xenproject.org/wp-content/uploads/2014/10/Testing.jpg' from origin 'http://stackoverflow.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://stackoverflow.com' is therefore not allowed access.

Also tried with:

img.crossOrigin = 'anonymous';

And got the same result.

Approach 3 (Canvas using img element):

img = document.createElement('img');
img.onload = function() {
    var canvas = document.createElement('CANVAS');
    var ctx = canvas.getContext('2d');
    var dataURL;
    canvas.height = this.height;
    canvas.width = this.width;
    ctx.drawImage(this, 0, 0);
    dataURL = canvas.toDataURL('image/jpg');
    console.log(dataURL);
};
img.src = 'https://blog.xenproject.org/wp-content/uploads/2014/10/Testing.jpg';

This way at least loads the image but fails on calling toDataURL with:

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported. at HTMLImageElement.img.onload (:9:22)

As an aside, I'm not sure exactly what the CORS policy is protecting against here. Suppose there is some kind of malicious payload that could trigger an exploit. We still load and display the image in the DOM and why would we trust CORS headers set by the same endpoint?

Does anyone know of any solutions to this problem?

Thanks for any help.

like image 932
Chris Avatar asked Apr 10 '17 13:04

Chris


1 Answers

You can get around the CORS restriction by making your request through a CORS proxy, like this:

var proxyUrl = 'https://cors-anywhere.herokuapp.com/',
   targetUrl = 'https://blog.xenproject.org/wp-content/uploads/2014/10/Testing.jpg'
toDataUrl(proxyUrl + targetUrl,
  function(data) { console.log(data)} );

The proxy sends the request to targetUrl, then after it gets the response back for that, adds the Access-Control-Allow-Origin response header & any other CORS response headers needed, and finally then passes that back to your requesting code.

That response with the Access-Control-Allow-Origin header added is what the browser sees, so the browser lets your frontend code access the response and you don’t get any CORS error.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS explains what’s happening that leads to the need to either configure the server the request is being made to such that it sends the necessary CORS headers, or else put a proxy in between to add the CORS headers for you (as described above).

And rather than using a third-party proxy as in the code snippet in this answer, you can pretty easily set up your own CORS proxy using code from https://github.com/Rob--W/cors-anywhere/ or such.


As an aside, I'm not sure exactly what the CORS policy is protecting against here.

In this particular case it’s not really protecting against anything at all, because you can get to https://blog.xenproject.org/wp-content/uploads/2014/10/Testing.jpg using any server-side programming language or a public proxy or curl or whatever.

The maintainers of https://blog.xenproject.org rightly should be have their server configured themselves to send a Access-Control-Allow-Origin response header that tells browsers to allow fetching their content from any origin. But since they don’t, you just have to use a proxy.

The only case where CORS restrictions are really useful is for resources running on an intranet or otherwise behind some kind of firewall. Because in that intranet/firewall case, it’s not a good idea to have arbitrary web apps running with the same authority as the user inside the intranet/firewall.

like image 116
sideshowbarker Avatar answered Nov 19 '22 05:11

sideshowbarker