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

Sol tarafta plastik bir sandalye bulunan basit bir masanın üzerinde duran bir dizüstü bilgisayarın etrafında toplanmış insanlar. Arka planda, gelişmekte olan bir ülkedeki bir okul görülü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ışı olarak kullanabilmeleri için Progressive Web Uygulaması teknolojisini ve Dosya Sistemi Erişimi API'sini 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 ve gelecekte yapılacak olası geliştirmeler ele alınmaktadı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 mı bitiyor? 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 çalışmada, modern tarayıcıya sahip her cihaz için evrensel ve basit bir çözüm olmayı amaçlayan Progresif Web Uygulaması (PWA) üzerinde durulacak.

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ı?

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 ilham alan bazı Kiwix geliştiricileri, yaklaşık 10 yıl önce Kiwix yazılımını C++'dan JavaScript'e taşımaya karar verdi.

Bu bağlantının ilk sürümü olan Kiwix HTML5, artık kullanımda olmayan 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 adlandırılan tarayıcı uzantıları hâlâ aktif olarak geliştirilmektedir.

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 hafiftir ve bu nedenle, mobil internetin kesintili veya pahalı olduğu bağlamlar için mükemmeldir. 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. Bu API'ler, 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 PC'ye indirip mobil cihaza veya tablete aktarıyor. Bazıları da, mobil internetin zayıf veya pahalı olduğu bölgelerde USB çubukları ya da taşınabilir sabit disklerde içerik alışverişi yapıyor. 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çilen giriş öğesi, temel olarak depolama alanındaki temel verilere referans veren meta veriler olan dosya nesnelerini barındırır. 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'lerini kullanarak dosya sistemine tam erişim de dahil olmak üzere güçlü özellikler sunan muhteşem bir çerçevedir. Ancak bu yöntemin bazı bilinen dezavantajları vardır:

  • Yalnızca masaüstü işletim sistemlerinde çalışır.
  • Dosya büyük ve ağırsa (70-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 karşılaştırıldığı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:

  • Bu, eşzamansız bir API'dir (Web İşleyicilerdeki ö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çilmesini 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ının tüm ZIM dosyalarını aynı dizinde düzenleme eğiliminde olması nedeniyle, 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 tutamacını işleme

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 bu uygulamasını cache.js dosyasında görebilirsiniz. Ancak yalnızca bir dosya veya klasör adını depolamak ve almak için kullanılıyorsa bu uygulama ö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 iterasyonunuz 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, dosyayı daha sonra kullanmanız gerektiğinde entry.getFile().then(function (file) { … }) ile veya async function içinde const file = await entry.getFile() kullanarak eşdeğerini almanız gerektiğ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 kodu sonlandırıyorlar. Bu, birçok PWA geliştiricisinin talep ettiği ve merakla beklenen bir özelliktir.

Ama beklememiz gerekmiyorsa ne olur? 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.
  • Bu depolama alanı, içinde saklanan dosyalara son derece optimize edilmiş erişim sağlar. Android'de hız iyileştirmeleri beş ila on kat daha hızlıdır.

Android'de File API'yi kullanarak 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) çok yavaştır. Bu yeni API ile tüm bunlar değişiyor. Çoğu kullanıcı, OPFS'de 97 GB'lık bir dosya depolayamaz (OPFS, microSD kart depolama alanını değil cihaz depolama alanını tüketir). Ancak OPFS, küçük ve orta boy arşivleri depolamak için mükemmeldir. WikiProject Medicine'den en kapsamlı tıbbi ansiklopediyi mi istiyorsunuz? Sorun değil, 1,7 GB olduğu için OPFS'ye kolayca sığar. (İpucu: Uygulama içi kitaplıkta diğermdwiki_tr_all_maxi dosyasını bulun.)

OPFS'nin işleyiş şekli

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. Chromium tabanlı masaüstü tarayıcılarında, 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 girişlerde getFile() kullanmaya devam etmeniz gerektiğini unutmayın.

Dosyaları OPFS'ye 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ı almak kolaydır: 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 kullanıcıların OPFS'yi denemesine olanak tanıyan küçük bir uygulama içi panel 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 kullanıcı tarafından görülebilen dosya sisteminden OPFS'ye dosya eklemesine olanak tanıyan bir düğme de vardır. İçe aktarılacak gerekli dosya nesnesini (veya nesnelerini) almak için File API'yi kullanabilirsiniz. Aslında, bu yöntem Firefox tarafından desteklenmediğinden window.showOpenFilePicker()'yi kullanmamak önemlidir. Öte yandan OPFS kesinlikle desteklenir.

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 click() gizli bir eski seçiciyi (<input type="file" multiple … /> öğesi) gösterir. 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 aktarma işlemi en hızlı işlem olmadığından Kiwix, arşivler içe aktarılırken bir banner ve küçük bir dönen simge de gösterir. Ekip, bu işlem için ilerleme göstergesi eklemeyi çözemedi. Çözerseniz lütfen yanıtları posta kartına yazıp gönderin.

Peki Kiwix, importOPFSEntries() işlevini nasıl uyguladı? Bu işlem, 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 piramitleri" etkisini önlediği de belirtilmelidir.)

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);
    });
}

Dosya akışını doğrudan OPFS'ye 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):

Alt kısmında kullanıcıyı uygulamadan çıkmaması konusunda uyaran ve .zim arşivinin indirme ilerleme durumunu gösteren bir çubuğun bulunduğu Kiwix kullanıcı arayüzü.

İ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çi mini dosya yöneticisi uygulama

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 dosyaları kullanıcının görebileceği dosya sistemine aktarmalarına da olanak tanıması gerekiyordu. Bu nedenle, uygulamaya küçük bir dosya yönetim sistemi uygulamak gerekiyordu.

Chrome için muhteşem OPFS Explorer uzantısını (Edge'de de çalışır) da buradan önermek isteriz. Geliştirici araçlarına, OPFS'de tam olarak ne olduğunu görmenize ve ayrıca kötü amaçlı veya başarısız dosyaları silmenize olanak tanıyan 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'te, seçilen dosyanın önce var olup olmadığını kontrol edip onay istemek için OPFS girişlerini yukarıda yaptığımız gibi yinelemeyi tercih ettik. Ancak bu işlem herkes için gerekli olmayabilir. Dilerseniz kodumuzu inceleyebilirsiniz.

Kiwix kullanıcı arayüzünü bu seçenekleri sunan düğmelerle doldurmamak ve bunun yerine doğrudan arşiv listesinin altına küçük simgeler yerleştirmek kararlaştırıldı. Bu simgelerden birine dokunulduğunda, arşiv listesinin rengi değişir. Bu, kullanıcıya ne yapacağını gösteren görsel bir ipucu olur. Ardından kullanıcı, arşivlerden birini tıklar veya ona dokunur ve ilgili işlem (dışa aktarma veya silme) gerçekleştirilir (onay alındıktan sonra).

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ı yakında Safari ve iOS'te de kullanıma sunmayı 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 felsefesine aykırıdır.
  • Kullanıcılar, OPFS sanal dosya sisteminin hangi depolama alanını kullanacağını 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 bazı mükemmel kaynaklar için şu sitelere göz atın: