一般團隊都會有一個專門網域放置靜態資源,例如騰訊是gtimg.com,百度是bdimg.com;或是很多團隊使用的是騰訊雲或是阿里雲的服務。
而主頁所在網域名稱往往不一樣,當需要對canvas圖片進行getImageData()或toDataURL()操作的時候,跨域問題就出來了,而且跨域問題還不只一層。
首先,第一步,圖片伺服器需要配置Access-Control-Allow-Origin訊息,例如:
如PHP添加回應頭訊息,*通配符表示允許任意網域名稱:
header(Access-Control-Allow-Origin: *);
或指定網域名稱:
header(Access-Control-Allow-Origin: www.zhangxinxu.com);
此時,Chrome瀏覽器就不會有Access-Control-Allow-Origin相關的錯誤訊息了,但是,還會有其他的跨域錯誤訊息。
二、canvas圖片getImageData cross-origin跨域問題對於跨域的圖片,只要能夠在網頁中正常顯示出來,就可以使用canvas的drawImage() API繪製出來。但是如果你想更進一步,透過getImageData()方法獲取圖片的完整的像素信息,則多半會出錯。
舉例來說,使用下面程式碼來取得github上的自己頭像圖片資訊:
var canvas = document.createElement('canvas');var context = canvas.getContext('2d');var img = new Image();img.onload = function () { context.drawImage(this, 0, 0) ; context.getImageData(0, 0, this.width, this.height);};img.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';
結果在Chrome瀏覽器下顯示如下錯誤:
Uncaught DOMException: Failed to execute 'getImageData ' on 'CanvasRenderingContext2D ': The canvas has been tainted by cross-origin data.
Firefox瀏覽器錯誤為:
SecurityError: The operation is insecure.
如果使用的是canvas.toDataURL()方法,則會報:
Failed to execute 'toDataURL ' on 'HTMLCanvasElement ': Tainted canvased may not be exported
原因其實都是一樣的,跨域導致。
那有沒有辦法可以解決這個問題呢?
可以試試crossOrigin屬性。
三、HTML crossOrigin屬性解決資源跨域問題在HTML5中,有些元素提供了支援CORS(Cross-Origin Resource Sharing)(跨域資源共享)的屬性,這些元素包括<img>,<video>,<script>等,而提供的屬性名稱就是crossOrigin屬性。
因此,上面的跨域問題可以這麼處理:
var canvas = document.createElement('canvas');var context = canvas.getContext('2d');var img = new Image();img.crossOrigin = '';img.onload = function () { context.drawImage (this, 0, 0); context.getImageData(0, 0, this.width, this.height);};img.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';
增加一個img.crossOrigin = ''即可,雖然JS程式碼這裡設定的是空字串,實際上起作用的屬性值是anonymous。
crossOrigin可以有下面兩個值:
關鍵字 | 釋義 |
---|---|
anonymous | 元素的跨域資源請求不需要憑證標誌設定。 |
use-credentials | 元素的跨域資源請求需要憑證標誌設置,這意味著該請求需要提供憑證。 |
其中,只要crossOrigin的屬性值不是use-credentials,全部都會解析為anonymous,包括空字串,包括類似'abc'這樣的字元。
例如:
img.crossOrigin = 'abc';console.log(img.crossOrigin); // 結果是'anonymous'
另外還有一點要注意,那就是雖然沒有crossOrigin屬性,和設定crossOrigin=use-credentials在預設情況下都會報跨域出錯,但是性質上卻不一樣,兩者有較大區別。
crossOrigin相容性IE11+(IE Edge),Safari,Chrome,Firefox瀏覽器皆支持,IE9和IE10會報SecurityError安全錯誤,如下截圖:
四、crossOrigin屬性為什麼可以解決資源跨域問題?crossOrigin=anonymous相對於告訴對方伺服器,你不需要帶任何非匿名資訊過來。例如cookie,因此,目前瀏覽器肯定是安全的。
就好比你要去別人家裡拿一件衣服,crossOrigin=anonymous相對於告訴對方,我只要衣服,其他都不要。如果不說,可能對方在衣服裡放竊聽器什麼的,就不安全了,瀏覽器就會阻止。
五、IE10瀏覽器不支援crossOrigin怎麼辦?我們要求圖片的時候,不是直接透過new Image(),而是藉助ajax和URL.createObjectURL()方法曲線救國。
程式碼如下:
var xhr = new XMLHttpRequest();xhr.onload = function () { var url = URL.createObjectURL(this.response); var img = new Image(); img.onload = function () { // 此時你就可以使用canvas對img為所欲為為了// ... code ... // 圖片用完後記得釋放內存URL.revokeObjectURL(url); }; img.src = url;};xhr.open('GET', url, true);xhr.responseType = 'blob';xhr.send();
此方法不僅IE10瀏覽器OK,原本支援crossOrigin的各位瀏覽器也是支援的。
也就多走一個ajax請求,還可以!
根據,根據實作發現,在IE瀏覽器下,如果要求的圖片過大,幾千像素那種,圖片會載入失敗,我猜是超過了blob尺寸限制。
六、結束語最近工作中學到的一點小經驗,希望可以幫助遇到類似問題的夥伴。也希望大家多多支持VeVb武林網。