html2canvas截图跨域问题

jQuery结合layui项目,当前页是一个iframeA,页面上嵌套了一个iframeB,点击iframeA页面的截图按钮,使用html2canvas实现iframeB的截图功能,A和B端口号不一样,报错Uncaught (in promise) DOMException:Failed to execute 'open' on 'Document':Can only call open() on same-origin decuments.

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
该报错是由于浏览器的同源策略导致的,即在一个域名下的网页无法直接访问另一个域名下的网页,因为它们属于不同的安全域,html2canvas截图时需要使用canvas元素,而canvas元素需要读取页面上的图片等资源,如果跨域了就会出现上述报错。

解决这个问题可以通过CORS(跨域资源共享)来实现,具体的做法是在B域名的服务器上添加一个响应头Access-Control-Allow-Origin,允许跨域访问A域名的页面。同时也可以通过JSONP等方式来解决跨域问题。

以下是使用CORS解决跨域的代码示例:

A页面的代码:

layui.use('layer',function(){
    var layer=layui.layer;  
    $('#btn-capture').click(function(){
        //获取iframeB的document对象
        var iframeDoc = document.getElementById('myiframe').contentWindow.document;
        //将iframeB的内容转成canvas并截图
        html2canvas(iframeDoc.body,{
            onrendered:function(canvas){
                var dataURL = canvas.toDataURL('image/png');
                //将截图转成图片并保存到本地
                var aLink = document.createElement("a");
                document.body.appendChild(aLink);
                aLink.href = dataURL;
                aLink.download = 'capture.png';
                aLink.click();
            }
        })
    });  
});

B页面的代码:

//在服务器端添加响应头Access-Control-Allow-Origin
response.setHeader('Access-Control-Allow-Origin', '*');

以上代码仅供参考,具体实现可能需要根据具体情况进行调整。
如果我的回答解决了您的问题,请采纳!

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答来自chatgpt:

    优化后的问题: 问题标题: 如何使用html2canvas解决跨域问题,并从嵌套的iframeA截取iframeB的内容? 问题内容: 我在使用html2canvas实现从嵌套的iframeA页面截取iframeB内容时,发现由于A和B的端口号不一致,出现了跨域问题。具体报错为Uncaught (in promise) DOMException:Failed to execute 'open' on 'Document':Can only call open() on same-origin documents. 我该如何解决这个问题?请提供具体的解决方法和代码示例,谢谢!

    回答:

    跨域问题是由于浏览器的同源策略导致的。由于iframeA和iframeB的端口号不一致,所以浏览器认为他们是不同源的,因此出现了跨域问题。要解决这个问题,我们可以使用html2canvas自带的proxyUrl选项或自定义代理服务器来解决跨域问题。以下是解决步骤和示例代码:

    1. 使用html2canvas自带的proxyUrl选项

    html2canvas提供了proxyUrl选项来解决跨域问题。该选项可以用来指定一个代理服务器的地址,html2canvas将通过该服务器获取要截取的内容。使用该选项的示例代码如下:

    html2canvas(document.querySelector("#iframeB"), {        
      proxyUrl: 'https://cors-anywhere.herokuapp.com/'        
    }).then(canvas => {
        document.body.appendChild(canvas);
    });
    

    在上面的示例代码中,我们使用了cors-anywhere代理服务器来获取iframeB的内容。注意,使用该代理服务器需要先进行注册并获取token,具体步骤详见https://github.com/Rob--W/cors-anywhere

    1. 自定义代理服务器

    除了使用html2canvas自带的proxyUrl选项外,我们还可以自定义代理服务器来解决跨域问题。我们可以在代理服务器上先请求iframeB的内容,然后将该内容返回给前端进行截取。以下是自定义代理服务器的示例代码:

    // 代理服务器代码
    const express = require('express');
    const request = require('request');
    const app = express();
    
    app.get('/proxy', (req, res) => {
        let url = req.query.url;
        if (url) {
            request(url, (err, response, body) => {
                if (err) {
                    console.error(err);
                    res.status(500).send('Error happened!');
                } else {
                    res.send(body);
                }
            });
        } else {
            res.status(400).send('Bad Request!');
        }
    });
    
    app.listen(3000, () => {
        console.log('Proxy server is listening on port 3000!');
    });
    

    在上面的示例代码中,我们使用express框架搭建了一个简单的代理服务器,监听3000端口。当我们访问http://localhost:3000/proxy?url=http://localhost:8080/iframeB时,代理服务器将会向http://localhost:8080/iframeB发送请求并将响应返回给前端。

    以下是在前端代码中使用自定义代理服务器的示例代码:

    html2canvas(document.querySelector("#iframeB"), {
        proxy: "http://localhost:3000/proxy?url=" + encodeURIComponent("http://localhost:8080/iframeB")
    }).then(canvas => {
        document.body.appendChild(canvas);
    });
    

    在上面的示例代码中,我们将自定义代理服务器的地址作为proxy选项传给了html2canvas,并在代理地址后面拼接了要请求的iframeB地址。注意,为了防止代理地址中的特殊字符对url造成影响,我们需要使用encodeURIComponent函数对iframeB地址进行编码。

    以上就是使用html2canvas解决跨域问题,并从嵌套的iframeA截取iframeB内容的方法和代码示例。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

引用chatGPT作答,这个错误提示是由于浏览器的同源策略所导致的。同源策略要求两个网页的协议、主机名和端口号都必须相同,否则就会出现跨域问题。

在你的情况下,iframeA和iframeB所在的页面的端口号不一样,因此html2canvas无法访问iframeB中的内容。为了解决这个问题,你需要在iframeA中嵌入iframeB时,将iframeB的源设置为和iframeA相同的域名和端口号,这样就可以保证它们处于同一个域中。

例如,假设iframeA所在的页面的URL为http://localhost:8080/index.html,而iframeB所在的页面的URL为http://localhost:8081/index.html。为了解决跨域问题,你可以在iframeA中嵌入iframeB时,使用以下代码:

<iframe src="http://localhost:8080/iframeB.html" id="iframeB"></iframe>

然后,在iframeA的JavaScript代码中,你可以使用以下代码来截取iframeB的内容:

html2canvas(document.getElementById("iframeB").contentDocument.body).then(function(canvas) {
    // 处理截图结果
});

这样,就可以避免跨域问题,并成功地截取iframeB的内容了。