在一次開發中,遇到了生成海報的需求:在圖片相冊界面用户選擇若干張圖片,在海報編輯界面用户通過拖動這些圖片生成一張海報,供用户下載。
在圖片相冊界面,圖片均展示正常且無報錯發生。當用户選擇若干張圖片之後,進入海報編輯界面前,添加cors屬性<img crossOrigin=anonymous>重新加載這些圖片,以便canvas處理這些圖片數據(圖片添加cors屬性原因)。神奇的是:所有的圖片均無法顯示,報錯 `......has been blocked by CORS policy: No
Access-Control-Allow-Origin header is present on the
requested resource.`,原來被瀏覽器的同源策略攔截了。
這些圖片存放在Amazon S3,都配置了允許跨域訪問。添加了cors參數的img不應該被同源策略攔截,而無法顯示。
抽象問題,並探究
添加圖片的方法:
const appendImage = (imageUrl, crossOrigin = 'anonymous') => {
const image = document.createElement('img')
if (crossOrigin) image.crossOrigin = crossOrigin
image.src = imageUrl
document.body.append(image)
}
當在Chrome瀏覽器中添加全新的圖片時,顯示正常:
清空瀏覽器緩存之後,當在瀏覽器中加載需要cors屬性的圖片時,顯示正常:
清空瀏覽器緩存之後,當在瀏覽器中添加該圖片時,顯示正常;添加cors屬性然後加載該圖片時,無法顯示:
通過Response Headers可以看到:因為沒有響應Access-Control-Allow-Origin字段,被瀏覽器的CORS策略攔截了。
初步結論:通過以上三個現象可以得知,開發需求時的異常現象應該與Chrome瀏覽器的緩存有關。
原因
通過查詢相關文檔,發現早在2014年就有人給chromium提出issue:圖片資源(url)會緩存在瀏覽器中,下次訪問相同的圖片資源(url)時,返回被緩存資源的response。
結合上面的開發需求,可以得出原因:
- 圖片相冊界面中的圖片,均以未添加cors屬性的方式請求,response返回無跨域頭的數據,加載之後便被瀏覽器緩存;
- 海報編輯界面中的圖片,均以添加cors屬性的方式請求,瀏覽器返回已有的緩存數據;
- 由於緩存數據無跨域頭,導致以添加cors屬性的方式請求的圖片資源,被瀏覽器的同源策略屏蔽掉。
然而Chromium的開發團隊將該問題標記為WontFix(Closed),可能是因為比較符合Chromium引擎的預期行為。
解決方案
在得知原因之後,便着手解決方案:為圖片url的search部分添加一個字段,用來區分無cors屬性的請求。
const appendImage = (imageUrl, crossOrigin = 'anonymous') => {
const image = document.createElement('img')
let src = imageUrl
if (crossOrigin) {
src = new URL(src)
src.searchParams.append(`cors-type`, crossOrigin)
src = src.toString()
image.crossOrigin = crossOrigin
}
image.src = src
document.body.append(image)
}
修改appendImage函數之後,再次請求圖片,有無cors屬性的圖片均展示:
總結
Chrome瀏覽器的圖片緩存,只按url來區分,因此有無cors屬性的圖片在請求時是無差別的。若緩存數據無響應Access-Control-Allow-Origin字段,而圖片資源需要該字段時就會產生問題。
在url-search裏添加字段,用以區分有無cors屬性,使緩存能匹配期望的響應。