一个关于image访问图片跨域的问题

1、背景

项目中遇到一个问题,同一个图片在 dom 节点中使用了 'img' 标签来加载,同时因为项目使用了 ThreeJS 3D 渲染引擎,在加载纹理时使用了 TextureLoader 来加载了同一张图片,而因为图片是在阿里云服务器上的,因此最后报出了以下错误,意思是在访问图片时出现了跨域问题: html

image 跨域错误

2、问题梳理

2.1 关于图片的加载

图片是来自于阿里云服务器的,和本地 localhost 必然存在跨域问题。经过 dom 节点的 'img' 标签来直接访问是没有问题,由于浏览器自己不会有跨域问题。问题出在经过 TextureLoader 来加载图片时出现了跨域问题。查看了 TextureLoader 的源码,发现其进一步使用了 ImageLoader 来加载图片,加载图片的代码大体以下:canvas

crossOrigin: 'anonymous',
......
var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' );
......
if ( url.substr( 0, 5 ) !== 'data:' ) {
	if ( this.crossOrigin !== undefined ) 
	    image.crossOrigin = this.crossOrigin;
}
......
image.src = url;
复制代码

这段代码所描述的大体思路是:跨域

  1. 经过JS代码,建立一个 img 的 dom element,而后使用这个 element 来加载图片。
  2. 默认状况下,设置了 crossOrigin 的跨域属性为 'anonymous'。

因此,问题的关键在于,同一张图片,先用 'img' 标签去加载了,而后再在 JS 代码中,建立一个 'img' 而且设置了 crossOrigin 的跨域属性为 'anonymous',那么在 JS 中建立的 'img' 就会出现访问图片而产生跨域的问题。浏览器

2.2 关于 crossOrigin

关于 crossOrigin,咱们看看 MDN 的解释。缓存

crossOrigin

这段话,用我本身的理解来解释一下:bash

  1. 加了 crossorigin 属性,则代表图片就必定会按照 CORS 来请求图片。而经过CORS 请求到的图片能够再次被复用到 canvas 上进行绘制。换言之,若是不加 crossorigin 属性的话,那么图片是不能再次被复用到 canvas 上去的。
  2. 能够设置的值有 anonymous 以及 use-credentials,2 个 value 的做用都是设置经过 CORS 来请求图片,区别在于 use-credentials 是加了证书的 CORS。
  3. 若是默认用户不进行任何设置,那么就不会发起 CORS 请求。但若是设置了除 anonymous 和 use-credentials 之外的其余值,包括空字串在内,默认会看成 anonymous来处理。

2.3 问题总结

经过前面 2 点的梳理,咱们得出以下结论:服务器

  1. 经过 'img' 加载的图片,浏览器默认状况下会将其缓存起来。
  2. 当咱们从 JS 的代码中建立的 'img' 再去访问同一个图片时,浏览器就不会再发起新的请求,而是直接访问缓存的图片。可是因为 JS 中的 'img' 设置了 crossorigin,也就意味着它将要以 CORS 的方式请求,但缓存中的图片显然不是的,因此浏览器直接就拒绝了。连网络请求都没有发起。
  3. 在 Chrome 的调试器中,在 network 面板中,咱们勾选了 disable cache 选项,验证了问题确实如第 2 点所述,浏览器这时发起了请求而且 JS 的 'img' 也能正常请求到图片。

3、解决问题

前面经过勾选 disable cache 来避免浏览器使用缓存图片而解决了问题,但实际用户不会这样使用啊。根据前面的梳理,'img' 不跨域请求,而 JS 中的 'img' 跨域请求,因此不能访问缓存,那么是否是能够将 JS 中的 'img' 也设置成不跨域呢,因而将 JS 中的 'img' 的 crossorigin 设置为 undefine,结果图片是能够加载了,但又获得以下错误。网络

image.png

这段错误的意思是,这一个来自于CORS 的图片,是不能够再次被复用到 canvas 上去的。这就验证了关于 crossorigin 中的第 1 点。dom

既然 'img' 和 JS 中的 'img' 都不加 crossorigin不能解决 canvas 重用的问题,那么在两边同时都加上 crossorigin 呢?果真,在 'img' 中和 JS 中的 'img' 都加上 crossorigin = "anonymous",图片能够正常加了,同时也能够被复用到 'canvas' 上去了。this

另外,须要注意的 2 个小问题是:

  1. 服务器必须加上字段,不然,客户端设置了也是没用的。

Access-Control-Allow-Origin: *

  1. 若是是已经出了问题,你才看到这篇文章,或者才去想到这么解决。那么要记得先清理一下游览器所缓存的图片。不然你就会发现,有的图片能够访问,而有的不能够。那是由于缓存中以前存储了未 CORS 的图片。

4、总结

前面说了一框,只是想把这个过程完整的记录下来。整个问题的总结是:

  1. 同一张图片或者同一个地址,同时被 'img' 所访问,而随后后又会被如 JS 中去访问。而图片存储的地址是跨域的,那么就可能由于缓存问题而致使 JS 中的访问出现跨域问题。
  2. 解决的办法是让 'img' 标签和 JS 中的访问都走跨域访问的方式,这样既能够解决跨域访问的问题,也能够解决跨域图片在 canvas 中的复用。

最后,感谢你能读到并读完此文章,若是分析的过程当中存在错误或者疑问都欢迎留言讨论。若是个人分享可以帮助到你,还请记得帮忙点个赞吧,谢谢。

相关文章
相关标签/搜索