web-dev-qa-db-ja.com

Service Worker APIを使用してHTML5ビデオ全体をオフラインでキャッシュすることはできますか?

すべての静的リソースをキャッシュするオフラインアプリがあります。現在、ビデオアセットの最初の15秒のみがキャッシュされます。

以下に、installおよびfetchイベントリスナーの基本的な実装を示します。

Service Worker:

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('v1').then(cache => {
      return cache.addAll([
        '/',
        '/videos/one.mp4',
        '/videos/two.mp4'
      ]);
    })
  );
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(response => {
      if (response) return response;
      return fetch(event.request);
    });
  );
});

そしてindex.html

<video controls preload>
  <source src="/videos/one.mp4" type="video/mp4">
</video>
14

次の手順を使用して、最初にビデオ全体を視聴せずに、最初のページの読み込みでオフラインビデオを実現しました。

  1. Service Workerを登録し、すべてのリクエストをキャッシュします。静的アセットは'/'この場合。 Service Workerのfetchイベントを調べると、後続のリクエストもキャッシュされていることがわかります。
  2. fetch AP​​Iを使用して、blobとして動画をリクエストします。

フェッチを使用して動画をblobとしてリクエストする例

const videoRequest = fetch('/path/to/video.mp4').then(response => response.blob());
videoRequest.then(blob => {
  ...
});
  1. IndexedDB AP​​Iを使用してblobを保存します。 (保存中にメインスレッドがブロックされないようにするには、IndexedDBではなくLocalStorageを使用してください。)

それでおしまい!これで、オフラインモードのときに、Service Workerはリクエストをインターセプトし、htmlblobの両方をキャッシュから提供します。

index.html

<!DOCTYPE html>
<html>
<head>
  <title>Test</title>
</head>
<body>

  <h1>Service Worker Test</h1>

  <p>Try reloading the page without an Internet connection.</p>

  <video controls></video>

  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js').then(registration => {
          console.log('ServiceWorker registration successful with scope: ', registration.scope);
        }).catch(error => {
          console.log('ServiceWorker registration failed: ', error);
        });
      });
    } else {
      alert('serviceWorker is not in navigator');
    }
  </script>

  <script>
    const videos = {
      one: document.querySelector('video')
    };

    const videoRequest = fetch('/path/to/video.mp4').then(response => response.blob());
    videoRequest.then(blob => {
      const request = indexedDB.open('databaseNameHere', 1);

      request.onsuccess = event => {
        const db = event.target.result;

        const transaction = db.transaction(['videos']);
        const objectStore = transaction.objectStore('videos');

        const test = objectStore.get('test');

        test.onerror = event => {
          console.log('error');
        };

        test.onsuccess = event => {
          videos.one.src = window.URL.createObjectURL(test.result.blob);
        };
      }

      request.onupgradeneeded = event => {
        const db = event.target.result;
        const objectStore = db.createObjectStore('videos', { keyPath: 'name' });

        objectStore.transaction.oncomplete = event => {
          const videoObjectStore = db.transaction('videos', 'readwrite').objectStore('videos');
          videoObjectStore.add({name: 'test', blob: blob});
        };
      }
    });
  </script>
</body>
</html>

Service Worker

const latest = {
  cache: 'some-cache-name/v1'
};

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(latest.cache).then(cache => {
      return cache.addAll([
        '/'
      ]);
    })
  );
});

self.addEventListener('fetch', event => {
  // exclude requests that start with chrome-extension://
  if (event.request.url.startsWith('chrome-extension://')) return;
  event.respondWith(
    caches.open(latest.cache).then(cache => {
      return cache.match(event.request).then(response => {
        var fetchPromise = fetch(event.request).then(networkResponse => {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        })
        return response || fetchPromise;
      })
    })
  );
});

self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.filter(cacheName => {
          if (cacheName === latest.cache) {
            return false;
          }

          return true;
        }).map(cacheName => {
          return caches.delete(cacheName)
        })
      );
    })
  );
});

リソース:

11