Html2canvas: Fehler beim Ausführen von 'toDataURL' auf 'HTMLCanvasElement': Befleckte Leinwände können nicht exportiert werden.

Erstellt am 2. Aug. 2018  ·  12Kommentare  ·  Quelle: 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');
});

Fehlerberichte:

Nicht abgefangen (versprochen) DOMException: Fehler beim Ausführen von 'toDataURL' auf 'HTMLCanvasElement': Befleckte Canvases können nicht exportiert werden.

  • html2canvas-Version getestet mit:
  • Chrom 67.0.3396.99
  • Windows 10

Hilfreichster Kommentar

Ich habe auch die gleichen Probleme.
Haben Sie eine Lösung oder einen Workaround gefunden?

Alle 12 Kommentare

Ich habe auch die gleichen Probleme.
Haben Sie eine Lösung oder einen Workaround gefunden?

Ich mache das gleiche in Firefox 61.0.2 und bekomme einen SecurityError, obwohl ich allowTaint auf true gesetzt habe

Keine Lösung, soweit ich weiß, aber ich habe einen Workaround: Ändern Sie jedes Image in base64. Auf diese Weise können Sie es im Canvas rendern, obwohl es ursprünglich aus einer anderen Domäne stammt.

Hast du das gesehen: CORS_enabled_image

Wenn die Quelle des fremden Inhalts ein HTML ist -Element ist der Versuch, den Inhalt des Zeichenbereichs abzurufen, nicht zulässig.

Sobald Sie Daten, die ohne CORS-Genehmigung von einem anderen Ursprung geladen wurden, in eine Leinwand zeichnen, wird die Leinwand verfälscht.

Ich verwende die Konfigurationsoptionen wie folgt:

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

Hallo, wir standen vor dem gleichen Problem. Wir sind dem Vorschlag von @dorklord23 gefolgt, da wir bereits eine Proxy-URL hatten, die die Konvertierung durchführte.

Wenn es jemand hilfreich fand, war die Lösung:

      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')}`);
      });

Wo ist die Hilfsfunktion 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 };

Dies sind übrigens die Tests für diese Hilfsfunktion (wir verwenden jest zum Schreiben von Test- und MockFetch-Paketen):

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-Backend-Endpunkt:

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

Ich hoffe, jemand findet das hilfreich. Ich hoffe, es hilft jemandem, nicht so viel Zeit wie wir zu investieren, um das Problem richtig zu beheben.

Wenn Sie eine Verbesserung sehen können, schlagen Sie sie bitte vor.
Grüße.

Wenn Sie unten mögen, was wird passieren?

const TempImage = window.Image

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

Lösung gefunden und es funktioniert

  1. Übergeben Sie beim Aufrufen von html2canvas useCORS true
    html2canvas(selectorElement, {useCORS:true} ).then(canvas => {
    //etwas tun
    });

  2. Korrigieren Sie die Datei html2canvas.js. Es liegt ein Tippfehler vor
    Ändere "anonym" in "Anonym" in diesem if-Block
    if (isInlineBase64Image(src) || ​​useCORS) {
    img.crossOrigin = 'Anonym';
    }

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

Fehlerberichte:

Nicht abgefangen (versprochen) DOMException: Fehler beim Ausführen von 'toDataURL' auf 'HTMLCanvasElement': Befleckte Canvases können nicht exportiert werden.

  • html2canvas-Version getestet mit:
  • Chrom 67.0.3396.99
  • Windows 10

Sie müssen nur die Eigenschaft 'useCORS: true' verwenden, wenn Sie die Eigenschaft 'allowTaint: true' verwenden, geben Sie die Berechtigung, Ihre Leinwand in eine verfälschte Leinwand zu verwandeln

BENUTZE DAS:

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

AN STELLE VON:

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

Hallo, schöne Arbeit für html2canvas.

Ich stehe leider vor dem gleichen Problem, hat das schon jemand gelöst?
@motarock bereits ausprobiert und alles davor, plus Kombinationen usw. Das Bild ist weiß ohne gemalt.

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..
      })
    });

Danke im Voraus

Ich habe auch die gleichen Probleme.
Kann jemand dieses Problem beheben?

:( gleiches Problem. Wir haben HTML mit verschachteltem SVG und es wird nicht gerendert

@motarock bereits ausprobiert und alles davor, plus Kombinationen usw. Das Bild ist weiß ohne gemalt.

Ich habe dieses Problem, wenn ich SSL nicht verwende. Mit SSL funktioniert es perfekt

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

Loki180 picture Loki180  ·  4Kommentare

rrutkows picture rrutkows  ·  4Kommentare

deepender87 picture deepender87  ·  4Kommentare

tibewww picture tibewww  ·  4Kommentare

kunal886496 picture kunal886496  ·  3Kommentare