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

创建于 2018-08-02  ·  12评论  ·  资料来源: niklasvh/html2canvas

var canvasPromise  = html2canvas(document.body, {
                allowTaint: true,
                useCORS: true
            });
canvasPromise.then(function(canvas) {
    document.body.appendChild(canvas);
    console.log(canvas);
    canvas.toDataURL('image/png');
});

错误报告:

未捕获(承诺)DOMException:无法在“HTMLCanvasElement”上执行“toDataURL”:可能无法导出受污染的画布。

  • html2canvas 版本测试:
  • 铬 67.0.3396.99
  • 视窗 10

最有用的评论

我也有同样的问题。
您找到解决方案或解决方法了吗?

所有12条评论

我也有同样的问题。
您找到解决方案或解决方法了吗?

我在 Firefox 61.0.2 中做同样的事情,尽管将 allowTaint 设置为 true,但我还是收到了 SecurityError

据我所知没有解决方案,但我有一个解决方法:将每个图像更改为 base64。 这样,即使它最初来自不同的域,您也可以在画布中渲染它。

你看到了吗: CORS_enabled_image

如果外部内容的来源是 HTML 元素,不允许尝试检索画布的内容。

一旦您将未经 CORS 批准从其他来源加载的任何数据绘制到画布中,画布就会受到污染。

我使用这样的配置选项:

html2canvas(document.body, {
    allowTaint: true,
    foreignObjectRendering: true
});

您好,我们遇到了同样的问题。 我们遵循了@dorklord23 的建议,因为我们已经有了一个进行转换的代理 url。

如果有人发现它有帮助,解决方案是:

      html2canvas(document.body, {
        proxy: this._proxyURL,
        allowTaint: true,
        onclone: (cloned) => convertAllImagesToBase64(this._proxyURL, cloned),
      }).then((canvas) => {
        this._postmessageChannel.send(`get.screenshot:${canvas.toDataURL('image/png')}`);
      });

辅助函数 convertAllImagesToBase64 在哪里:

const convertAllImagesToBase64 = (proxyURL, cloned) => {
  const pendingImagesPromises = [];
  const pendingPromisesData = [];

  const images = cloned.getElementsByTagName('img');

  for (let i = 0; i < images.length; i += 1) {
    // First we create an empty promise for each image
    const promise = new Promise((resolve, reject) => {
      pendingPromisesData.push({
        index: i, resolve, reject,
      });
    });
    // We save the promise for later resolve them
    pendingImagesPromises.push(promise);
  }

  for (let i = 0; i < images.length; i += 1) {
    // We fetch the current image
    fetch(`${proxyURL}?url=${images[i].src}`)
      .then((response) => response.json())
      .then((data) => {
        const pending = pendingPromisesData.find((p) => p.index === i);
        images[i].src = data;
        pending.resolve(data);
      })
      .catch((e) => {
        const pending = pendingPromisesData.find((p) => p.index === i);
        pending.reject(e);
      });
  }

  // This will resolve only when all the promises resolve
  return Promise.all(pendingImagesPromises);
};

export { convertAllImagesToBase64 };

顺便说一下,这是该辅助函数的测试(我们使用 jest 编写 test 和 mockFetch 包):

import { convertAllImagesToBase64 } from '../images';

fetch.resetMocks();

// Mock fetch to respond different for each image so we can assert that the image return the correct response
// Also make one of the response be delayed (2 seconds) to simulate the response is not in the same order we do the call (network latency, image size, etc)
fetch.mockImplementation((url) => {
  if (url.includes('imagesrc1')) {
    return Promise.resolve(new Response(JSON.stringify('data:image/png;base64,1')));
  } else if (url.includes('imagesrc2')) {
    return new Promise((resolve) => setTimeout(resolve(new Response(JSON.stringify('data:image/png;base64,2'))), 2000));
  } else if (url.includes('imagesrc3')) {
    return Promise.resolve(new Response(JSON.stringify('data:image/png;base64,3')));
  }
  return Promise.resolve(new Response(JSON.stringify('')));
});

const mocksImages = [
  { id: 1, src: 'imagesrc1' },
  { id: 2, src: 'imagesrc2' },
  { id: 3, src: 'imagesrc3' },
];

const mockClone = {
  getElementsByTagName: jest.fn(() => mocksImages),
};

describe('utils/images', () => {  
  it('convertAllImagesToBase64. Expect to call 3 times to the correct enpoint using the image source', async () => {
    const allPromises = convertAllImagesToBase64('http://localhost/fake_proxy', mockClone);

    // Expect the clone elements gets all the image tags
    expect(mockClone.getElementsByTagName).toBeCalledWith('img');

    allPromises.then(() => {
      // Expect to have done the 3 fetch calls and with the correct params
      expect(fetch).toBeCalledTimes(3);
      expect(fetch).toHaveBeenNthCalledWith(1, 'http://localhost/fake_proxy?url=imagesrc1');
      expect(fetch).toHaveBeenNthCalledWith(2, 'http://localhost/fake_proxy?url=imagesrc2');
      expect(fetch).toHaveBeenNthCalledWith(3, 'http://localhost/fake_proxy?url=imagesrc3');

      // Expect that our images where updated properly
      expect(mocksImages).toContainEqual({
        id: 1, src: 'data:image/png;base64,1',
      });
      expect(mocksImages).toContainEqual({
        id: 2, src: 'data:image/png;base64,2',
      });
      expect(mocksImages).toContainEqual({
        id: 3, src: 'data:image/png;base64,3',
      });
    });
  });
});

Ruby 后端端点:

require 'base64'
require 'net/http'

module Api
  module V1
    class ImageProxyController < ApiController
      def index
        url   = URI.parse(params[:url])
        image = Net::HTTP.get_response(url)

        render json: data_url(image).to_json, callback: params[:callback]
      end

      private

        def data_url(image)
          "data:#{image.content_type};base64,#{Base64.encode64(image.body)}"
        end

    end
  end
end

我希望有人觉得这有帮助。 我希望它可以帮助人们不要像我们一样投入太多时间来正确解决这个问题。

如果您可以看到任何改进,请提出建议。
问候。

如果你喜欢下面的,会发生什么?

const TempImage = window.Image

 const Image = function() {
        const img = new TempImage()
        img.crossOrigin = 'anonymous'
        return img
 }

找到了一个解决方案,它正在工作

  1. 在调用 html2canvas 时,通过 useCORS true
    html2canvas(selectorElement, {useCORS:true} )
    //做点什么
    });

  2. 正确的 html2canvas.js 文件。 有错别字
    在此 if 块中将“匿名”更改为“匿名”
    if (isInlineBase64Image(src) || useCORS) {
    img.crossOrigin = '匿名';
    }

var canvasPromise  = html2canvas(document.body, {
                allowTaint: true,
                useCORS: true
            });
canvasPromise.then(function(canvas) {
    document.body.appendChild(canvas);
    console.log(canvas);
    canvas.toDataURL('image/png');
});

错误报告:

未捕获(承诺)DOMException:无法在“HTMLCanvasElement”上执行“toDataURL”:可能无法导出受污染的画布。

  • html2canvas 版本测试:
  • 铬 67.0.3396.99
  • 视窗 10

您将只需要使用属性“useCORS: true”,如果您使用属性“allowTaint: true”,您就可以将画布变成受污染的画布

用这个:

var canvasPromise  = html2canvas(document.body, {
                useCORS: true
            });
canvasPromise.then(function(canvas) {
    document.body.appendChild(canvas);
    console.log(canvas);
    canvas.toDataURL('image/png');
});

而不是这个:

var canvasPromise  = html2canvas(document.body, {
                allowTaint: true,
                useCORS: true
            });
canvasPromise.then(function(canvas) {
    document.body.appendChild(canvas);
    console.log(canvas);
    canvas.toDataURL('image/png');
});

你好, html2canvas 做得很好。

可悲的是我面临同样的问题,有没有人解决过这个问题?
已经尝试过@motarock和之前的所有操作,加上组合等。图像是白色的,没有涂漆。

downloadQRCode = (fileName) => {
    html2canvas(document.getElementById('generated-qr-code'), {
      useCORS: true,
      // allowTaint: false,
      // logging: true,
    }).then((canvas) => {
      // document.body.appendChild(canvas); // checking
      const data = canvas.toDataURL('image/jpeg');
      const element = document.createElement('a');
      element.setAttribute('href', data);
      element.setAttribute('download', fileName + '.jpeg');
      document.body.appendChild(element);
      element.click();
      // document.body.removeChild(element);
      console.log("%c data", "font-size:2em;", data, fileName);
      console.log("%c canvas", "font-size:2em;", canvas );
      this.setState({
        imageSrc: data // setting the src of a img tag to check the result. Nothing in it either..
      })
    });

提前致谢

我也有同样的问题。
任何人都可以解决这个问题?

:(同样的问题。我们有带有嵌套 svg 的 html,它不会呈现

已经尝试过@motarock和之前的所有操作,加上组合等。图像是白色的,没有涂漆。

当我不使用 SSL 时,我遇到了这个问题,使用 SSL 可以完美运行

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

stevencherry1 picture stevencherry1  ·  3评论

Josh10101010 picture Josh10101010  ·  3评论

celik75 picture celik75  ·  4评论

koreanman picture koreanman  ·  4评论

AlphaDu picture AlphaDu  ·  4评论