影片是難以管理的素材資源,串流需要大量頻寬,快取也不容易。如果影片循環播放 (例如在資訊亭螢幕上),這些問題會更加嚴重。舉例來說,如果公司有數百部裝置每天重複播放 30 部影片,網路很快就會不堪負荷。系統會從快取提供影片,而非串流播放,因此您只需支付一次下載費用,後續播放速度會更快,且影片可離線播放。如要這麼做,您可以善用瀏覽器的儲存功能,其中 Cache storage API 和 IndexedDB 最適合儲存影片檔案。這兩種 API 都是不錯的選擇,但我們將著重於 Cache 儲存空間 API,因為它與熱門的 Service Worker 程式庫 Workbox 整合。
從 Service Worker 快取影片
由於下載及快取影片等大型資產可能需要大量時間和處理器資源,因此您應在主執行緒以外的背景執行這項作業。服務工作人員特別適合用於卸載快取工作。這些服務工作人員會充當網頁和網路之間的 Proxy,攔截要求並對網路回應套用額外邏輯,例如快取策略。
快取策略有很多種,每種策略都適用於不同的用途。舉例來說,如要從快取提供檔案 (如有),或在沒有檔案時改用網路,可以編寫下列程式碼。
self.addEventListener('fetch', function (event) { event.respondWith( caches.match(event.request).then(function (response) { return response || fetch(event.request); }), ); });
如果不同類型的資產或網址需要不同的快取策略,管理這類項目可能會很繁瑣,而且容易出錯。Workbox 提供一系列工具,包括路徑輔助程式和快取策略,可讓您以更具宣告性且可重複使用的方式編寫 Service Worker 程式碼。
先前的策略稱為「快取優先」。如要使用 Workbox 撰寫相同內容,請加入下列項目:
registerRoute( ({ request }) => request.destination === 'video', new CacheFirst() );
Workbox 提供其他快取策略和常見 Service Worker 工作類似的配方,包括與 Webpack 和 Rollup 等建構工具整合。
設定 Workbox 後,接著需要選擇影片的快取時間。這裡有兩種做法:在網頁載入時急切載入,或在要求影片時延遲載入。
積極做法
預先快取是一種技術,可在 Service Worker 安裝期間將檔案儲存至快取,讓檔案在 Service Worker 啟動後隨即可用。Workbox 可在建構程序期間自動為可存取的檔案設定預先快取。
您可以在 Service Worker 中使用下列 Workbox 程式碼,預先快取檔案:
import { addPlugins, precacheAndRoute } from 'workbox-precaching'; import { RangeRequestsPlugin } from 'workbox-range-requests'; addPlugins([new RangeRequestsPlugin()]); precacheAndRoute(self.__WB_MANIFEST);
import(s) - 從對應的 Workbox 模組載入必要繫結。由於服務工作人員目前尚未普遍支援 ESModules,因此您需要透過打包工具傳遞以 Workbox 為基礎的服務工作人員,才能在正式環境中運作。RangeRequestsPlugin:如果要求含有Range標頭,系統就能使用快取回應來完成要求。這是因為瀏覽器通常會使用Range標頭處理媒體內容。addPlugins- 允許您將 Workbox 外掛程式新增至每個 Workbox 要求。precacheAndRoute- 將項目新增至預先快取清單,並建立處理對應擷取要求的路徑。__WB_MANIFEST:預留位置,Workbox CLI (或建構工具外掛程式) 會將其替換為預先快取資訊清單。
將服務工作人員傳遞至 Workbox CLI 或您選擇的建構工具,並設定應如何產生預先快取;workbox-config.js 檔案 (如下所示) 會告訴 CLI 應如何轉譯服務工作人員:
module.exports = { globDirectory: '.', globPatterns: ['**/*.{html,mp4}'], maximumFileSizeToCacheInBytes: 5000000, swSrc: 'sw.js', swDest: 'sw.js', };
globDirectory- 開始搜尋預先快取檔案的根資料夾globPatterns:應預先快取的檔案模式 (「glob」)。maximumFileSizeToCacheInBytes:可預先快取檔案的大小上限 (以位元組為單位)。swSrc:用於產生 Service Worker 的檔案位置。swDest- 生成服務工作人員的目的地 (可與來源檔案相同,但請確保每次執行時都有self.__WB_MANIFEST)。
建構程序執行時,系統會產生新版 Service Worker,並將 self.__WB_MANIFEST 替換為檔案清單,每個檔案都有雜湊值,表示其修訂版本:
precacheAndRoute([ { revision: '524ac4b453c83f76eb9caeec11854ca5', url: 'ny.mp4', }, ]);
每次執行建構程序時,系統都會以目前的一組相符檔案及其目前的修訂版本雜湊值,重新編寫這份清單。這樣一來,每當檔案新增、移除或變更時,服務工作人員就會在下次安裝時更新快取。
延遲做法
如果您在建構時沒有所有影片,或只想在需要時快取影片,就應採用延遲做法。這種做法需要將快取和放送作業分開,因為影片播放期間只會從網路擷取部分內容,所以無法在串流播放時快取檔案。
快取檔案
快取可使用 Cache.open() 建立,然後使用 Cache.add() 或 Cache.addAll() 將檔案新增至快取。如果應用程式收到要快取的影片 JSON 清單,可以按照下列方式新增至影片快取:
// Open video cache const cache = await caches.open('video-cache'); // Fetch list of videos const videos = await (await fetch('/video-list.json')).json(); // Add videos to cache await cache.addAll(videos);
這種做法的優點是,即使來自其他網頁工作站,您也可以獨立控制快取步驟,不必受服務工作站生命週期影響。缺點是儲存空間管理部分由開發人員負責:您需要編寫自己的演算法來追蹤檔案變更、追蹤瀏覽器中目前快取的檔案,以及管理檔案更新,確保只有變更的檔案會更新。
放送快取的影片檔案
接著,可以使用服務工作人員執行階段快取策略 (例如先快取),提供先前快取的影片檔案:
import { registerRoute } from 'workbox-routing'; import { CacheFirst } from 'workbox-strategies'; import { CacheableResponsePlugin } from 'workbox-cacheable-response'; import { RangeRequestsPlugin } from 'workbox-range-requests'; registerRoute( ({ request }) => request.destination === 'video', new CacheFirst({ cacheName: 'video-cache', plugins: [ new CacheableResponsePlugin({ statuses: [200], }), new RangeRequestsPlugin(), ], }), );
import(s) - Loads the bindings required from the corresponding workbox modules.registerRoute- 將要求轉送至提供回應的函式 (快取策略和外掛程式)。CacheFirst- 快取策略,可從快取滿足要求 (如有),否則會從網路擷取要求並更新快取。CacheableResponsePlugin:用於指出回應必須包含哪些標頭才能快取。請務必只納入 200 個狀態,以利將影片快取至路徑,避免在串流影片時,將部分內容回應 (206) 快取為影片。RangeRequestsPlugin:外掛程式,可讓含有Range標頭的要求透過快取回應完成。這是因為瀏覽器通常會使用Range標頭處理媒體內容。
對於需要大量串流的應用程式來說,最佳化影片載入作業是相當重要的工作。只要善用瀏覽器的 Cache Storage API 和 Workbox,就能輕鬆管理這項原本困難的工作,節省使用者頻寬、減少伺服器負載、加快影片播放速度,甚至在離線時也能播放影片。