canvas.toDataURL() 安全错误

84

所以我正在使用Google地图,我得到了这张图片,它看起来像这样

<img id="staticMap"
        src="http://maps.googleapis.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=13&size=600x300&maptype=roadmap
&markers=color:blue%7Clabel:S%7C40.702147,-74.015794&markers=color:green%7Clabel:G%7C40.711614,-74.012318
&markers=color:red%7Ccolor:red%7Clabel:C%7C40.718217,-73.998284&sensor=false">

我需要保存它。我找到了这个:

function getBase64FromImageUrl(URL) {
    var img = new Image();
    img.src = URL;
    img.onload = function() {

        var canvas = document.createElement("canvas");
        canvas.width = this.width;
        canvas.height = this.height;

        var ctx = canvas.getContext("2d");
        ctx.drawImage(this, 0, 0);

        var dataURL = canvas.toDataURL("image/png");

        alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));

    };
}

但我遇到了这个问题:

未捕获的SecurityError错误:无法在“HTMLCanvasElement”上执行“toDataURL”:污染的画布可能无法导出。

我搜索了解决方法。我在这里找到了一个样例如何使用CORS,但我仍然无法将这两个代码片段结合起来使其正常工作。也许我做错了,有更简单的方法可以实现吗?我正在尝试保存这张图片,以便将数据传输到我的服务器。所以也许有人做过类似的事情,知道如何使.toDataURL()按照我的要求工作?

8个回答

110

如果谷歌没有正确使用Access-Control-Allow-Origin头来服务此图像,则您将无法在画布中使用它。这是因为没有CORS批准。您可以在此处了解更多信息,但基本上意味着:

  

虽然在画布中可以使用没有CORS批准的图像,但这会使画布变得不安全。一旦画布被污染,就无法从画布中取回数据。例如,您无法再使用canvas toBlob(),toDataURL()或getImageData()方法;这样做会抛出安全错误。

     

这可以防止使用图像从远程网站拉取信息并未经许可而暴露用户私人数据。

我建议只需将URL传递到服务器端语言,并使用curl下载图像。但要小心进行净化!

编辑:

由于此答案仍然是被接受的答案,因此您应该查看@shadyshrif的答案,即使用:

var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;

如果您拥有正确的权限,这将起作用,但至少可以让您做想做的事情。


1
我会进一步接受@Prisoner提出来的远程替代建议并建议将其作为默认行为。服务器几乎肯定能够接收_img.src_,然后比客户端更快地* wget * / * cURL *图像,将其POST到服务器。也许,仅在需要客户端身份验证的情况下(即您的服务器尝试以403结束),您可以回退到使用canvas.toDataURL(img)希望它与页面相同来源。这个默认行为还将导致原始文件(EXIF等)保留。 - Alastair
4
我已将我的 Google Cloud 存储桶设置为“Access-Control-Allow-Origin: *”,但在尝试使用 toDataURL() 方法时仍然出现安全错误。该图像通过从“getImageServingUrl”生成的 URL 提供给 Canvas。 - Mr Pablo
1
有解决方案了吗?这个答案很差劲。 - Yeats
太好了!非常感谢你的回答! - Kevin Vincent
5
TIL№╝џтюеУ«Йуй«img.srcС╣ІтЅЇУ«Йуй«crossOriginт▒ъТђДУЄ│тЁ│жЄЇУдЂсђѓ - horstwilhelm
它不起作用,因为在我的情况下,服务器没有发送CORS标头。如果不使用crossOrigin属性,我可以加载图像,但是使用它却不能。 - Ionel Lupu

61

只需使用crossOrigin属性,并将'anonymous'作为第二个参数传递即可。

var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;

8
澄清一下,这对于直接不允许您访问资源的服务器是行不通的。也就是说,这仅适用于公共资源或配置错误的服务器。 - m3nda
3
为了避免存储服务器未包含Access-Control-Allow-Origin标头的304响应,我不得不向图像URL添加时间戳。像这样:img.src = url + '?' + new Date().getTime();翻译完毕。 - Kirby
在IE11中,使用data-url源的图像对我不起作用。 - pumbo

6
该方法将防止您从访问的服务器上获得“Access-Control-Allow-Origin”错误。
var img = new Image();
var timestamp = new Date().getTime();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url + '?' + timestamp;

4

尝试下面的代码...

<img crossOrigin="anonymous"
     id="imgpicture" 
     fall-back="images/penang realty,Apartment,house,condominium,terrace house,semi d,detached,
                bungalow,high end luxury properties,landed properties,gated guarded house.png" 
     ng-src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" 
     height="220"
     width="200"
     class="watermark">

4
fall-back 属性的无意义值的原因是什么? - y.bregey
1
我相信这能够工作的唯一原因是 crossOrigin="anonymous",其余都不相关。我也很确定 ng-src 不是本机 JavaScript。 - Dane Brouwer

0
在我的情况下,我正在使用WebBrowser控件(强制使用IE 11),但我无法解决这个错误。转而使用使用Chrome的CefSharp解决了我的问题。

0

我遇到了同样的错误消息。我将文件保存为简单的 .html 文件格式,当我将文件传递给 Apache 中的 PHP 时它可以工作。

html2canvas(document.querySelector('#toPrint')).then(canvas => {
            let pdf = new jsPDF('p', 'mm', 'a4');
            pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 0, 0, 211, 298);
            pdf.save(filename);
        });

-2

如果第三方网站的图片没有设置跨域请求头("access-control-allow-origin"),即使你使用setAttribute('crossOrigin', 'anonymous'),你也无法通过Chrome下载图片文件。

  1. 黑客Chrome(使用扩展程序,这只适用于您的计算机)
  2. 通过在您的站点上运行的服务代理图像。浏览器将把域视为您的站点。这需要您的服务从第三方请求图像。

-3

通过使用fabric js,我们可以解决IE中的安全错误问题。

    function getBase64FromImageUrl(URL) {
        var canvas  = new fabric.Canvas('c');
        var img = new Image();
        img.onload = function() {
            var canvas1 = document.createElement("canvas");
            canvas1.width = this.width;
            canvas1.height = this.height;
            var ctx = canvas.getContext('2d');
            ctx.drawImage(this, 0, 0);
            var dataURL = canvas.toDataURL({format: "png"});
        };
        img.src = URL;
    }

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