一個關於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 中的複用。

最後,感謝你能讀到並讀完此文章,若是分析的過程當中存在錯誤或者疑問都歡迎留言討論。若是個人分享可以幫助到你,還請記得幫忙點個贊吧,謝謝。

相關文章
相關標籤/搜索