url图片显示并不会跨域,
只有要通过xhr获取其blob对象才会提示跨域
这就是为何通过
html2canvas(dom,{
allowTaint: true //允许污染画布
}).then(canvas=>{
document.body.appendChild(canvas);
})
以上的canvas是可以显示出来的,但仅仅是可以显示,如果dom中存在跨域url的图片,那么生成的canvas是被污染的,无法执行canvas.toDataURL();
html2canvas(dom,{
allowTaint: true //允许污染画布
}).then(canvas=>{
document.body.appendChild(canvas);
canvas.toDataURL(); //提示报错无法导出收到污染的画布
})
此时,如果图片所在的服务器设置了cors请求头,即Access-Control-Allow-Origin=*或者你页面所在的域名
那么加上useCORS: true 允许跨域即可,源码中会把img加上crossOrigin="anonymous"属性从而去申请跨域请求头(最低支持到IE10)
html2canvas(dom,{
allowTaint: true, //允许污染画布
useCORS: true
}).then(canvas=>{
document.body.appendChild(canvas);
canvas.toDataURL(); //提示报错无法导出收到污染的画布
})
最后,如果图片所在服务器没有提供cors请求头,那么只能通过搭建服务器转发请求的方式,常用的有nginx配置代理,当然java,.net也都有实现
例如源图片地址来自http://ip1:port1/img/test.png
nginx代理后的地址为http://ip2:port2/img/test.png,同时代理配置了Access-Control-Allow-Origin=*
此时html2canvas需要稍作修改才能实现以上的代理请求访问
html2canvas(dom,{
allowTaint: true, //允许污染画布
//useCORS: true, //使用代理需要设置为false或者不设置
proxy:"http://ip2:port2" //nginx转发后的ip和端口
}).then(canvas=>{
document.body.appendChild(canvas);
canvas.toDataURL(); //提示报错无法导出收到污染的画布
})
以上实际会请求这么个地址
http://ip2:port2?url=http://ip1:port1/img/test.png&responseType=blob
显然,我们的nginx代理配置的不是这样的,
此时,你可以配置nginx用正则的方式去匹配以上的格式的地址,
这儿给出了修改html2canvas的实现
这儿是html2canvas源码中proxy的实现
Cache.prototype.proxy = function (src) {
var _this = this;
var proxy = this._options.proxy;
if (!proxy) {
throw new Error('No proxy defined');
}
var key = src.substring(0, 256);
return new Promise(function (resolve, reject) {
var responseType = FEATURES.SUPPORT_RESPONSE_TYPE ? 'blob' : 'text';
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status === 200) {
if (responseType === 'text') {
resolve(xhr.response);
}
else {
var reader_1 = new FileReader();
reader_1.addEventListener('load', function () { return resolve(reader_1.result); }, false);
reader_1.addEventListener('error', function (e) { return reject(e); }, false);
reader_1.readAsDataURL(xhr.response);
}
}
else {
reject("Failed to proxy resource " + key + " with status code " + xhr.status);
}
};
xhr.onerror = reject;
xhr.open('GET', proxy + "?url=" + encodeURIComponent(src) + "&responseType=" + responseType);
if (responseType !== 'text' && xhr instanceof XMLHttpRequest) {
xhr.responseType = responseType;
}
if (_this._options.imageTimeout) {
var timeout_1 = _this._options.imageTimeout;
xhr.timeout = timeout_1;
xhr.ontimeout = function () { return reject("Timed out (" + timeout_1 + "ms) proxying " + key); };
}
xhr.send();
});
};
我们可以同样的方式在这个方法下新增一个ipProxy表示只替换ip的方式代理
Cache.prototype.ipProxy = function (src) {
var _this = this;
var proxy = this._options.ipProxy;
if (!proxy) {
throw new Error('No proxy defined');
}
if(proxy.substr(-1,1)!="/"){
proxy=proxy+"/";
}
var key = src.substring(0, 256);
return new Promise(function (resolve, reject) {
var responseType = FEATURES.SUPPORT_RESPONSE_TYPE ? 'blob' : 'text';
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status === 200) {
if (responseType === 'text') {
resolve(xhr.response);
}
else {
var reader_1 = new FileReader();
reader_1.addEventListener('load', function () { return resolve(reader_1.result); }, false);
reader_1.addEventListener('error', function (e) { return reject(e); }, false);
reader_1.readAsDataURL(xhr.response);
}
}
else {
reject("Failed to proxy resource " + key + " with status code " + xhr.status);
}
};
xhr.onerror = reject;
var _src = src.substr(src.indexOf(':')+(src.substr(0,4)==='http'?3:0));
xhr.open("GET",proxy+_src.substr(_src.indexOf('/')+1));
//xhr.open('GET', proxy + "?url=" + encodeURIComponent(src) + "&responseType=" + responseType);
if (responseType !== 'text' && xhr instanceof XMLHttpRequest) {
xhr.responseType = responseType;
}
if (_this._options.imageTimeout) {
var timeout_1 = _this._options.imageTimeout;
xhr.timeout = timeout_1;
xhr.ontimeout = function () { return reject("Timed out (" + timeout_1 + "ms) proxying " + key); };
}
xhr.send();
});
};
同时,新增一个option.ipProxy的解析
Cache.prototype.loadImage = function (key) {
return __awaiter(this, void 0, void 0, function () {
var isSameOrigin, useCORS, useProxy, src,useIPProxy; //新增变量useIPProxy
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
isSameOrigin = CacheStorage.isSameOrigin(key);
useCORS = !isInlineImage(key) && this._options.useCORS === true && FEATURES.SUPPORT_CORS_IMAGES && !isSameOrigin;
useProxy = !isInlineImage(key) &&
!isSameOrigin &&
typeof this._options.proxy === 'string' &&
FEATURES.SUPPORT_CORS_XHR &&
!useCORS;
//对useIPProxy 赋值
useIPProxy = !isInlineImage(key) &&
!isSameOrigin &&
typeof this._options.ipProxy === 'string' &&
FEATURES.SUPPORT_CORS_XHR &&
!useCORS;
if (!isSameOrigin && this._options.allowTaint === false && !isInlineImage(key) && !useProxy && !useCORS) {
return [2 /*return*/];
}
src = key;
//如果是需要ipproxy的提前返回并调用ipProxy方法
if (useIPProxy) return [4 /*yield*/, this.ipProxy(src)];
if (!useProxy) return [3 /*break*/, 2];
return [4 /*yield*/, this.proxy(src)];
case 1:
src = _a.sent();
_a.label = 2;
case 2:
Logger.getInstance(this.id).debug("Added image " + key.substring(0, 256));
return [4 /*yield*/, new Promise(function (resolve, reject) {
var img = new Image();
img.crossOrigin = '';
img.onload = function () { return resolve(img); };
img.onerror = reject;
//ios safari 10.3 taints canvas with data urls unless crossOrigin is set to anonymous
if (isInlineBase64Image(src) || useCORS) {
img.crossOrigin = 'anonymous';
}
img.src = src;
if (img.complete === true) {
// Inline XML images may fail to parse, throwing an Error later on
setTimeout(function () { return resolve(img); }, 500);
}
if (_this._options.imageTimeout > 0) {
setTimeout(function () { return reject("Timed out (" + _this._options.imageTimeout + "ms) loading image"); }, _this._options.imageTimeout);
}
})];
case 3: return [2 /*return*/, _a.sent()];
}
});
});
};
修改后的html2canvas就可以实现ip代理而不是拼接url代理了
此时你可以发现访问的不再是
http://ip2:port2?url=http://ip1:port1/img/test.png&responseType=blob
而是
http://ip2:port2/img/test.png