Html2canvas: Не удалось выполнить toDataURL на HTMLCanvasElement: испорченные холсты не могут быть экспортированы.

Созданный на 2 авг. 2018  ·  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: не удалось выполнить toDataURL для HTMLCanvasElement: испорченные холсты не могут быть экспортированы.

  • Версия html2canvas протестирована с помощью:
  • Хром 67.0.3396.99
  • Windows 10

Самый полезный комментарий

У меня такие же проблемы.
Вы нашли решение или обходной путь?

Все 12 Комментарий

У меня такие же проблемы.
Вы нашли решение или обходной путь?

Я делаю то же самое в Firefox 61.0.2, и я получаю SecurityError, несмотря на то, что для параметра allowTaint установлено значение true

Насколько я знаю, решения нет, но у меня есть обходной путь: поменяйте каждое изображение на base64. Таким образом, вы можете отобразить его на холсте, даже если он изначально из другого домена.

Вы видели это: CORS_enabled_image

Если источником чужого контента является HTML элемент, попытки получить содержимое холста не разрешены.

Как только вы рисуете на холсте любые данные, которые были загружены из другого источника без утверждения CORS, холст становится испорченным.

Я использую такие параметры конфигурации:

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

Привет, мы столкнулись с той же проблемой. Мы последовали предложению @ dorklord23, потому что у нас уже был прокси-адрес, по которому выполнялось преобразование.

Если кто-то нашел это полезным, решение было:

      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} ) .then (canvas => {
    //сделай что-нибудь
    });

  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: не удалось выполнить toDataURL для HTMLCanvasElement: испорченные холсты не могут быть экспортированы.

  • Версия html2canvas протестирована с помощью:
  • Хром 67.0.3396.99
  • Windows 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..
      })
    });

заранее спасибо

У меня такие же проблемы.
Кто-нибудь может исправить эту проблему?

:( Та же проблема. у нас есть html с вложенным svg, и он не будет отображать

Уже пробовал @motarock и все до этого плюс комбинации и т.д.

У меня эта проблема, когда я не использую SSL, с SSL работает отлично

Была ли эта страница полезной?
0 / 5 - 0 рейтинги