一蒸甜、背景
項目中遇到一個問題,同一個圖片在 dom 節(jié)點中使用了 <img> 標簽來加載余佛,同時由于項目使用了 ThreeJS 3D 渲染引擎柠新,在加載紋理時使用了 TextureLoader 來加載了同一張圖片,而由于圖片是在阿里云服務(wù)器上的辉巡,所以最后報出了如下錯誤恨憎,意思是在訪問圖片時出現(xiàn)了跨域問題:
二、問題梳理
2.1 關(guān)于圖片的加載
圖片是來自于阿里云服務(wù)器的郊楣,和本地 localhost 必然存在跨域問題憔恳。通過 dom 節(jié)點的 <img> 標簽來直接訪問是沒有問題,因為瀏覽器本身不會有跨域問題净蚤。問題出在通過 TextureLoader 來加載圖片時出現(xiàn)了跨域問題钥组。查看了 TextureLoader 的源碼,發(fā)現(xiàn)其進一步使用了 ImageLoader 來加載圖片今瀑,加載圖片的代碼大致如下:
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;
這段代碼所描述的大致思路是:
- 通過JS代碼程梦,創(chuàng)建一個 img 的 dom element,然后使用這個 element 來加載圖片橘荠。
- 默認情況下屿附,設(shè)置了 crossOrigin 的跨域?qū)傩詾?'anonymous'。
所以哥童,問題的關(guān)鍵在于挺份,同一張圖片,先用 <img> 標簽去加載了贮懈,然后再在 JS 代碼中匀泊,創(chuàng)建一個 <img> 并且設(shè)置了 crossOrigin 的跨域?qū)傩詾?'anonymous'影暴,那么在 JS 中創(chuàng)建的 <img> 就會出現(xiàn)訪問圖片而產(chǎn)生跨域的問題。
2.2 關(guān)于 crossOrigin
關(guān)于 crossOrigin探赫,我們看看 MDN 的解釋型宙。
這段話,用我自己的理解來解釋一下:
- 加了 crossorigin 屬性伦吠,則表明圖片就一定會按照 CORS 來請求圖片妆兑。而通過CORS 請求到的圖片可以再次被復(fù)用到 canvas 上進行繪制。換言之毛仪,如果不加 crossorigin 屬性的話搁嗓,那么圖片是不能再次被復(fù)用到 canvas 上去的。
- 可以設(shè)置的值有 anonymous 以及 use-credentials箱靴,2 個 value 的作用都是設(shè)置通過 CORS 來請求圖片腺逛,區(qū)別在于 use-credentials 是加了證書的 CORS。
- 如果默認用戶不進行任何設(shè)置衡怀,那么就不會發(fā)起 CORS 請求棍矛。但如果設(shè)置了除 anonymous 和 use-credentials 以外的其他值,包括空字串在內(nèi)抛杨,默認會當作 anonymous來處理够委。
2.3 問題總結(jié)
通過前面 2 點的梳理,我們得出如下結(jié)論:
- 通過 <img> 加載的圖片怖现,瀏覽器默認情況下會將其緩存起來茁帽。
- 當我們從 JS 的代碼中創(chuàng)建的 <img> 再去訪問同一個圖片時,瀏覽器就不會再發(fā)起新的請求屈嗤,而是直接訪問緩存的圖片潘拨。但是由于 JS 中的 <img> 設(shè)置了 crossorigin,也就意味著它將要以 CORS 的方式請求饶号,但緩存中的圖片顯然不是的铁追,所以瀏覽器直接就拒絕了。連網(wǎng)絡(luò)請求都沒有發(fā)起讨韭。
- 在 Chrome 的調(diào)試器中脂信,在 network 面板中癣蟋,我們勾選了 disable cache 選項透硝,驗證了問題確實如第 2 點所述,瀏覽器這時發(fā)起了請求并且 JS 的 <img> 也能正常請求到圖片疯搅。
三濒生、解決問題
前面通過勾選 disable cache 來避免瀏覽器使用緩存圖片而解決了問題,但實際用戶不會這樣使用啊幔欧。根據(jù)前面的梳理罪治,<img> 不跨域請求丽声,而 JS 中的 <img> 跨域請求,所以不能訪問緩存觉义,那么是不是可以將 JS 中的 <img> 也設(shè)置成不跨域呢雁社,于是將 JS 中的 <img> 的 crossorigin 設(shè)置為 undefine,結(jié)果圖片是可以加載了晒骇,但又得到如下錯誤霉撵。
這段錯誤的意思是,這一個來自于CORS 的圖片洪囤,是不可以再次被復(fù)用到 canvas 上去的徒坡。這就驗證了關(guān)于 crossorigin 中的第 1 點。
既然 <img> 和 JS 中的 <img> 都不加 crossorigin不能解決 canvas 重用的問題瘤缩,那么在兩邊同時都加上 crossorigin 呢喇完?果然,在 <img> 中和 JS 中的 <img> 都加上 crossorigin = "anonymous"剥啤,圖片可以正常加了锦溪,同時也可以被復(fù)用到 <canvas> 上去了。
另外府怯,需要注意的 2 個小問題是:
- 服務(wù)器必須加上字段海洼,否則,客戶端設(shè)置了也是沒用的富腊。
Access-Control-Allow-Origin: *
- 如果是已經(jīng)出了問題坏逢,你才看到這篇文章,或者才去想到這么解決赘被。那么要記得先清理一下游覽器所緩存的圖片是整。否則你就會發(fā)現(xiàn),有的圖片可以訪問民假,而有的不可以浮入。那是因為緩存中之前存儲了未 CORS 的圖片。
四羊异、總結(jié)
前面說了一框事秀,只是想把這個過程完整的記錄下來。整個問題的總結(jié)是:
- 同一張圖片或者同一個地址野舶,同時被 <img> 所訪問易迹,而隨后后又會被如 JS 中去訪問。而圖片存儲的地址是跨域的平道,那么就可能因為緩存問題而導(dǎo)致 JS 中的訪問出現(xiàn)跨域問題睹欲。
- 解決的辦法是讓 <img> 標簽和 JS 中的訪問都走跨域訪問的方式,這樣既可以解決跨域訪問的問題,也可以解決跨域圖片在 canvas 中的復(fù)用窘疮。
最后袋哼,感謝你能讀到并讀完此文章,如果分析的過程中存在錯誤或者疑問都歡迎留言討論闸衫。如果我的分享能夠幫助到你涛贯,還請記得幫忙點個贊吧,謝謝蔚出。