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' μ€ν μ€ν¨: μ€μΌλ μΊλ²μ€λ₯Ό λ΄λ³΄λΌ μ μμ΅λλ€.
λλ κ°μ λ¬Έμ κ° μμ΅λλ€.
ν΄κ²°μ±
μ΄λ ν΄κ²° λ°©λ²μ μ°Ύμμ΅λκΉ?
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
}
μ루μ μ μ°Ύμκ³ μλ μ€μ λλ€.
html2canvasλ₯Ό νΈμΆνλ λμ useCORS trueλ₯Ό μ λ¬ν©λλ€.
html2canvas(selectorElement, {useCORS:true} ).then(μΊλ²μ€ => {
//무μΈκ°λ₯Ό νλ€
});
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μ΄ μλ²½νκ² μλν©λλ€.
κ°μ₯ μ μ©ν λκΈ
λλ κ°μ λ¬Έμ κ° μμ΅λλ€.
ν΄κ²°μ± μ΄λ ν΄κ²° λ°©λ²μ μ°Ύμμ΅λκΉ?