最近瀏覽官網(wǎng)時,突然發(fā)現(xiàn)在Safari中視頻播放不了了,上線前是可以播放的,在Chrome中打開也是能播放的,這就很奇怪了。
問題分析
1. 查看請求
首先打開開發(fā)者工具,對比Chrome和Safari的視頻請求:


通過比較
Chrome和Safari的請求我們發(fā)現(xiàn),Chrome請求頭中range字段的值是bytes=0-,而Safari請求頭中range字段的值是bytes=0-1。從此得知,瀏覽器請求視頻時是使用的范圍請求,Chrome是用一個HTTP請求請求了整個視頻,即請求視頻的第0個字節(jié)到最后一個字節(jié),Chrome不強制要求服務(wù)端支持范圍請求,服務(wù)端響應(yīng)200或206,Chrome都能支持。但是Safari要求服務(wù)端必須支持視頻的范圍請求,Safari會先請求視頻的第0個字節(jié)到第一個字節(jié),來測試服務(wù)端是否支持范圍請求,如果服務(wù)端支持范圍請求,則響應(yīng)狀態(tài)碼206,響應(yīng)頭中有正確的Content-Range字段,響應(yīng)體是視頻的第一個字節(jié),此時,Safari才會繼續(xù)請求視頻的其他字節(jié),否則Safari會放棄該視頻的請求。從圖中可以看到,我們視頻的服務(wù)端不支持范圍請求,響應(yīng)的是整個視頻,狀態(tài)碼200,所以導(dǎo)致無法在Safari播放。
2. ServiceWorker
剛才所說的視頻服務(wù)端并不是指nginx或CDN,阿里云CDN和nginx都是支持視頻的范圍請求的。這個不支持范圍請求的服務(wù)端是ServiceWorker。因為我們官網(wǎng)采用了PWA技術(shù),視頻使用ServiceWorker緩存了,瀏覽器請求視頻時,請求是由ServiceWorker在處理。這就是為什么上線前沒有發(fā)現(xiàn)這個問題,因為上線前視頻還沒被緩存,測試不夠充分,那時Safari是請求到CDN,可以正常請求到視頻,如下圖:

從圖中可以看到,
Safari將一個視頻分成了多次范圍請求,每次請求部分字節(jié),只有當(dāng)?shù)谝粋€0-1字節(jié)的測試請求被正確響應(yīng)時,Safari才會發(fā)送其他范圍請求。
問題解決
問題已經(jīng)定位清楚了,解決辦法就是使ServiceWorker支持視頻的范圍請求。
我們官網(wǎng)是采用的nextjs框架,PWA是通過next-offline這個插件實現(xiàn)的,next-offline封裝的workbox-webpack-plugin,workbox-webpack-plguin是workbox庫提供的一個webpack插件,workbox是Google開發(fā)的一個功能強大的用于搭建PWA站點的庫。workbox還提供了一個workbox.rangeRequests.Plugin插件來支持范圍請求,所以我們只需要使用這個插件就可以了。
首先,workbox-webpack-plugin支持使用兩種模式:GenerateSw和InjectMainfest。
GenerateSw模式是根據(jù)配置自動生成整個service-worker.js文件,它適用于簡單的使用場景。
InjectMainfest模式是需要你自定義service-worker.js文件,然后由workbox-webpack-plugin將webpack打包生成的靜態(tài)資源precache自動插入到你的service-worker.js文件中。
我們要使用workbox.rangeRequests.Plugin就需要使用InjectMainfest模式。
next-offline默認(rèn)是使用workbox-webpack-plugin的GenerateSw模式,所以需要在next.config.js中加入如下配置:

然后在
service-worker.js中使用workbox.rangeRequests.Plugin:
在
service-worker.js中,我們用到了兩個緩存策略:
- 以
.mp4結(jié)尾的所有請求都用CacheFirst策略,并且在這個策略中使用了workbox.rangeRequests.Plugin以支持范圍請求。 - 以
https開頭的所有請求都用NetworkFirst策略。
匹配.mp4的策略必須寫在匹配https策略之前,因為視頻也能被https策略匹配到,其實這里可以將https匹配的正則改成以https開頭但不以.mp4結(jié)尾:/^https?.*(?<!\.mp4)$/,但是Safari不支持否定逆序環(huán)視。
還有需要注意的一點,如果我們對mp4使用CacheFirst策略,需要在servicer-worker.js install的時候?qū)⒁曨l都添加到緩存。
其他一些限制參見官網(wǎng)文檔:Serve cached audio and video
這樣配置了之后,Safari也能播放ServiceWorker緩存的視頻了:

Workbox調(diào)試技巧
Chrome的開發(fā)者工具很強大,在Application標(biāo)簽下可以看到Service Workers和Cache、Storage。具體教程參見官網(wǎng)文檔。
但是Safari就不行了,很難調(diào)試。首先我們可以配置Workbox Debug模式:

這個配置必須寫在所有引用Workbox的代碼之前。配置好之后,Workbox會打印攔截請求的詳情信息:

可以清楚的看到哪個請求被命中,使用了什么緩存策略處理。
但是這個日志不是打印到控制臺的。Safari需要在開發(fā)-服務(wù)工作線程-<你的站點域名>打開ServiceWorker網(wǎng)頁檢查器,才能看到Workbox日志:

Safari也沒給你控制ServiceWorker的調(diào)試工具,即使你修改了service-worker.js代碼,也有可能瀏覽器還在用老的service-worker.js。
你可以將以下代碼復(fù)制粘貼到Safari控制臺執(zhí)行,然后重新打開瀏覽器以調(diào)試最新代碼:

【完】