HTML中的<img src=...>可以正常工作,但JS加载图片会导致CORS错误。

31

我想用js+jquery创建图片编辑器。在第一步中,我要求用户提供图像url。但是,在尝试在JS内部加载图像数据(以生成base64图像URI)时,我遇到了问题。控制台中出现了错误:...已被CORS策略阻止:访问控制允许来源...。但我想知道为什么会这样?如果在HTML文件中,我创建一个例如(image hotlink):

<img src="http://example.com/image.jpg">

<img  src="https://static.pexels.com/photos/87293/pexels-photo-87293.jpeg" />

浏览器可以加载图像而不会出现任何CORS问题!以下是我的JS代码,用于处理同一图像时的CORS问题:

浏览器可以加载图像而不会出现任何CORS问题!以下是我的JS代码,用于处理同一图像时的CORS问题:

 function downloadFile(url) {
    console.log({url});    
    var img = new Image();
    img.onload = function() {
        console.log('ok'); 
        // never execute because cors error
        // … make base64 uri with image data needed for further processing
    };

    img.crossOrigin = "Anonymous";
    img.src = url;
}
所以问题是 - 如何强制JS加载图像(就像html标签加载它),并将其转换为base64 URL来避免CORS问题? https://static.pexels.com/photos/87293/pexels-photo-87293.jpeg

你在测试哪个Web服务器? - J. Titus
我没有使用任何Web服务器 - 我创建了一个Fat客户端应用程序:只有JS + HTML + CSS。 - Kamil Kiełczewski
1
这就是你的问题。你不能仅仅从文件系统中运行文件。看看在LAMP/XAMPP上进行测试/开发或使用像http-server这样的NodeJS轻量级服务器。 - J. Titus
不,我使用Node.js作为我的Web服务器。我误解了你的问题 - 当然,我将我的JS + HTML + CSS文件放入文件服务器中。 - Kamil Kiełczewski
也许这个可以帮到你:https://dev59.com/AmMl5IYBdhLWcg3winkW - J. Titus
1
@J.Titus — 由于图像URL由用户提供,并且(可能)是第三方托管的,因此这似乎不太可能。 - Quentin
3个回答

22

我尝试自己找到解决方案(JS ES6),但只找到了部分。我们能够从不支持 CORS 的 src 中加载 img 到 canvas,但浏览器会将 canvas 切换到“taint mode”,这将阻止我们调用 toDataURL(以及任何其他内容的访问)。

function loadImgAsBase64(url, callback) {
  let canvas = document.createElement('CANVAS');
  let img = document.createElement('img');
  //img.setAttribute('crossorigin', 'anonymous');
  img.src = url;

  img.onload = () => {
    canvas.height = img.height;
    canvas.width = img.width;
    let context = canvas.getContext('2d');
    context.drawImage(img, 0, 0);
    let dataURL = canvas.toDataURL('image/png');
    canvas = null;
    callback(dataURL);
  };
}


let url = 'http://lorempixel.com/500/150/sports/9/';

this.loadImgAsBase64(url, (dataURL) => {
   msg.innerText = dataURL.slice(0,50)+'...';
});
IMAGE DATA: loading...<br>
<div id="msg"></div>

所以,克服这个障碍的唯一方法是创建代理服务器(例如在PHP中),该服务器将启用CORS,并下载给定URL的图像并将其发送回我们在JS中的应用程序。我找到了一些免费的服务器https://cors-anywhere.herokuapp.com,我们可以在开发测试中使用。以下是完整的功能代码,可以从给定的图像网址返回数据URI:

function loadImgAsBase64(url, callback) {
  let canvas = document.createElement('CANVAS');
  let img = document.createElement('img');
  img.setAttribute('crossorigin', 'anonymous');
  img.src = 'https://cors-anywhere.herokuapp.com/' + url;

  img.onload = () => {
    canvas.height = img.height;
    canvas.width = img.width;
    let context = canvas.getContext('2d');
    context.drawImage(img, 0, 0);
    let dataURL = canvas.toDataURL('image/png');
    canvas = null;
    callback(dataURL);
  };
}


let url = 'http://lorempixel.com/500/150/sports/9/';

this.loadImgAsBase64(url, (dataURL) => {
   msg.innerText = dataURL.slice(0,50)+'...';
   // show pic
   document.body.innerHTML += `<img src="${dataURL}">`
});
IMAGE DATA Loading...<br>
<div id="msg"></div>

就是这样 :) (我在Chrome、Firefox和Safari上进行了测试)


1
请注意,托管此类代理服务器将使您的服务器容易受到服务器端请求伪造(SSRF)攻击。 - Heinzi
@Heinzi,您能否进一步解释一下这种攻击如何工作-在我们的代理设置CORS仅允许来自我们JS应用程序域的请求的情况下? - Kamil Kiełczewski
1
假设您的Web服务器还具有仅限本地主机的/admin管理界面。假设此管理界面可以在/admin/stats?format=png以图像形式显示当前使用情况统计信息。现在,我启动您的图片编辑器并将http://localhost/admin/stats?format=png输入为图像URL。您的PHP代理愉快地为我获取该文件,并且请求成功,因为它来自Web服务器本身。 - Heinzi
@Heinzi,我不确定我是否完全理解您的意思。我想允许我的用户获取任何图像并进行编辑。因此,他们转到 https://myeditor.com,输入其图像链接并可以编辑它(因为 https://myeditor.com 使用带有允许CORS的 https://myproxy.com 以“编辑模式”读取图像)。我不明白这种行为存在什么问题?任何其他第三方网站(如 https://evil.com)都无法使用我的代理程序来获取图像...只有使用 https://myeditor.com 的用户才能使用我的代理程序...问题出在哪里? :) - Kamil Kiełczewski
3
用户可以通过将图片链接放入你的编辑器中,让你的代理服务器获取他们想要的任何图片。请求将来自你的代理服务器,使用你的代理服务器的IP地址和网络权限。这本身并不是问题,但如果你的代理服务器可以访问世界上不应该访问的任何内容,则会成为一个问题。 - Heinzi

1

对于我来说,在一种环境中解决了问题,但在另一个环境中部署相同的代码后,出现了相同的错误。 - Siluveru Kiran Kumar

-5

避免CORS的唯一方法是进行更改以避免跨域共享,至少让浏览器认为它是跨域的。

或者,您必须修改服务器以支持CORS。


2
如何避免跨域共享?请进行更改。以下是示例代码: - Kamil Kiełczewski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接