Html2canvas: Gagal menjalankan 'toDataURL' di 'HTMLCanvasElement': Kanvas tercemar tidak boleh diekspor.

Dibuat pada 2 Agu 2018  ·  12Komentar  ·  Sumber: 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');
});

Laporan bug:

Tidak tertangkap (dalam janji) DOMException: Gagal menjalankan 'toDataURL' di 'HTMLCanvasElement': Kanvas tercemar tidak boleh diekspor.

  • versi html2canvas diuji dengan:
  • Chrome 67.0.3396.99
  • Windows 10

Komentar yang paling membantu

Saya juga memiliki masalah yang sama.
Apakah Anda menemukan solusi atau solusi?

Semua 12 komentar

Saya juga memiliki masalah yang sama.
Apakah Anda menemukan solusi atau solusi?

saya melakukan hal yang sama di Firefox 61.0.2 dan saya mendapatkan SecurityError meskipun mengatur allowTaint ke true

Tidak ada solusi sejauh yang saya tahu, tetapi saya punya solusi: ubah setiap gambar ke base64. Dengan begitu, Anda bisa merendernya di kanvas meskipun berasal dari domain yang berbeda.

Pernahkah Anda melihat ini: CORS_enabled_image

Jika sumber konten asing adalah HTML elemen, mencoba mengambil konten kanvas tidak diperbolehkan.

Segera setelah Anda menggambar ke kanvas data apa pun yang dimuat dari asal lain tanpa persetujuan CORS, kanvas menjadi tercemar.

Saya menggunakan opsi konfigurasi seperti ini:

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

Hai, kami menghadapi masalah yang sama. Kami mengikuti saran @dorklord23 karena kami sudah memiliki url proxy yang melakukan konversi.

Jika seseorang merasa terbantu, solusinya adalah:

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

Di mana fungsi pembantu convertAllImagesToBase64 adalah:

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

Omong-omong, ini adalah tes untuk fungsi pembantu itu (kami menggunakan lelucon untuk tes menulis dan paket 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 backend endpoint:

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

Saya harap seseorang menemukan ini bermanfaat. Saya harap ini membantu seseorang untuk tidak menginvestasikan waktu sebanyak yang kami lakukan untuk memperbaikinya dengan benar.

Jika Anda dapat melihat peningkatan, harap sarankan.
Salam.

Jika Anda melakukan seperti di bawah ini, apa yang akan terjadi?

const TempImage = window.Image

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

Menemukan solusi dan berhasil

  1. Saat memanggil html2canvas, berikan useCORS true
    html2canvas(selectorElement, {useCORS:true} ).then(canvas => {
    //lakukan sesuatu
    });

  2. File html2canvas.js yang benar. Ada kesalahan ketik
    Ubah "anonim" menjadi "Anonim" di blok if ini
    if (isInlineBase64Image(src) || ​​useCORS) {
    img.crossOrigin = 'Anonim';
    }

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

Laporan bug:

Tidak tertangkap (dalam janji) DOMException: Gagal menjalankan 'toDataURL' di 'HTMLCanvasElement': Kanvas tercemar tidak boleh diekspor.

  • versi html2canvas diuji dengan:
  • Chrome 67.0.3396.99
  • Windows 10

Anda hanya perlu menggunakan properti 'useCORS: true', jika Anda menggunakan properti 'allowTaint: true' Anda memberikan izin untuk mengubah kanvas Anda menjadi kanvas yang tercemar

GUNAKAN INI:

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

BUKAN INI:

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

Halo, kerja bagus untuk html2canvas.

Sayangnya saya menghadapi masalah yang sama, adakah yang menyelesaikan ini?
Sudah mencoba @motarock dan semua sebelum itu, ditambah kombinasi, dll.

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

Terima kasih sebelumnya

Saya juga memiliki masalah yang sama.
Adakah yang bisa memperbaiki masalah ini?

:( masalah yang sama. kami memiliki html dengan svg bersarang dan itu tidak akan dirender

Sudah mencoba @motarock dan semua sebelum itu, ditambah kombinasi, dll.

Saya memiliki masalah ini ketika saya tidak menggunakan SSL, Dengan SSL berfungsi dengan baik

Apakah halaman ini membantu?
0 / 5 - 0 peringkat