【问题标题】:'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported“HTMLCanvasElement”上的“toDataURL”:可能无法导出受污染的画布
【发布时间】:2018-04-09 14:38:25
【问题描述】:

在 Chrome 中,当尝试从远程 url 测试画布绘图图像时,我在 jsfiddle 上收到以下错误。

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

在 S3 存储桶上,我有以下 CORS 策略,允许跨资源共享:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
</CORSRule>
</CORSConfiguration>

如果我使用 jsfiddle 作为原点卷曲图像,我得到:

curl -H 'Origin: https://fiddle.jshell.net' -I 'https://i.ezr.io/products3/472_ARDSCR.jpg?h=45&w=165&fit=scale'
HTTP/1.1 200 OK
Cache-Control: public,max-age=31536000
Last-Modified: Wed, 07 Feb 2018 23:42:47 GMT
Server: imgix-fe
Content-Length: 6371
Accept-Ranges: bytes
Date: Fri, 16 Mar 2018 17:10:20 GMT
Age: 3173253
Connection: keep-alive
Content-Type: image/jpeg
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Served-By: cache-lax8630-LAX, cache-sea1027-SEA
X-Cache: HIT, HIT

var Rack = {
  init: function(params) {
    Rack.conf = params;
    Rack.bindEvents();
  },
  bindEvents: function() {
    Rack.conf.scaleDown.addEventListener("click", function() {
      Rack.clearCanvas();
      Rack.conf.scaleW = Rack.round(Rack.conf.scaleW - .1, 1);
      Rack.conf.scaleY = Rack.round(Rack.conf.scaleY - .1, 1);

      if (Rack.conf.scaleW > 0 && Rack.conf.scaleY > 0) {
        Rack.build();
      }
    });

    Rack.conf.resetScale.addEventListener("click", function() {
      Rack.clearCanvas();
      Rack.conf.scaleW = 1;
      Rack.conf.scaleY = 1;
      Rack.build(true);
    });

    Rack.getRackJSON();
    Rack.setNumImages();
    Rack.build();
  },
  getRackJSON: function() { // will send ajax call based on item being added to the rack, will return json rack
    Rack.conf.rack = {
      "awards": [{
          "src": "https://i.ezr.io/products3/472_ARDSCR.jpg?h=45&w=165&fit=scale",
          "name": "Army Distinguished Service Cross",
          "sku": "472_ARDSCR",
          "x": 0,
          "y": 0,
          "width": 165,
          "height": 45,
          "attachments": [{
            "src": "https://i.ezr.io/products3/913NP.png?h=20",
            "name": "Bronze Oak Leaf",
            "sku": "913NP",
            "x": 73,
            "y": 13,
            "width": 20,
            "height": 20
          }]
        },
        {
          "src": "https://i.ezr.io/products3/470_ARDDR.jpg?h=45&w=165&fit=scale",
          "name": "Department of Defense Distinguished Service",
          "sku": "470_ARDDR",
          "x": 165,
          "y": 0,
          "width": 165,
          "height": 45
        }
      ]
    };
  },
  setNumImages() {
    for (var i = 0; i < Rack.conf.rack.awards.length; i++) {
      var award = Rack.conf.rack.awards[i];

      ++Rack.conf.numImages;

      if (award.hasOwnProperty('attachments')) {
        Rack.conf.numImages += award.attachments.length;
      }
    }
  },
  loadImages: function(callback) {
    var numImagesLoaded = 0;

    for (var i = 0; i < Rack.conf.rack.awards.length; i++) {
      var award = Rack.conf.rack.awards[i];

      Rack.conf.images[award.sku] = new Image();
      Rack.conf.images[award.sku].onload = function() {
        if (++numImagesLoaded >= Rack.conf.numImages) {
          callback();
        }
      }
      Rack.conf.images[award.sku].src = award.src;

      if (award.hasOwnProperty('attachments')) {
        for (var j = 0; j < award.attachments.length; j++) {
          var attachment = award.attachments[j];

          Rack.conf.images[attachment.sku] = new Image();
          Rack.conf.images[attachment.sku].crossOrigin = 'anonymous';
          Rack.conf.images[attachment.sku].onload = function() {
            if (++numImagesLoaded >= Rack.conf.numImages) {
              callback();
            }
          }
          Rack.conf.images[attachment.sku].src = attachment.src;
        }
      }
    }
  },
  build: function(reset) {
    if (Rack.conf.outputType === 'jpg') {
      Rack.conf.ctx.fillStyle = "#ffffff";
      Rack.conf.ctx.fillRect(0, 0, Rack.conf.c.width, Rack.conf.c.height);
    }

    reset === true ? Rack.conf.ctx.setTransform(1, 0, 0, 1, 0, 0) : Rack.conf.ctx.scale(Rack.conf.scaleW, Rack.conf.scaleY);

    Rack.loadImages(function() {
      for (var i = 0; i < Rack.conf.rack.awards.length; i++) {
        var award = Rack.conf.rack.awards[i];

        Rack.conf.ctx.drawImage(Rack.conf.images[award.sku], award.x, award.y, award.width, award.height);

        if (award.hasOwnProperty('attachments')) {
          for (var j = 0; j < award.attachments.length; j++) {
            var attachment = award.attachments[j];

            Rack.conf.ctx.drawImage(Rack.conf.images[attachment.sku], attachment.x, attachment.y, attachment.width, attachment.height);
          }
        }
      }
    });

    Rack.conf.imageData = Rack.conf.c.toDataURL((Rack.conf.outputType === 'jpg' ? 'image/jpeg' : null), (Rack.conf.outputType === 'jpg' ? 1.0 : null));
  },
  clearCanvas: function() {
    Rack.conf.ctx.clearRect(0, 0, Rack.conf.c.width, Rack.conf.c.height);
  },
  round: function(number, precision) {
    var factor = Math.pow(10, precision);
    var tempNumber = number * factor;
    var roundedTempNumber = Math.round(tempNumber);
    return roundedTempNumber / factor;
  }
};

Rack.init({
  c: document.getElementById("myCanvas"),
  ctx: document.getElementById("myCanvas").getContext("2d"),
  outputType: 'png',
  scaleDown: document.getElementById('scale-down'),
  resetScale: document.getElementById('reset-scale'),
  images: {},
  numImages: 0,
  awards: null,
  imageData: null,
  scaleW: 1,
  scaleY: 1,
  rack: null
});
<canvas id="myCanvas" width="330" height="45">Your browser does not support the HTML5 canvas tag.</canvas>
<div>
  <button id="scale-down" style="cursor:pointer;">
        Scale Down
    </button>
  <button id="reset-scale" style="cursor:pointer;">
        Reset
    </button>
</div>

【问题讨论】:

  • 它是图像托管服务器,通过在响应中添加适当的 CORS 标头来允许跨域。如果服务器的响应中不存在此内容,则您无法访问图像中包含的像素数据。
  • Access-Control-Allow-Origin: * 正在返回,所以应该没有 CORS 问题。
  • 问题已解决:缺少 Rack.conf.images[award.sku].crossOrigin = 'anonymous';

标签: cors html5-canvas jsfiddle


【解决方案1】:

问题已解决:缺少 Rack.conf.images[award.sku].crossOrigin = 'anonymous';

【讨论】:

    【解决方案2】:

    您现在已经知道问题的答案,即添加Rack.conf.images[award.sku].crossOrigin = 'anonymous';

    下面是解释需求的类似问题的线程

    https://github.com/locomotivecms/engine/issues/1152

    toDataURL throw Uncaught Security exception

    https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image

    什么是“受污染的”画布?

    尽管您可以在画布中使用未经 CORS 批准的图像,但这样做会污染画布。一旦画布被污染,您就不能再将数据拉出画布。例如,您不能再使用画布的 toBlob()、toDataURL() 或 getImageData() 方法;这样做会引发安全错误。

    这可以防止用户通过使用图像在未经许可的情况下从远程网站提取信息而暴露私人数据。

    【讨论】:

      猜你喜欢
      • 2017-08-22
      • 1970-01-01
      • 2016-04-07
      • 2020-02-20
      • 2018-08-02
      • 1970-01-01
      • 2017-07-12
      • 2021-02-10
      • 2022-08-19
      相关资源
      最近更新 更多