html2canvas 跨域问题的全方位解读

2021年4月26

加上allowTaint: true和useCORS: true依然显示空白

        html2canvas(document.querySelector(".poster"), {
            allowTaint: true,
            useCORS: true /*使用跨域*/
        }).then(canvas => {
            let imageTofile = document.getElementById("")
            $('body').append(canvas)
            var url = canvas.toDataURL('image/jpg');
            // console.log(url);
        });

问题:
截图区域的图品是服务器上的图片导致执行后因为跨域图片显示空白,上网百度说加上allowTaint: true和useCORS: true,但加上有依然显示空白,还有说先把图片转成base64再截图,尝试后依然报跨域无法显示图片
后来求助一位大佬后发现 将我从官网下的新版本换成“旧版本”的html2Canvas.js 后再设置了“allowTaint: true和useCORS: true”后问题尽然奇迹般地解决了(旧版本的JS:链接: https://pan.baidu.com/s/1LudA1rsdUPaqut0fj9niRA 提取码: u9p4),图片可以显示了
奇葩~~

此时将截图toDataURL时又报错

真的是一坑解决又入一坑,坑坑不断,在将canvas转url的时候又报错“Failed to execute ‘toDataURL’ on ‘HTMLCanvasElement’: Tainted canvases may not be exported.”
百度一天一无所获,各种复制黏贴的回答,让人头疼

转天偶然发现一篇文章完美解释 文章地址:https://www.jianshu.com/p/22bd5b98e38a

向大佬致敬

详情请点链接查看原文章

原因跟html2canvas库的工作原理有很大的关系。如前文所说,html2canvas库需要我们先提供一段DOM节点,然后它再读取并解析这一段DOM节点生成canvas对象。如果DOM节点中已经使用了标签的话,它也会解析这个标签的src属性,然后重新创建一个Image对象,给它添加crossOrigin="anonymous"属性后尝试以跨域的方式重新读取图片数据。需要注意的是,一般CDN上的图片都是带有缓存响应头并且会在浏览器端缓存的,而且缓存的不仅仅是图片数据,还有HTTP响应头。所以问题的根本原因我们就找到了,当html2canvas尝试以跨域的方式去读取图片数据时,它读取到的是浏览器的缓存数据,而且因为我们没有给DOM节点中的标签添加crossorigin="anonymous"属性,所以缓存数据是不带Access-Control-Allow-Origin响应头的,进而导致html2canvas库读取到的图片数据污染了生成的canvas对象,最终致使canvas导出数据报错。

看到这里已经真相大白了。所以我们要做的事情也很简单,就是给DOM节点中的每一个标签都加上crossorigin="anonymous"属性就可以了。再回过头说一下为什么之前的那个国人的文章虽然解决了问题但是却并没有找到问题的根本原因,因为他修改了html2canvas读取图片的源代码,给每一个Image的src属性添加了一个随机字符串,意外地避开了读取到缓存数据的问题,但是却会导致CDN的缓存被击穿。

最后总结一下,其实说了一大堆,我们要做的事情却很简单:

1、添加useCORS:true属性;
2、给要生成canvas的DOM中包含的每一个 img 标签添加crossorigin="anonymous"属性;
3、确保你的图片CDN服务器支持CORS访问,也就是会返回Access-Control-Allow-Origin等响应头;

在 SpringBoot 和 Vue 中实现表之间的关联需要以下步骤: 1. 定义实体类,并建立关联关系。 例如,假设有两个表:用户表和订单表,一个用户可以有多个订单,那么可以在用户表中定义一个 List<Order> orders 属性,并在订单表中定义一个 User user 属性,表示订单属于哪个用户。 2. 使用 JPA 在数据库中创建表,并建立外键关系。 在实体类中定义好关联关系后,可以使用 JPA 自动生成表和外键关系。在 SpringBoot 中,可以使用 @Entity 和 @OneToMany/@ManyToOne 注解来定义实体类和关联关系。例如: ```java @Entity public class User { @Id private Long id; private String name; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List<Order> orders; // getter and setter } @Entity public class Order { @Id private Long id; private String name; @ManyToOne @JoinColumn(name = "user_id") private User user; // getter and setter } ``` 3. 在后端编写 API。 在 SpringBoot 中,可以使用 @RestController 和 @RequestMapping 注解来定义 API。例如,可以定义一个 UserController 和一个 OrderController,分别处理用户和订单的请求。在 UserController 中,可以定义一个方法来获取某个用户的所有订单: ```java @RestController @RequestMapping("/users") public class UserController { @Autowired private UserRepository userRepository; @Autowired private OrderRepository orderRepository; @GetMapping("/{id}/orders") public List<Order> getOrdersByUserId(@PathVariable Long id) { User user = userRepository.findById(id).orElse(null); if (user != null) { return orderRepository.findByUser(user); } return null; } } ``` 在 OrderController 中,可以定义一个方法来获取某个订单所属的用户: ```java @RestController @RequestMapping("/orders") public class OrderController { @Autowired private OrderRepository orderRepository; @GetMapping("/{id}/user") public User getUserByOrderId(@PathVariable Long id) { Order order = orderRepository.findById(id).orElse(null); if (order != null) { return order.getUser(); } return null; } } ``` 4. 在前端编写页面。 在 Vue 中,可以使用 axios 发送请求,获取后台数据并展示在页面上。例如,可以在用户详情页中展示该用户的所有订单: ```html <template> <div> <h1>{{ user.name }} 的订单</h1> <ul> <li v-for="order in orders" :key="order.id"> {{ order.name }} </li> </ul> </div> </template> <script> import axios from 'axios' export default { data() { return { user: {}, orders: [] } }, mounted() { const userId = this.$route.params.id axios.get(`/users/${userId}/orders`).then(response => { this.orders = response.data }) } } </script> ``` 在订单详情页中展示该订单所属的用户: ```html <template> <div> <h1>{{ order.name }} 的用户</h1> <p>{{ user.name }}</p> </div> </template> <script> import axios from 'axios' export default { data() { return { order: {}, user: {} } }, mounted() { const orderId = this.$route.params.id axios.get(`/orders/${orderId}/user`).then(response => { this.user = response.data }) } } </script> ``` 以上就是在 SpringBoot 和 Vue 中实现表之间的关联的步骤。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值