Çevrimdışı yayın özellikli PWA

Derek Herman
Derek Herman
Jaroslav Polakovič
Jaroslav Polakovič

Progresif Web Uygulamaları, daha önce yerel kullanım için ayrılmış birçok özellik sunar. kullanıma sunuyoruz. Proje yönetimiyle ilgili en önemli özelliklerden biri, PWA'lar çevrimdışı bir deneyimdir.

Daha da iyisi, çevrimdışı medya yayını deneyimi geliştirmeyi birkaç farklı şekilde öğreneceksiniz. Ancak, bu gerçek anlamda benzersiz bir sorun oluşturur. Medya dosyaları çok büyük olabilir. ODK şu soruyu soruyor olabilirsiniz:

  • Büyük bir video dosyasını nasıl indirir ve saklarım?
  • Bunu kullanıcıya nasıl sunabilirim?

Bu makalede, bu soruların cevaplarını ele alacağız. pratik bilgiler sunan Kino demo PWA'sını referans alarak olmadan çevrimdışı medya akışı deneyimini işlevsel veya sunumsal çerçeveler kullanmayı deniyor. Aşağıdaki örnekler: genel olarak eğitim amaçlıdır. Çünkü çoğu durumda büyük olasılıkla eğitim mevcut Medya Çerçevelerinden birine sahip olması gerekir.

Kendiniz için iyi bir iş gerekçeniz yoksa PWA oluşturmak zorluklarını da beraberinde getiriyor. Bu makalede, kullanıcılara yüksek kaliteli çevrimdışı medya sunmak için kullanılan API'ler ve teknikler sahip olacaksınız.

Büyük bir medya dosyasını indirme ve depolama

Progresif Web Uygulamaları, indirme işlemlerini hem indirme işlemi hem de indirme için genellikle çevrimdışı deneyimi sağlamak için gereken belgeleri, belgeleri, içerik oluşturabilirsiniz.

Bir hizmet çalışanı içinde Cache API'yi kullanmaya dair temel bir örneği burada bulabilirsiniz:

const cacheStorageName = 'v1';

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(cacheStorageName).then(function(cache) {
      return cache.addAll([
        'index.html',
        'style.css',
        'scripts.js',

        // Don't do this.
        'very-large-video.mp4',
      ]);
    })
  );
});

Yukarıdaki örnek teknik olarak işe yarar. Cache API'sini kullanmak için büyük dosyalarla kullanımını zora sokan bazı sınırlamalar içerir.

Örneğin, Cache API:

  • İndirme işlemlerini kolayca duraklatmanıza ve devam ettirmenize olanak tanır
  • İndirme işlemlerinin ilerleme durumunu izlemenize olanak tanır
  • HTTP aralığı isteklerine doğru şekilde yanıt verme yöntemi sunun.

Bu sorunların hepsi, herhangi bir video uygulaması için oldukça ciddi sınırlamalardır. Sizin için daha uygun olabilecek diğer seçenekleri inceleyelim.

Günümüzde Getirme API'si, uzaktan kumandaya eşzamansız olarak erişmenin farklı bir yoludur. dosyası olarak da kaydedebilir. Kullanım örneğimizde, büyük video dosyalarına akış olarak erişmenizi sağlar. bunları bir HTTP aralığı isteği kullanarak artımlı şekilde parçalar halinde depolamanız gerekir.

Artık Getirme API'si ile veri parçalarını okuyabildiğinize göre saklamanız gerekir. Muhtemelen medyanızla ilişkili çok sayıda meta veri vardır. Örneğin, ad, açıklama, çalışma zamanı uzunluğu, kategori vb.

Yalnızca tek bir medya dosyasını değil, yapılandırılmış bir nesneyi de depoluyorsunuz. medya dosyası da onun özelliklerinden yalnızca biridir.

Bu durumda IndexedDB API, hem verileri ve meta verileri içerir. Büyük miktarlarda ikili veriyi kolayca saklayabilir ve , çok hızlı veri aramaları yapmanızı sağlayan dizinler de sunar.

Medya dosyalarını Fetch API'yi kullanarak indirme

Demo PWA'mızda Fetch API'yi kullanarak birkaç ilginç özellik geliştirdik. Kino olarak adlandırdık. Kaynak kod herkese açıktır, bu nedenle incelemekten çekinmeyin.

  • Tamamlanmamış indirme işlemlerini duraklatma ve devam ettirme olanağı.
  • Veri parçalarını veritabanında depolamak için özel bir arabellek.

Bu özelliklerin nasıl uygulandığını göstermeden önce, Getirme API'sini kullanarak dosyaları nasıl indirebileceğinizi gösteren kısa bir özet.

/**
 * Downloads a single file.
 *
 * @param {string} url URL of the file to be downloaded.
 */
async function downloadFile(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  do {
    const { done, dataChunk } = await reader.read();
    // Store the `dataChunk` to IndexedDB.
  } while (!done);
}

await reader.read() adlı cihazın döngüye girdiğini fark ettiniz mi? Parçaları bu şekilde alırsınız gelen verileri içerebilir. Nasıl yapılır? Şu açıdan yararlıdır: ağdan.

İndirmeler devam ettiriliyor

Bir indirme işlemi duraklatıldığında veya kesintiye uğradığında, gelen veri parçaları IndexedDB veritabanında güvenli bir şekilde saklanabilir. Ardından aşağıdakileri yapmak için bir düğme görüntüleyebilirsiniz: uygulamanızda indirme işlemini devam ettirebilirsiniz. Çünkü Kino demo PWA sunucusu HTTP aralığı isteklerini destekler, indirme işlemini devam ettirmek biraz basittir:

async downloadFile() {
  // this.currentFileMeta contains data from IndexedDB.
  const { bytesDownloaded, url, downloadUrl } = this.currentFileMeta;
  const fetchOpts = {};

  // If we already have some data downloaded,
  // request everything from that position on.
  if (bytesDownloaded) {
    fetchOpts.headers = {
      Range: `bytes=${bytesDownloaded}-`,
    };
  }

  const response = await fetch(downloadUrl, fetchOpts);
  const reader = response.body.getReader();

  let dataChunk;
  do {
    dataChunk = await reader.read();
    if (!dataChunk.done) this.buffer.add(dataChunk.value);
  } while (!dataChunk.done && !this.paused);
}

IndexedDB için özel yazma arabelleği

Kağıt üzerinde, IndexedDB veritabanına dataChunk değerleri yazma işlemi çok basit. Bu değerler zaten depolanabilen ArrayBuffer örnektir Böylece yalnızca uygun şekle sahip bir nesne oluşturabiliriz. saklamasına gerek yok.

const dataItem = {
  url: fileUrl,
  rangeStart: dataStartByte,
  rangeEnd: dataEndByte,
  data: dataChunk,
}

// Name of the store that will hold your data.
const storeName = 'fileChunksStorage'

// `db` is an instance of `IDBDatabase`.
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const putRequest = store.put(data);

putRequest.onsuccess = () => { ... }

Bu yaklaşım işe yaramış olsa da IndexedDB'nizin, Search Console'da indirme işleminizden önemli ölçüde daha yavaş. Bunun nedeni, IndexedDB yazmalarının bunun nedeni, her şeyin hazır olduğundan çok fazla işlem bir ağdan aldığımız her veri parçası için yeni bir işlem oluşturur.

İndirilen parçalar oldukça küçük olabilir ve akış tarafından başarılı sonuçlar verir. IndexedDB yazma hızını sınırlamanız gerekir. Kino demo PWA'sı bunun için bir aracı yazma arabelleği uygulayarak yapılır.

Veri parçaları ağdan geldikçe ilk olarak tamponumuza eklenir. Eğer uygun değilse tam tamponu veritabanına atarız ve bunları eklemeden önce temizleyin. Bunun sonucunda, IndexedDB yazma sıklığı daha azdır ve bu da yazım kalitesinin önemli ölçüde artmasını sağlar bazı yolları da görmüştük.

Çevrimdışı depolama alanından medya dosyası sunma

Bir medya dosyası indirildikten sonra, muhtemelen hizmet çalışanınızın bu dosyayı ağdan getirmek yerine IndexedDB'den sunacaktır.

/**
 * The main service worker fetch handler.
 *
 * @param {FetchEvent} event Fetch event.
 */
const fetchHandler = async (event) => {
  const getResponse = async () => {
    // Omitted Cache API code used to serve static assets.

    const videoResponse = await getVideoResponse(event);
    if (videoResponse) return videoResponse;

    // Fallback to network.
    return fetch(event.request);
  };
  event.respondWith(getResponse());
};
self.addEventListener('fetch', fetchHandler);

Peki getVideoResponse() bölgesinde ne yapmanız gerekiyor?

  • event.respondWith() yöntemi, parametre olarak bir Response nesnesini bekler.

  • Response() oluşturucusu, bizim kullandığımız birkaç nesne türü olduğunu Response nesnesini örneklendirmek için kullanabileceğiniz bir terim: Blob, BufferSource, ReadableStream ve daha fazlası.

  • Tüm verilerini bellekte tutmayan bir nesneye ihtiyacımız var, bu yüzden muhtemelen ReadableStream cihazını seçmek istersiniz.

Ayrıca, büyük dosyalarla işlem yaptığımızdan ve tarayıcıların bu dosyaları dosyanın yalnızca şu anda ihtiyaç duydukları bölümü istemesi durumunda, HTTP aralığı istekleri için bazı temel destekleri verir.

/**
 * Respond to a request to fetch offline video file and construct a response
 * stream.
 *
 * Includes support for `Range` requests.
 *
 * @param {Request} request  Request object.
 * @param {Object}  fileMeta File meta object.
 *
 * @returns {Response} Response object.
 */
const getVideoResponse = (request, fileMeta) => {
  const rangeRequest = request.headers.get('range') || '';
  const byteRanges = rangeRequest.match(/bytes=(?<from>[0-9]+)?-(?<to>[0-9]+)?/);

  // Using the optional chaining here to access properties of
  // possibly nullish objects.
  const rangeFrom = Number(byteRanges?.groups?.from || 0);
  const rangeTo = Number(byteRanges?.groups?.to || fileMeta.bytesTotal - 1);

  // Omitting implementation for brevity.
  const streamSource = {
     pull(controller) {
       // Read file data here and call `controller.enqueue`
       // with every retrieved chunk, then `controller.close`
       // once all data is read.
     }
  }
  const stream = new ReadableStream(streamSource);

  // Make sure to set proper headers when supporting range requests.
  const responseOpts = {
    status: rangeRequest ? 206 : 200,
    statusText: rangeRequest ? 'Partial Content' : 'OK',
    headers: {
      'Accept-Ranges': 'bytes',
      'Content-Length': rangeTo - rangeFrom + 1,
    },
  };
  if (rangeRequest) {
    responseOpts.headers['Content-Range'] = `bytes ${rangeFrom}-${rangeTo}/${fileMeta.bytesTotal}`;
  }
  const response = new Response(stream, responseOpts);
  return response;

Aşağıdakileri öğrenmek için Kino demo PWA service Worker kaynak koduna göz atabilirsiniz: IndexedDB'deki dosya verilerini nasıl okuduğumuzu ve bir uygulamadır.

Dikkat edilmesi gereken diğer noktalar

Önünüzdeki ana engelleri ortadan kaldırdıktan sonra birkaç Nitelikli ve işlevsel özellikler sunar. Çevik yaklaşımın aşağıdaki özellikleri Kino demo PWA'da bulabilirsiniz:

  • Kullanıcılarınızın medyayı kontrol etmesine olanak tanıyan Media Session API'si entegrasyonu özel donanım medya tuşlarını kullanarak veya medya bildiriminden oynatma pop-up'lar.
  • Altyazılar gibi medya dosyalarıyla ilişkili diğer öğelerin önbelleğe alınması ve Poster resimlerini, eski ve iyi Cache API'yi kullanarak oluşturun.
  • Uygulama içinde video akışları (DASH, HLS) indirme desteği. Çünkü akış manifest dosyaları genellikle farklı bit hızlarına sahip birden çok kaynak bildirdiği için manifest dosyasını dönüştürün ve depolamadan önce yalnızca bir medya sürümü indirin. çevrimdışı görüntüleme için kullanın.

Sıradaki videoda Ses ve video önceden yükleyerek hızlı oynatma hakkında bilgi edineceksiniz.