在網頁上管理影片儲存空間

影片是難以管理的素材資源,串流需要大量頻寬,快取也不容易。如果影片循環播放 (例如在資訊亭螢幕上),這些問題會更加嚴重。舉例來說,如果公司有數百部裝置每天重複播放 30 部影片,網路很快就會不堪負荷。系統會從快取提供影片,而非串流播放,因此您只需支付一次下載費用,後續播放速度會更快,且影片可離線播放。如要這麼做,您可以善用瀏覽器的儲存功能,其中 Cache storage APIIndexedDB 最適合儲存影片檔案。這兩種 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 工作類似的配方,包括與 WebpackRollup 等建構工具整合。

設定 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,就能輕鬆管理這項原本困難的工作,節省使用者頻寬、減少伺服器負載、加快影片播放速度,甚至在離線時也能播放影片。