mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Add support for loading cross origin images using proxy
This commit is contained in:
@ -59,9 +59,9 @@ const testBase64 = (document: Document, src: string): Promise<boolean> => {
|
||||
});
|
||||
};
|
||||
|
||||
const testCORS = () => {
|
||||
return typeof new Image().crossOrigin !== 'undefined';
|
||||
};
|
||||
const testCORS = () => typeof new Image().crossOrigin !== 'undefined';
|
||||
|
||||
const testResponseType = () => typeof new XMLHttpRequest().responseType === 'string';
|
||||
|
||||
const testSVG = document => {
|
||||
const img = new Image();
|
||||
@ -156,6 +156,20 @@ const FEATURES = {
|
||||
const value = testCORS();
|
||||
Object.defineProperty(FEATURES, 'SUPPORT_CORS_IMAGES', {value});
|
||||
return value;
|
||||
},
|
||||
// $FlowFixMe - get/set properties not yet supported
|
||||
get SUPPORT_RESPONSE_TYPE() {
|
||||
'use strict';
|
||||
const value = testResponseType();
|
||||
Object.defineProperty(FEATURES, 'SUPPORT_RESPONSE_TYPE', {value});
|
||||
return value;
|
||||
},
|
||||
// $FlowFixMe - get/set properties not yet supported
|
||||
get SUPPORT_CORS_XHR() {
|
||||
'use strict';
|
||||
const value = 'withCredentials' in new XMLHttpRequest();
|
||||
Object.defineProperty(FEATURES, 'SUPPORT_CORS_XHR', {value});
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@ export type ImageElement = Image | HTMLCanvasElement;
|
||||
type ImageCache<T> = {[string]: Promise<T>};
|
||||
|
||||
import FEATURES from './Feature';
|
||||
import {Proxy} from './Proxy';
|
||||
|
||||
// $FlowFixMe
|
||||
export default class ImageLoader<T> {
|
||||
@ -46,7 +47,10 @@ export default class ImageLoader<T> {
|
||||
return this.addImage(src, src, false);
|
||||
} else if (!this.isSameOrigin(src)) {
|
||||
if (typeof this.options.proxy === 'string') {
|
||||
// TODO proxy
|
||||
this.cache[src] = Proxy(src, this.options).then(src =>
|
||||
loadImage(src, this.options.imageTimeout || 0)
|
||||
);
|
||||
return src;
|
||||
} else if (this.options.useCORS === true && FEATURES.SUPPORT_CORS_IMAGES) {
|
||||
return this.addImage(src, src, true);
|
||||
}
|
||||
@ -61,7 +65,12 @@ export default class ImageLoader<T> {
|
||||
if (this.hasImageInCache(src)) {
|
||||
return this.cache[src];
|
||||
}
|
||||
// TODO proxy
|
||||
if (!this.isSameOrigin(src) && typeof this.options.proxy === 'string') {
|
||||
return (this.cache[src] = Proxy(src, this.options).then(src =>
|
||||
loadImage(src, this.options.imageTimeout || 0)
|
||||
));
|
||||
}
|
||||
|
||||
return this.xhrImage(src);
|
||||
}
|
||||
|
||||
@ -71,7 +80,12 @@ export default class ImageLoader<T> {
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status !== 200) {
|
||||
reject(`Failed to fetch image ${src} with status code ${xhr.status}`);
|
||||
reject(
|
||||
`Failed to fetch image ${src.substring(
|
||||
0,
|
||||
256
|
||||
)} with status code ${xhr.status}`
|
||||
);
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
// $FlowFixMe
|
||||
@ -87,7 +101,9 @@ export default class ImageLoader<T> {
|
||||
const timeout = this.options.imageTimeout;
|
||||
xhr.timeout = timeout;
|
||||
xhr.ontimeout = () =>
|
||||
reject(__DEV__ ? `Timed out (${timeout}ms) fetching ${src}` : '');
|
||||
reject(
|
||||
__DEV__ ? `Timed out (${timeout}ms) fetching ${src.substring(0, 256)}` : ''
|
||||
);
|
||||
}
|
||||
xhr.open('GET', src, true);
|
||||
xhr.send();
|
||||
|
62
src/Proxy.js
Normal file
62
src/Proxy.js
Normal file
@ -0,0 +1,62 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
import type Options from './index';
|
||||
|
||||
import FEATURES from './Feature';
|
||||
|
||||
export const Proxy = (src: string, options: Options): Promise<string> => {
|
||||
if (!options.proxy) {
|
||||
return Promise.reject(__DEV__ ? 'No proxy defined' : null);
|
||||
}
|
||||
const proxy = options.proxy;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const responseType =
|
||||
FEATURES.SUPPORT_CORS_XHR && FEATURES.SUPPORT_RESPONSE_TYPE ? 'blob' : 'text';
|
||||
const xhr = FEATURES.SUPPORT_CORS_XHR ? new XMLHttpRequest() : new XDomainRequest();
|
||||
xhr.onload = () => {
|
||||
if (xhr instanceof XMLHttpRequest) {
|
||||
if (xhr.status === 200) {
|
||||
if (responseType === 'text') {
|
||||
resolve(xhr.response);
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
// $FlowFixMe
|
||||
reader.addEventListener('load', () => resolve(reader.result), false);
|
||||
// $FlowFixMe
|
||||
reader.addEventListener('error', e => reject(e), false);
|
||||
reader.readAsDataURL(xhr.response);
|
||||
}
|
||||
} else {
|
||||
reject(
|
||||
__DEV__
|
||||
? `Failed to proxy image ${src.substring(
|
||||
0,
|
||||
256
|
||||
)} with status code ${xhr.status}`
|
||||
: ''
|
||||
);
|
||||
}
|
||||
} else {
|
||||
resolve(xhr.responseText);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = reject;
|
||||
xhr.open('GET', `${proxy}?url=${encodeURIComponent(src)}&responseType=${responseType}`);
|
||||
|
||||
if (responseType !== 'text' && xhr instanceof XMLHttpRequest) {
|
||||
xhr.responseType = responseType;
|
||||
}
|
||||
|
||||
if (options.imageTimeout) {
|
||||
const timeout = options.imageTimeout;
|
||||
xhr.timeout = timeout;
|
||||
xhr.ontimeout = () =>
|
||||
reject(__DEV__ ? `Timed out (${timeout}ms) proxying ${src.substring(0, 256)}` : '');
|
||||
}
|
||||
|
||||
xhr.send();
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user