fabric.js里添加图片因为跨域问题无法正常渲染和canvas污染无法保存问题
问题
会报两种错误,要么保存新图片时报错,要么插入图片渲染时报错:
图片插入到画布成功了,画布保存新图片时报错。
图片无法插入到画布,直接提示CORS报错。
我在网上查询了很多资料,有说fabric.js没有配置跨域,后端没有配置跨域,浏览器缓存原因等等,这些都遗漏了一个比较常见根本的因素,往下继续看。
前提检查
前提是我们需要使用img标签的crossorigin="anonymous"来测试前后端是否产生跨域问题,如果后端服务器没有正确配置图片资源的跨域问题,是无法显示图片的,会报跨域的错误。
在浏览器控制台中我们能看到没有配置跨域的图片响应头是这样的:
在响应头中是并没有看到Access-Control-Allow-Origin: *;那么这个请求就会被浏览器规则所拦截无法正常渲染。
解决方法
有人疑惑说我后端明明在头部加了header(Access-Control-Allow-Origin: *)呀,为什么还是无法渲染,关键的问题是在nginx上,针对http:xxx/static/1.png这种图片链接,我们需要在nginx的location /static/ { … }中一定要加入add_header跨域配置!!!
server{
listen 80;
server_name xxx.xxx.xxx.xxx;
index index.html index.htm index.php;
root /www/wwwroot/xxx.xxx.xxx.xxx/frontEnd/build;
include enable-php.conf;
access_log /www/wwwlogs/access.log;
location /static/ {
alias /www/wwwroot/xxx.xxx.xxx.xxx/static/; #静态资源路径
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'DELETE,PATCH,PUT,VIEW,GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'ACCEPT,Accept-Encoding,AUTHORIZATION,ORIGIN,Access-Token,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
再次访问图片资源,会发现响应头中添加了Access-Control-Allow-Origin,图片在img标签下也能正常渲染!
我是django项目部署到堡塔服务器的,项目中的settings.py也是需要配置跨域请求,具体看其他人配置的就行:
通过 django-cors-headers 库来实现跨域
如果是其他语言的,如PHP项目的,可以看看除了添加header(Access-Control-Allow-Origin: *)外,还记得针对图片文件http:xxx/static/1.png的路径location /static/添加跨域!
通过fabric.js渲染图片加入canvas和保存图片:
1.添加图片进入canvas:
canvas = new fabric.Canvas("canvas", {
width: "400",
height: "400",
backgroundColor: "#f5deb3",
});
// 1. 通过fabric.Image.fromURL插入图片
function addImgByUrl(url) {
fabric.Image.fromURL(
url,
(imgEl) => {
imgEl.set({
left: 100,
top: 100,
});
// 设置缩放
canvas.add(imgEl);
canvas.setActiveObject(imgEl);
canvas.renderAll();
},
{ crossOrigin: 'anonymous' }
);
}
// 2. fabric.util.loadImage
function addImgByUtils(url) {
fabric.util.loadImage(url, function(catImg) {
const mycatImg = new fabric.Image(catImg)
mycatImg.set({ left: 180, top: 180, });
canvas.add(mycatImg);
canvas.setActiveObject(mycatImg);
canvas.renderAll();
}, {crossOrigin: 'Anonymous'});
}
// 3. new Image() 与 document.createElement('img')是等价的方式
function addImgByNewImage(url) {
const image = new Image()
image.setAttribute('crossOrigin', 'anonymous')
image.onload = function () {
fabricImage = new fabric.Image(image)
fabricImage.set({ left: 150, top: 150, });
canvas.add(fabricImage);
canvas.setActiveObject(fabricImage);
canvas.renderAll();
}
image.src = url
}
function addImgByCreateTag(url) {
const tagImg = document.createElement('img')
tagImg.crossOrigin = 'anonymous'
tagImg.src = url
// 等待图片加载完
tagImg.onload = function() {
const myTagImg = new fabric.Image(tagImg)
myTagImg.set({ left: 190, top: 190, });
canvas.add(myTagImg);
canvas.setActiveObject(myTagImg);
canvas.renderAll();
}
}
// 4.<img />
// <img id="imgTag" src="http://127.0.0.1:8081/1.png" crossorigin="anonymous" alt="">
function addImgByTag() {
const catImg = document.getElementById('imgTag')
// 等待图片加载完
catImg.onload = function() {
const mycatImg = new fabric.Image(catImg)
mycatImg.set({ left: 190, top: 190, });
canvas.add(mycatImg);
canvas.setActiveObject(mycatImg);
canvas.renderAll();
}
}
为了防止浏览器缓存,可使用**var Url = url+‘?time=’ + new Date().valueOf()**来避免缓存!
2.保存图片或PDF:
function canvasToImage() {
// 保存图片
var canvas = document.getElementById("canvas");
const MIME_TYPE = "image/png";
const imgURL = canvas.toDataURL(MIME_TYPE);
// 创建一个隐藏的 a 标签
const dlLink = document.createElement("a");
const filename = "个人画板_" + new Date().getTime() + ".png";
// 设置下载文件名和链接的 href 属性
dlLink.download = filename;
dlLink.href = imgURL;
// 触发下载
document.body.appendChild(dlLink);
dlLink.click();
document.body.removeChild(dlLink);
// 保存ptf
//import html2Canvas from "html2canvas";
//import JsPDF from "jspdf";
// var title = "单页报告";
// var dom = document.getElementById("canvas"); // 生成pdf的html内容
// html2Canvas(dom, {
// allowTaint: true,
// scrollY: 0,
// scrollX: 0,
// useCORS: true, // 开启跨院
// // width: 1000, // 宽度
// height: dom.offsetHeight,
// }).then(function (canvas) {
// var contentWidth = canvas.width;
// var contentHeight = canvas.height;
// var pdfWidth = ((contentWidth + 10) / 2) * 0.75;
// var pdfHeight = ((contentHeight + 200) / 2) * 0.75; // 500为底部留白
// var imgWidth = pdfWidth;
// var imgHeight = (contentHeight / 2) * 0.75; //内容图片这里不需要留白的距离
// var pageData = canvas.toDataURL("image/jpeg", 1.0);
// var pdf = new JsPDF("", "pt", [pdfWidth, pdfHeight]);
// pdf.addImage(pageData, "jpeg", 0, 0, imgWidth, imgHeight);
// pdf.addPage();
// // pdf.save(title + ".pdf");
// let blob = new Blob([pdf.output("blob")], { type: "application/pdf" });
// let name = "pdfFile";
// let file = new File([blob], `${name}.pdf`, { type: blob.type });
// pdf.save(file);
// });
}
跨域的设置
跨域区分前端跨域与后端跨域设置,得出如下结果:
都不设置跨域:渲染正常,保存不正常。
仅前端设置跨域:渲染不正常。
仅后端设置跨域:渲染正常,保存不正常。
前后端均设置跨域:渲染正常,保存正常。
多看看请求响应头中是否带有Access-Control-Allow-Origin,带有则表明后端开启跨域,前端img标签中设置crossorigin="anonymous“或js中设置 {crossOrigin: ‘Anonymous’}这种,说明前端开启了跨域,不然可打印img标签出来,看是否带有。