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

μ•ˆλ…•ν•˜μ„Έμš”, μš°λ¦¬λŠ” 같은 λ¬Έμ œμ— μ§λ©΄ν–ˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬λŠ” 이미 λ³€ν™˜μ„ μˆ˜ν–‰ν•œ ν”„λ‘μ‹œ URL이 μžˆμ—ˆκΈ° λ•Œλ¬Έμ— @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 };

그건 κ·Έλ ‡κ³  이것은 κ·Έ λ„μš°λ―Έ κΈ°λŠ₯에 λŒ€ν•œ ν…ŒμŠ€νŠΈμž…λ‹ˆλ‹€(μš°λ¦¬λŠ” ν…ŒμŠ€νŠΈ 및 mockFetch νŒ¨ν‚€μ§€λ₯Ό μž‘μ„±ν•˜κΈ° μœ„ν•΄ jestλ₯Ό μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€):

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

루비 λ°±μ—”λ“œ μ—”λ“œν¬μΈνŠΈ:

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(μΊ”λ²„μŠ€ => {
    //무언가λ₯Ό ν•˜λ‹€
    });

  2. html2canvas.js νŒŒμΌμ„ μˆ˜μ •ν•˜μ„Έμš”. μ˜€νƒ€ μ˜€νƒ€κ°€ μžˆμŠ΅λ‹ˆλ‹€
    이 if λΈ”λ‘μ—μ„œ "anonymous"λ₯Ό "Anonymous"둜 λ³€κ²½ν•˜μ‹­μ‹œμ˜€.
    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 λ“±κΈ‰