Kiwix PWA, kullanıcıların çevrimdışı kullanım için internetten Gigabaytlarca veriyi nasıl depolamasına olanak tanıyor?

Geoffrey Kantaris
Geoffrey Kantaris
Stéphane Coillet-Matillon
Stéphane Coillet-Matillon

Sol tarafta plastik bir sandalye bulunan basit bir masanın üzerinde duran bir dizüstü bilgisayarın etrafında toplanmış insanlar. Arka plan, gelişmekte olan bir ülkedeki okul gibi görünüyor.

Bu örnek olayda, kâr amacı gütmeyen bir kuruluş olan Kiwix'in, kullanıcıların internetteki büyük arşivleri indirip çevrimdışı kullanabilmeleri için Progressive Web App teknolojisini ve File System Access API'yi nasıl kullandığı incelenmektedir. Kiwix PWA'daki yeni bir tarayıcı özelliği olan Origin Private File System (OPFS) ile ilgili kodun teknik uygulaması hakkında bilgi edinin. Bu özellik, dosya yönetimini iyileştirerek izin istemleri olmadan arşivlere daha iyi erişim sağlar. Makalede, bu yeni dosya sisteminde karşılaşılan zorluklar ele alınmakta ve gelecekte yapılabilecek olası geliştirmeler vurgulanmaktadır.

Kiwix Hakkında

Uluslararası Telekomünikasyon Birliği'ne göre, web'in ortaya çıkmasından 30 yıldan uzun bir süre geçmesine rağmen dünya nüfusunun üçte biri hâlâ internete güvenilir bir şekilde erişemiyor. Hikaye burada bitiyor mu? Elbette değil. İsviçre merkezli bir sivil toplum kuruluşu olan Kiwix, internet erişimi sınırlı veya hiç olmayan kullanıcıların bilgiye erişebilmesini amaçlayan açık kaynak uygulama ve içerik ekosistemi geliştirdi. Siz internete kolayca erişemiyorsanız bağlantının olduğu yerlerde ve zamanlarda sizin için önemli kaynakları indirip daha sonra çevrimdışı kullanmak üzere yerel olarak depolayabilir. Wikipedia, Project Gutenberg, Stack Exchange ve hatta TED konuşmaları gibi birçok önemli site artık ZIM dosyaları adı verilen yüksek sıkıştırılmış arşivlere dönüştürülebiliyor ve Kiwix tarayıcısı tarafından anında okunabiliyor.

ZIM arşivleri, çoğunlukla HTML, JavaScript ve CSS'yi depolamak için son derece verimli Zstandard (ZSTD) sıkıştırma yöntemini (eski sürümlerde XZ kullanılıyordu) kullanır. Resimler ise genellikle sıkıştırılmış WebP biçimine dönüştürülür. Her ZIM'de bir URL ve başlık dizini de bulunur. Burada sıkıştırma çok önemlidir. İngilizce Wikipedia'nın tamamı (6,4 milyon makale ve resimler) ZIM biçimine dönüştürüldükten sonra 97 GB'a sıkıştırılır. Bu, tüm insan bilgisinin artık orta sınıf bir Android telefona sığabileceğini anlayana kadar çok fazla gibi görünebilir. Matematik ve tıp gibi temalı Wikipedia sürümleri de dahil olmak üzere birçok küçük kaynak da sunulur.

Kiwix, masaüstü (Windows/Linux/macOS) ve mobil (iOS/Android) kullanıma yönelik bir dizi yerel uygulama sunar. Ancak bu örnek olayda, modern bir tarayıcıya sahip tüm cihazlar için evrensel ve basit bir çözüm sunmayı amaçlayan Progresif Web Uygulaması (PWA) özelliğine odaklanacağız.

Tamamen çevrimdışı olarak büyük içerik arşivlerine hızlı erişim sağlaması gereken evrensel bir web uygulaması geliştirmeyle ilgili zorluklara ve bu zorluklara yenilikçi ve heyecan verici çözümler sunan bazı modern JavaScript API'lerine (özellikle File System Access API ve Origin Private File System) bakacağız.

Çevrimdışı kullanım için bir web uygulaması mı var?

Kiwix kullanıcıları, birçok farklı ihtiyacı olan eklektik bir gruptur ve Kiwix, kullanıcıların içeriklerine erişecekleri cihazlar ve işletim sistemleri üzerinde çok az kontrole sahiptir. Bu cihazlardan bazıları, özellikle dünyanın düşük gelirli bölgelerinde yavaş veya eski olabilir. Kiwix mümkün olduğunca çok kullanım alanı kapsamaya çalışırken, herhangi bir cihazdaki en evrensel yazılımı kullanarak daha da fazla kullanıcıya ulaşabileceğini fark etti: web tarayıcısı. Bu nedenle, JavaScript ile yazılabilen her uygulamanın sonunda JavaScript ile yazılacağını belirten Atwood Yasası'ndan esinlenerek yaklaşık 10 yıl önce bazı Kiwix geliştiricileri Kiwix yazılımını C++'dan JavaScript'e taşımaya karar verdi.

Bu bağlantı noktasının Kiwix HTML5 adı verilen ilk sürümü, artık kullanılmayan Firefox OS ve tarayıcı uzantıları içindi. Temel olarak, Emscripten derleyicisi kullanılarak ASM.js'nin ara JavaScript diline ve daha sonra Wasm veya WebAssembly'ye derlenen bir C++ sıkıştırma motoru (XZ ve ZSTD) idi (ve öyledir). Daha sonra Kiwix JS olarak yeniden adlandırılan tarayıcı uzantıları hâlâ aktif bir şekilde geliştirilmeye devam etmektedir.

Kiwix JS Çevrimdışı Tarayıcı

Progresif Web Uygulaması'nı (PWA) girin. Bu teknolojinin potansiyelini fark eden Kiwix geliştiricileri, Kiwix JS'nin özel bir PWA sürümünü oluşturdu ve uygulamanın özellikle çevrimdışı kullanım, yükleme, dosya işleme ve dosya sistemi erişimi alanlarında yerele benzer özellikler sunmasına olanak tanıyacak OS entegrasyonlarını eklemeye başladı.

Çevrimdışı öncelikli PWA'lar son derece hafif olduğundan, aralıklı veya pahalı mobil internetin olduğu bağlamlar için idealdir. Bunun arkasındaki teknoloji, Kiwix JS'ye dayalı tüm uygulamalar tarafından kullanılan Service Worker API ve ilgili Cache API'dir. Bu API'ler, uygulamaların sunucu gibi davranmasına olanak tanır. Görüntülenen ana doküman veya makaleden Getirme İsteklerinin alınmasını ve ZIM arşivinden bir yanıt alıp oluşturmak için (JS) arka uca yönlendirilmesini sağlar.

Her yerde depolama alanı

ZIM arşivlerinin büyük boyutu göz önüne alındığında, depolama ve özellikle mobil cihazlarda bunlara erişim, Kiwix geliştiricilerinin en büyük sorunlarından biri. Birçok Kiwix son kullanıcısı, internet bağlantısı olduğunda uygulama içinden içerik indirerek daha sonra çevrimdışı kullanabilir. Diğer kullanıcılar, torrent kullanarak içeriği bilgisayara indirip mobil cihaza veya tablete aktarır. Bazı kullanıcılar ise mobil internetin zayıf olduğu veya pahalı olduğu bölgelerde USB çubukları ya da taşınabilir sabit diskler üzerinden içerik alışverişi yapar. Kullanıcıların erişebildiği herhangi bir konumdan içeriğe erişme yöntemlerinin tümü Kiwix JS ve Kiwix PWA tarafından desteklenmelidir.

Kiwix JS'nin başlangıçta yüzlerce GB'lık devasa arşivleri (ZIM arşivlerimizin biri 166 GB'tır!) düşük bellek kapasiteli cihazlarda bile okumasını sağlayan şey File API'dir. Bu API, çok eski tarayıcılar dahil olmak üzere tüm tarayıcılarda evrensel olarak desteklenir. Bu nedenle, daha yeni API'ler desteklenmediğinde evrensel yedek olarak kullanılır. Bu, Kiwix'in durumunda HTML'de bir input öğesi tanımlamak kadar kolaydır:

<input
  type="file"
  accept="application/octet-stream,.zim,.zimaa,.zimab,.zimac, ..."
  value="Select folder with ZIM files"
  id="archiveFilesLegacy"
  multiple
/>

Seçildikten sonra giriş öğesi, depolama alanındaki temel verilere referans veren meta veriler olan Dosya nesnelerini tutar. Teknik olarak, Kiwix'in tamamen istemci tarafı JavaScript ile yazılmış nesne yönelimli arka ucu, gerektiğinde büyük arşivden küçük parçalar okur. Bu dilimlerin sıkıştırmasının çözülmesi gerekiyorsa arka uç, bunları Wasm sıkıştırma aracına iletir ve istek üzerine daha fazla dilim alır. Bu işlem, tam bir blob (genellikle bir makale veya öğe) sıkıştırması çözülene kadar devam eder. Bu, büyük arşivin hiçbir zaman tamamen belleğe okunması gerekmediği anlamına gelir.

Evrensel olan File API'nin, Kiwix JS uygulamalarının yerel uygulamalara kıyasla hantal ve eski moda görünmesine neden olan bir dezavantajı vardır: Bu API ile erişim izinlerinin bir oturumdan diğerine devam etmesinin bir yolu olmadığından, kullanıcının uygulama her başlatıldığında bir dosya seçici kullanarak arşiv seçmesini veya dosyayı uygulamaya sürükleyip bırakmasını gerektirir.

Birçok geliştirici gibi Kiwix JS geliştiricileri de bu kötü kullanıcı deneyimini azaltmak için başlangıçta Electron yolunu izledi. ElectronJS, Node API'leri kullanarak dosya sistemine tam erişim dahil olmak üzere güçlü özellikler sunan muhteşem bir çerçevedir. Ancak, bilinen bazı dezavantajları vardır:

  • Yalnızca masaüstü işletim sistemlerinde çalışır.
  • Büyük ve ağırdır (70 MB - 100 MB).

Her uygulamaya Chromium'un tam bir kopyasının dahil edilmesi nedeniyle Electron uygulamalarının boyutu, küçültülmüş ve paketlenmiş PWA'nın yalnızca 5,1 MB boyutuyla kıyaslandığında çok daha büyüktür.

Peki Kiwix, PWA kullanıcıları için durumu iyileştirmenin bir yolunu buldu mu?

File System Access API'nin yardımı

2019 civarında Kiwix, Chrome 78'de kaynak denemesi yapılan ve o zamanlar Native File System API olarak adlandırılan yeni bir API'nin varlığından haberdar oldu. Bu özellik, bir dosya veya klasör için dosya tutamacını alma ve IndexedDB veritabanında depolama olanağı sunuyordu. En önemlisi, bu herkese açık kullanıcı adı uygulama oturumları arasında kalır. Böylece kullanıcı, uygulamayı yeniden başlattığında dosyayı veya klasörü tekrar seçmek zorunda kalmaz (ancak kısa bir izin istemini yanıtlaması gerekir). Üretime ulaştığında File System Access API olarak yeniden adlandırıldı ve WHATWG tarafından standartlaştırılan temel parçalar File System API (FSA) olarak adlandırıldı.

Peki API'nin File System Access bölümü nasıl çalışır? Dikkat edilmesi gereken birkaç önemli nokta:

  • Eşzamansız bir API'dir (Web İşçileri'ndeki özel işlevler hariç).
  • Dosya veya dizin seçicileri, kullanıcı hareketi (bir kullanıcı arayüzü öğesini tıklama veya öğeye dokunma) yakalanarak programatik olarak başlatılmalıdır.
  • Kullanıcının daha önce seçilmiş bir dosyaya (yeni bir oturumda) erişmek için tekrar izin vermesi için kullanıcı hareketi de gerekir. Aslında tarayıcı, kullanıcı hareketi tarafından başlatılmazsa izin istemini göstermeyi reddeder.

Dosya ve dizin tutamaçlarını depolamak için hantal IndexedDB API'yi kullanmak dışında kod nispeten basittir. Neyse ki browser-fs-access gibi, işin büyük kısmını sizin yerinize yapan birkaç kitaplık var. Kiwix JS'de, çok iyi belgelenmiş API'lerle doğrudan çalışmaya karar verdik.

Dosya ve dizin seçicileri açma

Dosya seçici açmak şöyle görünür (Burada Promises kullanılmaktadır ancak async/await sugar'ı tercih ediyorsanız Chrome for Developers eğitim videosuna bakın):

return window
  .showOpenFilePicker({ multiple: false })
  .then(function (fileHandles) {
    return processFileHandle(fileHandles[0]);
  })
  .catch(function (err) {
    // This is normal if app is launching
    console.warn(
      'User cancelled, or cannot access fs without user gesture',
      err,
    );
  });

Basitlik açısından bu kodun yalnızca ilk seçilen dosyayı işlediğini (ve birden fazla dosya seçmeyi yasakladığını) unutmayın. { multiple: true } ile birden fazla dosya seçmeye izin vermek istiyorsanız her bir imleci işleyen tüm Promise'leri bir Promise.all().then(...) ifadesine sarmalamak yeterlidir. Örneğin:

let promisesForFiles = fileHandles.map(function (fileHandle) {
    return processFileHandle(fileHandle);
});
return Promise.all(promisesForFiles).then(function (arrayOfFiles) {
    // Do something with the files array
    console.log(arrayOfFiles);
}).catch(function (err) {
    // Handle any errors that occurred during processing
    console.error('Error processing file handles!', err);
)};

Ancak, özellikle Kiwix kullanıcıları tüm ZIM dosyalarını aynı dizinde düzenleme eğiliminde olduğundan, birden fazla dosyayı seçmek için kullanıcıdan bu dosyaları içeren dizini seçmesini istemek daha iyi bir seçenek olabilir. Dizin seçiciyi başlatma kodu, window.showDirectoryPicker.then(function (dirHandle) { … }); kullanmanız dışında yukarıdaki kodla neredeyse aynıdır.

Dosya veya dizin tanıtıcısı işleniyor

Herkese açık kullanıcı adını aldıktan sonra işlemeniz gerekir. Bu nedenle, processFileHandle işlevi şu şekilde görünebilir:

function processFileHandle(fileHandle) {
  // Serialize fileHandle to indexedDB
  serializeFSHandletoIdxDB('pickedFSHandle', fileHandle, function (val) {
    console.debug('IndexedDB responded with ' + val);
  });
  return fileHandle.getFile().then(function (file) {
    // Do something with the file
    return file;
  });
}

Dosya tutamacını depolayacak işlevi sağlamanız gerektiğini unutmayın. Bir soyutlama kitaplığı kullanmadığınız sürece bunun için kolay yöntemler yoktur. Kiwix'in bunu uygulaması cache.js dosyasında görülebilir, ancak sadece bir dosya veya klasör tutma yerini depolamak ve almak için kullanılırsa süreç önemli ölçüde basitleştirilebilir.

İstediğiniz dosyaları veya dosya türlerini bulmak için seçili dizindeki girişleri eşzamansız entries.next() ile iterasyonla incelemeniz gerektiğinden dizinleri işleme biraz daha karmaşıktır. Bunu yapmanın çeşitli yolları vardır ancak Kiwix PWA'da kullanılan kod şu şekildedir:

let iterableEntryList = dirHandle.entries();
return iterateAsyncDirEntries(iterableEntryList, []).then(function (entryList) {
  // Do something with the entry list
  return entryList;
});

/**
 * Iterates FileSystemDirectoryHandle iterator and adds entries to an array
 * @param {Iterator} entries An asynchronous iterator of entries
 * @param {Array} archives An array to which to add the entries (may be empty)
 * @return {Promise<Array>} A Promise for an array of entries in the directory
 */
function iterateAsyncDirEntries(entries, archives) {
  return entries
    .next()
    .then(function (result) {
      if (!result.done) {
        let entry = result.value[1];
        // Filter for the files you want
        if (/\.zim(\w\w)?$/i.test(entry.name)) {
          archives.push(entry);
        }
        return iterateAsyncDirEntryArray(entries, archives);
      } else {
        // We've processed all the entries
        if (!archives.length) {
          console.warn('No archives found in the picked directory!');
        }
        return archives;
      }
    })
    .catch(function (err) {
      console.error('There was an error processing the directory!', err);
    });
}

entryList içindeki her giriş için, daha sonra dosyayı kullanmanız gerektiğinde entry.getFile().then(function (file) { … }) veya async function içinde const file = await entry.getFile() kullanarak eşdeğerini almanız gerekeceğini unutmayın.

Daha fazla bilgi verebilir misiniz?

Uygulamanın sonraki açılışlarında kullanıcı hareketi ile başlatılan izin verme şartı, dosya ve klasör (yeniden) açma işlemine biraz zorluk getirse de yine de bir dosyayı yeniden seçmeye zorlanmaktan çok daha akıcıdır. Chromium geliştiricileri, şu anda yüklü PWA'lar için kalıcı izinlere olanak tanıyacak kodları sonlandırmaktadır. Bu, birçok PWA geliştiricisinin aradığı ve heyecanla beklenen bir şey.

Peki, beklemek zorunda kalmazsak ne olacak? Kiwix geliştiricileri kısa süre önce, hem Chromium hem de Firefox tarayıcıları tarafından desteklenen (ve Safari tarafından kısmen desteklenen ancak FileSystemWritableFileStream eksik olan) File Access API'nin yeni ve parlak bir özelliğini kullanarak şu anda tüm izin istemlerini ortadan kaldırmanın mümkün olduğunu keşfetti. Bu yeni özellik Origin Private File System'dir.

Tamamen yerele geçiş: Origin Private File System

Origin Private File System (OPFS), Kiwix PWA'da hâlâ deneysel bir özelliktir ancak ekip, yerel uygulamalar ile web uygulamaları arasındaki boşluğu büyük ölçüde kapattığı için kullanıcıları denemeye teşvik etmekten heyecan duyuyor. Temel avantajları şunlardır:

  • OPFS'deki arşivlere, ilk kullanımda bile izin istemi gösterilmeden erişilebilir. Kullanıcılar, bir makaleyi okumaya ve bir arşive göz atmaya önceki oturumda kaldıkları yerden devam edebilir.
  • Dosyada depolanan dosyalara ileri düzeyde optimize erişim sağlar: Android'de hızda beş ila on kat daha hızlı iyileşme görüyoruz.

Android'de File API kullanılarak yapılan standart dosya erişimi, özellikle büyük arşivler cihaz depolama alanında değil de microSD kartta depolanıyorsa (Kiwix kullanıcıları için genellikle geçerlidir) son derece yavaştır. Bu yeni API ile tüm bunlar değişiyor. Çoğu kullanıcı 97 GB'lık bir dosyayı OPFS'de depolayamasa da (microSD kart depolama alanını değil, cihaz depolama alanını kullanır) küçük ve orta ölçekli arşivleri depolamak için mükemmel bir seçenektir. WikiProject Medicine'den en kapsamlı tıbbi ansiklopediyi mi istiyorsunuz? Sorun değil, 1,7 GB boyutuyla OPFS'ye kolayca uygundur. (İpucu: Uygulama içi kitaplıkta diğermdwiki_tr_all_maxi dosyasını bulun.)

OPFS nasıl çalışır?

OPFS, tarayıcı tarafından sağlanan ve her kaynak için ayrı olan, Android'deki uygulama kapsamlı depolamaya benzer bir dosya sistemidir. Dosyalar, kullanıcı tarafından görülebilen dosya sisteminden OPFS'ye aktarılabilir veya doğrudan OPFS'ye indirilebilir (API, OPFS'de dosya oluşturmaya da olanak tanır). OPFS'ye girdikten sonra cihazın geri kalanından izole edilirler. Masaüstü Chromium tabanlı tarayıcılarda, dosyaları OPFS'den kullanıcının görebildiği dosya sistemine geri aktarmak da mümkündür.

OPFS'yi kullanmak için ilk adım, navigator.storage.getDirectory() kullanarak erişim istemektir (await kullanan kodu görmek isterseniz Kaynak Özel Dosya Sistemi başlıklı makaleyi okuyun):

return navigator.storage
  .getDirectory()
  .then(function (handle) {
    return processDirHandle(handle);
  })
  .catch(function (err) {
    console.warn('Unable to get the OPFS directory entry', err);
  });

Buradan aldığınız herkese açık kullanıcı adı, yukarıda bahsedilen window.showDirectoryPicker() kaynağından aldığınız FileSystemDirectoryHandle ile aynı türdedir. Bu, bu kullanıcı adını işleyen kodu yeniden kullanabileceğiniz anlamına gelir (ve neyse ki bunu indexedDB içinde saklamanıza gerek yoktur. İhtiyacınız olduğunda bu kullanıcı adını alabilirsiniz). OPFS'de halihazırda bazı dosyalarınız olduğunu ve bunları kullanmak istediğinizi varsayalım. Daha önce gösterilen iterateAsyncDirEntries() işlevini kullanarak aşağıdaki gibi bir işlem yapabilirsiniz:

return navigator.storage.getDirectory().then(function (dirHandle) {
  let entries = dirHandle.entries();
  return iterateAsyncDirEntries(entries, [])
    .then(function (archiveList) {
      return archiveList;
    })
    .catch(function (err) {
      console.error('Unable to iterate OPFS entries', err);
    });
});

archiveList dizisinden çalışmak istediğiniz herhangi bir girişte getFile() kullanmanız gerektiğini unutmayın.

OPFS'ye dosya aktarma

Peki, dosyaları OPFS'ye nasıl aktarabilirsiniz? O kadar da kolay değil. Öncelikle, kullanmanız gereken depolama alanı miktarını tahmin etmeniz ve kullanıcıların sığmayacaksa 97 GB'lık bir dosya yüklememeye dikkat etmeniz gerekir.

Tahmini kotayı kolayca öğrenebilirsiniz: navigator.storage.estimate().then(function (estimate) { … });. Bunu kullanıcıya nasıl göstereceğinizi belirlemek biraz daha zordur. Kiwix uygulamasında, onay kutusunun hemen yanında görünen ve kullanıcıların OPFS'yi deneyebileceği küçük bir uygulama içi panel kullanmayı tercih ettik:

Kullanılan depolama alanını yüzde olarak, kalan depolama alanını ise gigabayt cinsinden gösteren panel.

Panel, estimate.quota ve estimate.usage kullanılarak doldurulur. Örneğin:

let OPFSQuota; // Global variable, so we don't have to keep checking it
return navigator.storage.estimate().then(function (estimate) {
  const percent = ((estimate.usage / estimate.quota) * 100).toFixed(2);
  OPFSQuota = estimate.quota - estimate.usage;
  document.getElementById('OPFSQuota').innerHTML =
    '<b>OPFS storage quota:</b><br />Used:&nbsp;<b>' +
    percent +
    '%</b>; ' +
    'Remaining:&nbsp;<b>' +
    (OPFSQuota / 1024 / 1024 / 1024).toFixed(2) +
    '&nbsp;GB</b>';
});

Gördüğünüz gibi, kullanıcıların görebildiği dosya sisteminden OPFS'ye dosya eklemesine olanak tanıyan bir düğme de vardır. Ancak iyi bir haberimiz var. İçe aktarılacak gerekli Dosya nesnesini (veya nesnelerini) almak için File API'yi kullanabilirsiniz. Aslında, window.showOpenFilePicker() yöntemi Firefox tarafından desteklenmediğinden OPFS kesinlikle desteklendiğinden bu yöntemin kullanılmaması önemlidir.

Yukarıdaki ekran görüntüsünde gördüğünüz görünür Dosya ekle düğmesi eski bir dosya seçici değildir ancak tıklandığında veya dokunulduğunda gizli bir eski seçici (<input type="file" multiple … /> öğesi) click() ekler. Uygulama daha sonra gizli dosya girişinin change etkinliğini yakalar, dosyaların boyutunu kontrol eder ve kota için çok büyüklerse bunları reddeder. Her şey yolundaysa kullanıcıya bu kişileri eklemek isteyip istemediğini sorun:

archiveFilesLegacy.addEventListener('change', function (files) {
  const filesArray = Array.from(files.target.files);
  // Abort if user didn't select any files
  if (filesArray.length === 0) return;
  // Calculate the size of the picked files
  let filesSize = 0;
  filesArray.forEach(function (file) {
    filesSize += file.size;
  });
  // Check the size of the files does not exceed the quota
  if (filesSize > OPFSQuota) {
    // Oh no, files are too big! Tell user...
    console.log('Files would exceed the OPFS quota!');
  } else {
    // Ask user if they're sure... if user said yes...
    return importOPFSEntries(filesArray)
      .then(function () {
        // Tell user we successfully imported the archives
      })
      .catch(function (err) {
        // Tell user there was an error (error catching is important!)
      });
  }
});

Kullanıcıdan, kaynak özel dosya sistemine .zim dosyalarının listesini eklemek isteyip istemediğini soran iletişim kutusu.

Android gibi bazı işletim sistemlerinde arşivleri içe aktarmak en hızlı işlem olmadığından Kiwix, arşivler içe aktarılırken bir banner ve küçük bir döner simge gösterir. Ekip, bu işlem için ilerleme göstergesi eklemeyi çözemedi. Bu sorunu çözerseniz lütfen yanıtları posta kartına yazıp gönderin.

Peki Kiwix, importOPFSEntries() işlevini nasıl uyguladı? Bu, her dosyanın OPFS'ye etkili bir şekilde aktarılmasına olanak tanıyan fileHandle.createWriteable() yönteminin kullanılmasını içerir. Tüm zor işler tarayıcı tarafından halledilir. (Kiwix, eski kod tabanımızla ilgili nedenlerden dolayı burada Promises'i kullanıyor ancak bu durumda await'ün daha basit bir söz dizimi oluşturduğu ve "kader piramidi" etkisini önlediği söylenmelidir.)

function importOPFSEntries(files) {
  // Get a handle on the OPFS directory
  return navigator.storage
    .getDirectory()
    .then(function (dir) {
      // Collect the promises for each file that we want to write
      let promises = files.map(function (file) {
        // Create the file and get a writeable handle on it
        return dir
          .getFileHandle(file.name, { create: true })
          .then(function (fileHandle) {
            // Get a writer for the file
            return fileHandle.createWritable().then(function (writer) {
              // Show a banner / spinner, then write the file
              return writer
                .write(file)
                .then(function () {
                  // Finished with this writer
                  return writer.close();
                })
                .catch(function (err) {
                  console.error('There was an error writing to the OPFS!', err);
                });
            });
          })
          .catch(function (err) {
            console.error('Unable to get file handle from OPFS!', err);
          });
      });
      // Return a promise that resolves when all the files have been written
      return Promise.all(promises);
    })
    .catch(function (err) {
      console.error('Unable to get a handle on the OPFS directory!', err);
    });
}

Doğrudan OPFS'ye dosya akışı indirme

Bunun bir varyasyonu, bir dosyayı internetten doğrudan OPFS'ye veya dizin adı olan herhangi bir dizine (yani window.showDirectoryPicker() ile seçilen dizinlere) aktarma olanağıdır. Yukarıdaki kodla aynı ilkeleri kullanır ancak ReadableStream ve uzak dosyadan okunan baytları sıraya ekleyen bir denetleyiciden oluşan bir Response oluşturur. Elde edilen Response.body, OPFS içindeki yeni dosyanın yazıcısına yönlendirilir.

Bu durumda Kiwix, ReadableStream üzerinden geçen baytları sayabilir ve böylece kullanıcıya bir ilerleme göstergesi sunabilir ve indirme sırasında uygulamadan çıkmamaları konusunda onları uyarabilir. Kod burada gösterilemeyecek kadar karmaşıktır ancak uygulamamız açık kaynak bir uygulama olduğundan benzer bir şey yapmak istiyorsanız kaynak koduna bakabilirsiniz. Kiwix kullanıcı arayüzü aşağıdaki gibi görünür (Aşağıda gösterilen farklı ilerleme değerleri, banner'ın yalnızca yüzde değiştiğinde güncellenmesi, ancak İndirme ilerleme durumu panelinin daha düzenli olarak güncellenmesi nedeniyledir):

Kiwix kullanıcı arayüzünde, kullanıcının uygulamadan çıkmaması gerektiğini belirten bir çubuk yer alıyor ve .zim arşivinin indirme ilerleme durumu gösteriliyor.

İndirme işlemi oldukça uzun sürebileceğinden Kiwix, kullanıcıların işlem sırasında uygulamayı özgürce kullanmasına izin verir ancak indirme işlemi tamamlanana kadar kullanıcıların uygulamayı kapatmamaları gerektiğini hatırlatmak için banner'ın her zaman gösterilmesini sağlar.

Uygulama içinde bir mini dosya yöneticisi kullanma

Bu noktada Kiwix PWA geliştiricileri, OPFS'ye dosya eklemenin yeterli olmadığını fark etti. Uygulamanın, kullanıcılara bu depolama alanında artık ihtiyaç duymadıkları dosyaları silmelerinin yanı sıra ideal olarak OPFS'de kilitli olan tüm dosyaları kullanıcının görebileceği dosya sistemine aktarmalarına olanak tanıması da gerekiyordu. Verimli bir şekilde, uygulama içinde mini bir dosya yönetim sistemi uygulamak gerekli hale geldi.

Chrome için olağanüstü OPFS Explorer uzantısını (Edge'de de çalışır) kullanıma sunuyoruz. Geliştirici araçlarına, OPFS'de tam olarak ne olduğunu görmenizi ve ayrıca kötü amaçlı veya başarısız dosyaları silmenizi sağlayan bir sekme ekler. Bu, kodun çalışıp çalışmadığını kontrol etmek, indirmelerin davranışını izlemek ve genel olarak geliştirme denemelerimizi temizlemek için çok değerliydi.

Dosya dışa aktarma, Kiwix'in dışa aktarılan dosyayı kaydedeceği seçili bir dosya veya dizinde dosya tutamacını alma olanağına bağlıdır. Bu nedenle, yalnızca window.showSaveFilePicker() yönteminin kullanılabildiği bağlamlarda çalışır. Kiwix dosyaları birkaç GB'tan küçük olsaydı bellekte bir blob oluşturabilir, ona bir URL verebilir ve ardından kullanıcının görebileceği dosya sistemine indirebilirdik. Maalesef bu kadar büyük arşivlerde bu mümkün değildir. Destekleniyorsa dışa aktarma işlemi oldukça basittir: Bir dosyayı OPFS'ye kaydetme işleminin ters çevrilmiş hali gibidir (kaydedilecek dosyanın adını alın, kullanıcıdan window.showSaveFilePicker() ile dosyayı kaydedeceği bir yer seçmesini isteyin, ardından saveHandle üzerinde createWriteable()'yi kullanın). Kodu repoda görebilirsiniz.

Dosya silme işlemi tüm tarayıcılar tarafından desteklenir ve basit bir dirHandle.removeEntry('filename') ile yapılabilir. Kiwix'in durumunda, OPFS girişlerini yukarıda yaptığımız gibi yinelemeyi tercih ettik. Böylece önce seçilen dosyanın var olup olmadığını kontrol edip onay isteyebiliriz, ancak bu herkes için gerekli olmayabilir. Dilerseniz kodumuzu inceleyebilirsiniz.

Kiwix kullanıcı arayüzünü bu seçenekleri sunan düğmelerle karıştırmamaya ve bunun yerine doğrudan arşiv listesinin altına küçük simgeler yerleştirmemeye karar verdik. Bu simgelerden birine dokunduğunuzda arşiv listesinin rengi değişir. Bu, kullanıcıya ne yapacağına dair görsel bir ipucu verir. Kullanıcı arşivlerden birini tıklar veya arşive dokunur. Ardından, ilgili işlem (dışa aktarma veya silme) gerçekleştirilir (onayın ardından).

Kullanıcıya, .zim dosyasını silmek isteyip istemediğini soran iletişim kutusu.

Son olarak, yukarıda bahsedilen tüm dosya yönetimi özelliklerinin (OPFS'ye dosya ekleme, dosyayı doğrudan indirme, dosyayı silme ve kullanıcı tarafından görülebilen dosya sistemine dışa aktarma) ekran kaydı demosunu burada bulabilirsiniz.

Geliştiricilerin işi hiçbir zaman bitmez

OPFS, PWA geliştiricileri için harika bir yeniliktir. Yerel uygulamalar ile web uygulamaları arasındaki boşluğu kapatmaya yardımcı olan gerçekten güçlü dosya yönetimi özellikleri sunar. Ancak geliştiriciler asla memnun olmaz. OPFS neredeyse mükemmel, ancak tam olarak değil. Ana özelliklerin hem Chromium hem de Firefox tarayıcılarında çalışması ve masaüstünde olduğu kadar Android'de de uygulanması harika. Özellik setinin tamamının yakında Safari ve iOS'te de kullanıma sunulmasını umuyoruz. Aşağıdaki sorunlar devam etmektedir:

  • Firefox, şu anda temel disk alanı ne kadar olursa olsun OPFS kotasına 10 GB sınırı uygulamaktadır. Bu, çoğu PWA yazarı için yeterli olsa da Kiwix için oldukça kısıtlayıcıdır. Neyse ki Chromium tarayıcıları çok daha cömert.
  • window.showSaveFilePicker() uygulanmadığı için büyük dosyaları OPFS'den mobil tarayıcılarda veya masaüstü Firefox'ta kullanıcı tarafından görülebilen dosya sistemine aktarmak şu anda mümkün değildir. Bu tarayıcılarda büyük dosyalar etkili bir şekilde OPFS'de sıkışır. Bu, Kiwix'in içeriğe açık erişim ve arşivleri özellikle de internet bağlantısı kesintili veya pahalı olan bölgelerde kullanıcılar arasında paylaşma ilkelerine aykırıdır.
  • Kullanıcılar, OPFS sanal dosya sisteminin hangi depolama alanını tüketeceğini kontrol edemez. Bu durum özellikle kullanıcıların microSD kartta çok fazla, cihaz depolama alanında ise çok az alana sahip olabileceği mobil cihazlarda soruna yol açabilir.

Ancak genel olarak bu sorunlar, PWA'larda dosya erişimi için büyük bir adım olan bu özellikte küçük sorunlardır. Kiwix PWA ekibi, Dosya Sistemi Erişimi API'sini ilk kez öneren ve tasarlayan Chromium geliştiricileri ve destekçilerine, ayrıca Kaynak Özel Dosya Sistemi'nin önemi konusunda tarayıcı tedarikçileri arasında fikir birliğine varmak için gösterilen yoğun çabaya çok teşekkür eder. Kiwix JS PWA için, geçmişte uygulamayı yavaşlatan kullanıcı deneyimi sorunlarının büyük bir kısmını çözdü ve Kiwix içeriğinin herkes için erişilebilirliğini artırma hedefimizde bize yardımcı oldu. Lütfen Kiwix PWA'yı deneyin ve geliştiricilere fikrinizi bildirin.

PWA özellikleriyle ilgili muhteşem kaynaklar için şu sitelere göz atın: