Ücretsiz APK oluştur

JavaScript köprü API'si

Dosyalar: her seferinde bir dosya

Bir dizeye sığmayacak kadar büyük dosyalar için sistem seçicisi ve parçalı ikili okuma/yazma.

AppMint.downloadFile yardımcı#

AppMint.downloadFile(data, fileName, mimeType?)

Örnek

Sayfanızın oluşturduğu bir dosyayı - base64 dizesinden, bir data: URL'sinden, bir blob: URL'sinden veya bir Blob'dan - seçtiğiniz dosya adıyla kaydeder. Android'in "Farklı kaydet…" sayfası bu ad doldurulmuş olarak açılır.

Döndürür: (eşzamanlı) kayıt Android'e devredildiğinde true (false yalnızca ayrıştıramadığı bir data: URL'si için). data bir Blob veya dize değilse TypeError fırlatır. Klasörü yine kullanıcı seçer ve iptal edebilir. Gerekir: hiçbir şey - uygulamada her zaman mevcuttur.

Base64 dizesi ve bir MIME türü:

function exportCsv(rows) {
  const csv = rows.map(function (r) { return r.join(','); }).join('\n');
  const b64 = btoa(unescape(encodeURIComponent(csv)));          // UTF-8 safe
  if (window.AppMint && typeof AppMint.downloadFile === 'function') {
    AppMint.downloadFile(b64, 'Ledger_Q3.csv', 'text/csv');
  } else {
    const a = document.createElement('a');                       // a normal browser
    a.href = 'data:text/csv;base64,' + b64;
    a.download = 'Ledger_Q3.csv';
    a.click();
  }
}

Bir data: URL'si (MIME türü URL'den gelir) veya bir Blob:

const png = document.querySelector('canvas').toDataURL('image/png');
AppMint.downloadFile(png, 'Chart.png');

const blob = new Blob([JSON.stringify(state)], { type: 'application/json' });
AppMint.downloadFile(blob, 'Backup.json');                        // same as AppMint.saveBlob

Sunucunuzun JSON içinde döndürdüğü base64 bir PDF; uzantısı olmayan bir ada MIME türünden .pdf eklenir:

async function downloadInvoice(id) {
  const res = await fetch('/api/invoices/' + id + '?format=base64');
  const body = await res.json();                                  // { base64: 'JVBERi0x…' }
  if (!window.AppMint || typeof AppMint.downloadFile !== 'function') {
    location.href = 'data:application/pdf;base64,' + body.base64;
    return;
  }
  AppMint.downloadFile(body.base64, 'Invoice_' + id, 'application/pdf');   // saved as Invoice_<id>.pdf
}

Ekrandaki bir önizleme için zaten oluşturduğunuz bir blob: URL'si:

// earlier: preview.src = URL.createObjectURL(editedImageBlob);
document.getElementById('save-photo').addEventListener('click', function () {
  const preview = document.getElementById('preview');
  if (window.AppMint && typeof AppMint.downloadFile === 'function') {
    AppMint.downloadFile(preview.src, 'Edited_Photo.jpg');
  }
});

Notlar: fileName değerinin yalnızca son bölümü kullanılır (klasör yok). Uzantısı olmayan bir ada MIME türünden bir uzantı eklenir. Üçüncü argüman yalnızca düz bir base64 dizesi için kullanılır. Telefonda hiç "Farklı kaydet…" ekranı yoksa dosya doğrudan İndirilenler'e yazılır. Normal <a download> kodunuz da çalışır - bkz. downloads.

AppMint.saveBlob yardımcı#

AppMint.saveBlob(blob, fileName)

Örnek

Bir Blob'u (veya File'ı) seçtiğiniz adla kaydeder. Android'in "Farklı kaydet…" sayfası bu ad doldurulmuş olarak açılır.

Döndürür: (eşzamanlı) kayıt Android'e devredildiğinde true. İlk argüman bir Blob değilse TypeError fırlatır. Gerekir: hiçbir şey - uygulamada her zaman mevcuttur.

async function saveReport() {
  const blob = await makePdfBlob();                    // e.g. jsPDF: doc.output('blob')
  if (window.AppMint && typeof AppMint.saveBlob === 'function') {
    AppMint.saveBlob(blob, 'Site_Report_2026-08.pdf');
    return;
  }
  const url = URL.createObjectURL(blob);               // a normal browser
  const a = document.createElement('a');
  a.href = url;
  a.download = 'Site_Report_2026-08.pdf';
  a.click();
  setTimeout(function () { URL.revokeObjectURL(url); }, 10000);
}

Bir File kendi adını getirir, bu yüzden ad belirtilmeyebilir:

const file = new File(['name,score\n'], 'Scores.csv', { type: 'text/csv' });
AppMint.saveBlob(file);            // saved as Scores.csv

Sunucunuzdan, oturum açma başlığı gerektiren ve seçtiğiniz bir adla kaydedilen bir dosya:

async function saveStatement(month, token) {
  const res = await fetch('/api/statements/' + month, {
    headers: { Authorization: 'Bearer ' + token }
  });
  if (!res.ok) { alert('Could not fetch the statement (' + res.status + ')'); return; }
  const blob = await res.blob();
  if (window.AppMint && typeof AppMint.saveBlob === 'function') {
    AppMint.saveBlob(blob, 'Statement_' + month + '.pdf');
  }
}

Tek bir ZIP'te toplanan (burada JSZip ile) ve tek seferde kaydedilen birkaç dosya:

async function exportAll(notes) {
  const zip = new JSZip();
  notes.forEach(function (n) { zip.file(n.title + '.txt', n.body); });
  zip.file('index.json', JSON.stringify(notes.map(function (n) { return n.title; })));
  const blob = await zip.generateAsync({ type: 'blob' });
  if (window.AppMint && typeof AppMint.saveBlob === 'function') {
    AppMint.saveBlob(blob, 'Notes_' + new Date().toISOString().slice(0, 10) + '.zip');
  }
}

Notlar: Blob belleΔe okunur ve Android'e tek parΓ§a halinde gΓΆnderilir; bu yΓΌzden Γ§ok bΓΌyΓΌk blob'lar (yΓΌzlerce MB) belleΔi tΓΌketebilir. KlasΓΆrΓΌ kullanΔ±cΔ± seΓ§er ve vazgeΓ§ebilir; hangisinin olduΔunu sayfaya hiΓ§bir Εey sΓΆylemez.

AppMint.setDownloadName yardımcı#

AppMint.setDownloadName(url, fileName)

Örnek

Kodunuz (veya bir kütüphane) indirmeyi tetiklemeden önce bir blob: veya data: URL'sine kaydedileceği dosya adını verir.

Döndürür: hiçbir şey. Gerekir: hiçbir şey - uygulamada her zaman mevcuttur.

Bir kütüphane daha önce oluşturduğunuz bir URL'yi indirdiğinde ve ona ad veremediğinizde kullanışlıdır:

const blob = new Blob([csvText], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
if (window.AppMint && typeof AppMint.setDownloadName === 'function') {
  AppMint.setDownloadName(url, 'Invoice_0042.csv');
}
location.href = url;           // saved as Invoice_0042.csv, not "download.csv"

Sayfanızın window.open ile açtığı bir PDF telefonun PDF görüntüleyicisine gider; "download.pdf" olmasın diye önce ona bir ad verin:

function previewPdf(pdfBlob, invoiceNo) {
  const url = URL.createObjectURL(pdfBlob);
  if (window.AppMint && typeof AppMint.setDownloadName === 'function') {
    AppMint.setDownloadName(url, 'Invoice_' + invoiceNo + '.pdf');
  }
  window.open(url, '_blank');
}

Bir data: URL'si de aynı şekilde çalışır; örneğin bir kütüphanenin download özniteliği olmadan bağlantı verdiği bir grafik resmi:

const dataUrl = chart.toBase64Image();         // e.g. Chart.js: 'data:image/png;base64,…'
if (window.AppMint && typeof AppMint.setDownloadName === 'function') {
  AppMint.setDownloadName(dataUrl, 'Sales_by_month.png');
}
const link = document.getElementById('chart-link');
link.href = dataUrl;                           // a tap on this link saves Sales_by_month.png
link.textContent = 'Save chart';

Notlar: Bazı kütüphaneler kayıt bitmeden önce iptal ettiği için, ad URL.revokeObjectURL(url) sonrasında bile hatırlanır. Buna nadiren ihtiyaç duyarsınız: <a download="name">, URL.createObjectURL(file) (bir File bir ada sahiptir) ve FileSaver'ın saveAs(blob, name) çağrısı otomatik olarak yakalanır. 'download' adı "ad yok" sayılarak yok sayılır.

WebToApkFS.maxChunkSize yardımcı#

WebToApkFS.maxChunkSize()

Örnek

Tek bir readBytes veya writeBytes çağrısının taşıyabileceği en fazla baytı (4 MB) söyler; böylece parça döngünüzün boyutunu ayarlayabilirsiniz.

DΓΆner deΔer: (eΕzamanlΔ±) bayt sayΔ±sΔ± ya da kΓΆprΓΌ yΓΆntemi eksikse 0. Gerekenler: derleme yaparken AdΔ±m 4'te (EriΕim) Yerel KlasΓΆr EriΕimi (SAF) kutusunu iΕaretleyin (bunu enjekte eden budur): WebToApkFS).

function copyFile(fromUri, toPath) {
  if (!window.WebToApkFS) return { ok: false, error: 'not in the app' };
  const step = WebToApkFS.maxChunkSize() || 1048576;   // fall back to 1 MB pieces
  let offset = 0;
  for (;;) {
    const r = WebToApkFS.readBytes(fromUri, offset, step);
    if (!r.ok) return r;
    if (r.bytesRead > 0) {
      const w = WebToApkFS.writeBytes(toPath, r.base64, offset, offset === 0 ? 'truncate' : 'patch');
      if (!w.ok) return w;
    }
    offset += r.bytesRead;
    if (r.eof || r.bytesRead <= 0) return { ok: true, size: offset };
  }
}

Okuma ilerlemesini gΓΆsterin: parΓ§a boyutu bir dosyanΔ±n kaΓ§ adΔ±m sΓΌrdΓΌΔΓΌnΓΌ sΓΆyler:

function readWithProgress(uri, onProgress) {
  const info = WebToApkFS.stat(uri);
  if (!info.ok) return info;
  const step = WebToApkFS.maxChunkSize() || 1048576;
  const steps = Math.max(1, Math.ceil(info.size / step));
  for (let i = 0; i < steps; i++) {
    const r = WebToApkFS.readBytes(uri, i * step, step);
    if (!r.ok) return r;
    onProgress(Math.round(((i + 1) / steps) * 100), r.base64);
  }
  return { ok: true, size: info.size };
}

İzin verilen klasördeki bir dosyaya büyük bir bayt dizisini, her seferinde izin verilen bir parça olarak ekleyin:

function appendBytes(path, bytes) {                    // bytes: a Uint8Array
  const step = WebToApkFS.maxChunkSize();
  if (!step) return { ok: false, error: 'not in the app' };
  for (let at = 0; at < bytes.length; at += step) {
    const piece = bytes.subarray(at, at + step);
    let bin = '';
    for (let i = 0; i < piece.length; i += 0x8000) bin += String.fromCharCode.apply(null, piece.subarray(i, i + 0x8000));
    const w = WebToApkFS.writeBytes(path, btoa(bin), 0, 'append');   // 'append' ignores the offset
    if (!w.ok) return w;
  }
  return { ok: true };
}

Notlar: Daha küçük bir parça sayfayı daha duyarlı tutar (her çağrı eşzamanlıdır); sınır yalnızca bir çağrının ne kadar büyük olabileceğini söyler.

WebToApkFS.pickFile yardımcı#

WebToApkFS.pickFile(mimeFilter)

Örnek

Android dosya seçicisini açar, böylece kullanıcı BİR dosya seçer; sayfanız sonra o dosyanın gerçek baytlarını okuyup yazabilir, uygulama yeniden başlasa bile.

Döndürür: Promise<{ok, uri, name, size, mime, error}>. Başarıda ok true olur ve uri, stat, readBytes, writeBytes işlevlerine verdiğiniz bir content:// adresidir. Hatada ok false olur ve error 'cancelled', 'disabled', 'no-picker', 'busy' (daha önceki bir çağrı için zaten bir seçici açık, o kendi yanıtını korur), 'foreign_origin' veya 'unsupported' (uygulama yok) olur. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

async function openOneFile() {
  if (!window.WebToApkFS) { alert('Open this page in the app to pick a file.'); return; }
  const f = await WebToApkFS.pickFile('application/pdf');   // '' = any file, 'image/*' = pictures
  if (!f.ok) {
    if (f.error !== 'cancelled') alert('Cannot open a file: ' + f.error);
    return;
  }
  console.log(f.name, f.size + ' bytes', f.mime);
  localStorage.setItem('lastFile', f.uri);   // the grant survives a restart
}

Kaydedilen uri değerini kullanıcıya ikinci kez sormadan daha sonra yeniden kullanın:

const uri = localStorage.getItem('lastFile');
if (uri && window.WebToApkFS) {
  const info = WebToApkFS.stat(uri);
  if (info.ok) console.log('still here:', info.name);
  else console.log('file is gone or access was removed:', info.error);
}

Filtre TEK bir desen alır; bu yüzden geniş seçin ve adı kendiniz kontrol edin:

async function importTable() {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('text/*');        // CSV and TSV files are both text/*
  if (!f.ok) {
    if (f.error === 'busy') return;                     // a picker is already open
    if (f.error !== 'cancelled') alert('Cannot open a file: ' + f.error);
    return;
  }
  if (!/\.(csv|tsv)$/i.test(f.name)) {
    WebToApkFS.releaseFile(f.uri);
    alert(f.name + ' is not a CSV or TSV file.');
    return;
  }
  const r = WebToApkFS.readAll(f.uri);
  if (r.ok) showTable(atob(r.base64), f.name.toLowerCase().endsWith('.tsv') ? '\t' : ',');
  WebToApkFS.releaseFile(f.uri);
}

Notlar: Filtre bir liste değil, tek bir MIME türü desenidir ('image/*', 'text/csv'). Seçici okuma VE yazma erişimi ister ve bunu korur. Android, bir uygulamanın bu yolla kaç dosyayı tutabileceğini sınırlar; bu yüzden bir dosyayla işiniz bittiğinde WebToApkFS.releaseFile(uri) çağırın. Tüm bir klasör için bunun yerine WebToApkFS.requestAccess() kullanın. WebToApkFS, Yerel Klasör Erişimi veya Güvenli Anahtarlar işaretlendiğinde yüklenir; çağrıları yalnızca Yerel Klasör Erişimi ile çalışır (WebToApkFS.isAvailable()).

WebToApkFS.readAll yardımcı#

WebToApkFS.readAll(pathOrUri)

Örnek

Bir dosyanın tamamını tek bir base64 dizesine okur. Sizin yerinize readBytes üzerinde, her seferinde 4 MB olacak şekilde döngü yapar.

Döndürür: (eşzamanlı) {ok:true, base64, size} veya başarısız olan ilk parçanın {ok:false, error} değeri. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

Seçilen bir resmi şuraya yükleyin <img>:

async function showPicture() {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('image/*');
  if (!f.ok) return;
  const r = WebToApkFS.readAll(f.uri);
  if (!r.ok) { alert('Read failed: ' + r.error); return; }
  document.getElementById('photo').src = 'data:' + f.mime + ';base64,' + r.base64;
}

UTF-8 bir metin dosyası okuyun (yalnızca İngilizce değil, her dilde çalışır):

function readUtf8(pathOrUri) {
  const r = WebToApkFS.readAll(pathOrUri);
  if (!r.ok) throw new Error(r.error);
  const bytes = Uint8Array.from(atob(r.base64), function (c) { return c.charCodeAt(0); });
  return new TextDecoder('utf-8').decode(bytes);
}

Bir dosyanın SHA-256 sağlama toplamını gösterin; örneğin bir indirmeyi yayımlananla karşılaştırmak için:

async function checksum() {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('');
  if (!f.ok) return;
  const r = WebToApkFS.readAll(f.uri);
  if (!r.ok) { alert('Read failed: ' + r.error); return; }
  const bytes = Uint8Array.from(atob(r.base64), function (c) { return c.charCodeAt(0); });
  const hash = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));
  const hex = Array.from(hash, function (b) { return b.toString(16).padStart(2, '0'); }).join('');
  console.log(f.name, r.size + ' bytes', hex);
}

Notlar: Dosyanın tamamı belleğe alınır (önce bayt, sonra base64 olarak) ve çağrı çalışırken sayfayı bloke eder. Belleğe rahatça sığan dosyalar için kullanın; büyük dosyalar için WebToApkFS.readBytes üzerinde döngü yapın ve her parçayı işleyin. İzin verilen klasördeki düz metin için WebToApkFS.readText(path) daha basittir.

WebToApkFS.readBytes yardımcı#

WebToApkFS.readBytes(pathOrUri, offset, length)

Örnek

Bir dosyanın gerçek baytlarını, bir bayt konumundan başlayarak base64 olarak okur. Resimler, PDF'ler, ZIP'ler için güvenlidir - metin kod çözme hiçbir şeyi değiştirmez.

Döndürür: (eşzamanlı) {ok:true, base64, bytesRead, offset, size, eof} veya {ok:false, error} ('not-found', 'disabled', 'cannot-open', 'offset past end of file (N bytes)', 'foreign_origin', 'unsupported'). Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

İlk 64 KB'ı okuyun; örneğin bir dosya başlığını kontrol etmek için:

async function checkPng() {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('image/*');
  if (!f.ok) return;
  const r = WebToApkFS.readBytes(f.uri, 0, 65536);
  if (!r.ok) { alert('Read failed: ' + r.error); return; }
  const head = atob(r.base64).slice(1, 4);
  console.log(head === 'PNG' ? 'a PNG' : 'not a PNG', '- file is', r.size, 'bytes');
}

Büyük bir dosyayı parça parça okuyun; böylece bir anda belleğe sığması gerekmez:

function eachChunk(uri, onChunk) {
  const step = WebToApkFS.maxChunkSize() || 1048576;
  let offset = 0;
  for (;;) {
    const r = WebToApkFS.readBytes(uri, offset, step);
    if (!r.ok) return r;                 // stop on the first error
    onChunk(r.base64, offset);
    offset += r.bytesRead;
    if (r.eof || r.bytesRead <= 0) return { ok: true, size: offset };
  }
}

Bir dosyanın yalnızca sonunu okuyun; örneğin bir MP3'ün sonundaki 128 baytlık etiket:

function readMp3Title(uri) {
  const info = WebToApkFS.stat(uri);
  if (!info.ok || info.size < 128) return null;
  const r = WebToApkFS.readBytes(uri, info.size - 128, 128);
  if (!r.ok) return null;
  const tail = atob(r.base64);
  if (tail.slice(0, 3) !== 'TAG') return null;          // no old-style tag in this file
  return tail.slice(3, 33).replace(/\0+$/, '').trim();  // bytes 3-32 hold the title
}

Notlar: Bir çağrı en fazla WebToApkFS.maxChunkSize() bayt (4 MB) okur; length 0 "bir çağrının izin verdiği kadar" demektir. bytesRead üzerinde döngü yapın ve eof olunca durun. Çağrı eşzamanlıdır, bu yüzden UI iş parçacığında parçaları makul tutun. Küçük dosyalar için WebToApkFS.readAll(uri) döngüyü sizin yerinize yapar.

WebToApkFS.readSealed yardımcı#

WebToApkFS.readSealed(pathOrUri, alias)

Örnek

WebToApkFS.writeSealed tarafından yazılan bir dosyayı okur ve aynı anahtar deposu anahtarıyla şifresini çözer.

DΓΆner deΔer: (eΕzamanlΔ±) {ok:true, base64} (orijinal baytlar) veya {ok:false, error}: 'keystore-disabled', 'corrupt-sealed-file', bir anahtar deposu kodu (anahtar silindikten sonra 'no-such-key', 'auth-required', 'key-invalidated') ya da bir dosya hatasΔ± ('not-found' β¦). Gerekenler: derleme yaparken AdΔ±m 4'te (EriΕim) Yerel KlasΓΆr EriΕimi (SAF) ve GΓΌvenli Anahtarlar (Εifreleme) kutularΔ±nΔ± iΕaretleyin.

function loadSecret() {
  if (!window.WebToApkFS || !window.AppMintKeystore || !AppMintKeystore.isAvailable()) return null;
  const out = WebToApkFS.readSealed('secret.bin', 'vault');
  if (out.ok) {
    const bytes = Uint8Array.from(atob(out.base64), function (c) { return c.charCodeAt(0); });
    return new TextDecoder().decode(bytes);
  }
  if (out.error === 'auth-required') {
    alert('Unlock your phone, then try again.');
  } else if (out.error === 'no-such-key') {
    alert('This note was deleted.');
  } else {
    alert('Could not open the note: ' + out.error);
  }
  return null;
}

Uygulama ayarlarını mühürlü bir JSON dosyasında tutun, ilk kayıttan önce varsayılanlarla:

function loadSettings() {
  const defaults = { theme: 'light', pinHint: '' };
  const out = WebToApkFS.readSealed('settings.sealed', 'settings');
  if (!out.ok) {
    if (out.error === 'not-found') return defaults;      // nothing saved yet
    throw new Error('Settings could not be opened: ' + out.error);
  }
  const bytes = Uint8Array.from(atob(out.base64), function (c) { return c.charCodeAt(0); });
  return Object.assign(defaults, JSON.parse(new TextDecoder().decode(bytes)));
}

Mühürlü bir dosyayı yeni bir anahtara taşıyın, sonra eski anahtarı yok edin:

function rekey(path, oldAlias, newAlias) {
  const plain = WebToApkFS.readSealed(path, oldAlias);
  if (!plain.ok) return plain;
  if (!AppMintKeystore.hasKey(newAlias)) {
    const k = AppMintKeystore.generateKey(newAlias, {});
    if (!k.ok) return k;
  }
  const w = WebToApkFS.writeSealed(path, plain.base64, newAlias);
  if (!w.ok) return w;
  AppMintKeystore.deleteKey(oldAlias);                  // only after the new copy is written
  return { ok: true };
}

Notlar: AppMintKeystore.deleteKey(alias) Γ§alΔ±ΕtΔ±ktan sonra kalΔ±cΔ± olarak baΕarΔ±sΔ±z olur; asΔ±l amaΓ§ budur. DosyanΔ±n tamamΔ± belleΔe okunur ve eΕzamanlΔ± olarak Εifresi Γ§ΓΆzΓΌlΓΌr; mΓΌhΓΌrlΓΌ dosyalarΔ± kΓΌΓ§ΓΌk tutun (notlar, anahtarlar, belirteΓ§ler).

WebToApkFS.releaseFile yardımcı#

WebToApkFS.releaseFile(uri)

Örnek

Kullanıcı bir dosyayı şununla seçtiğinde uygulamanızın aldığı uzun vadeli erişimi geri verir WebToApkFS.pickFile.

Döndürür: (eşzamanlı) izin bırakıldığında true, aksi halde false (izin yok, geçersiz URI, uygulamada değil). Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

async function importOnce() {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('text/csv');
  if (!f.ok) return;
  const r = WebToApkFS.readAll(f.uri);
  if (r.ok) importCsv(atob(r.base64));
  WebToApkFS.releaseFile(f.uri);       // done with it: do not keep the grant
}

Kullanıcı yeni bir varsayılan dosya seçtiğinde, eskisinin iznini geri verin:

async function chooseDefaultFile() {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('');
  if (!f.ok) return;
  const old = localStorage.getItem('defaultFile');
  if (old && old !== f.uri) WebToApkFS.releaseFile(old);
  localStorage.setItem('defaultFile', f.uri);
}

Uygulamanın hatırladığı her dosyanın iznini bırakan bir "Son dosyaları unut" düğmesi:

function forgetRecentFiles() {
  const recent = JSON.parse(localStorage.getItem('recentFiles') || '[]');
  let released = 0;
  for (const uri of recent) {
    if (WebToApkFS.releaseFile(uri)) released++;       // false: already gone or never granted
  }
  localStorage.removeItem('recentFiles');
  console.log('released', released, 'of', recent.length);
}

Notlar: Android, bir uygulamanın erişimini sürdürebileceği seçilmiş dosya sayısını sınırlar. Çok sayıda dosya seçen bir uygulama (içe aktarıcı, görüntüleyici) her birini işi bitince bırakmalıdır. Bıraktıktan sonra o URI üzerindeki stat / readBytes / writeBytes çağrıları not-found yanıtını verir. Dosyayı silmez.

WebToApkFS.stat yardımcı#

WebToApkFS.stat(pathOrUri)

Örnek

Bir dosyanın adını, boyutunu, türünü ve tarihini içeriğini okumadan söyler.

Döndürür: (eşzamanlı) {ok:true, name, size, mime, lastModified, uri, isDirectory, canWrite} veya error = 'not-found', 'disabled', 'foreign_origin' ya da 'unsupported' ile {ok:false, error}. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

async function showInfo() {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('');
  if (!f.ok) return;
  const info = WebToApkFS.stat(f.uri);
  if (!info.ok) { alert('Cannot read file info: ' + info.error); return; }
  console.log(info.name, info.size + ' bytes', info.mime,
              new Date(info.lastModified).toLocaleString(),
              info.canWrite ? 'writable' : 'read only');
}

WebToApkFS.requestAccess() ile izin verdiΔiniz klasΓΆrΓΌn iΓ§indeki bir yol da Γ§alΔ±ΕΔ±r:

const info = WebToApkFS.stat('photos/cover.jpg');
if (info.ok && !info.isDirectory) console.log('cover is', info.size, 'bytes');

Hatırlanan bir dosyayı yalnızca son içe aktarımdan beri değiştiyse yeniden içe aktarın:

function importIfChanged(uri) {
  const info = WebToApkFS.stat(uri);
  if (!info.ok) return 'gone: ' + info.error;
  const seen = JSON.parse(localStorage.getItem('lastImport') || '{}');
  if (seen.size === info.size && seen.lastModified === info.lastModified) return 'unchanged';
  const r = WebToApkFS.readAll(uri);
  if (!r.ok) return 'read failed: ' + r.error;
  importData(atob(r.base64));
  localStorage.setItem('lastImport', JSON.stringify({ size: info.size, lastModified: info.lastModified }));
  return 'imported';
}

Notlar: pathOrUri, pickFile() ile alınan bir content:// URI'si veya izin verilen klasöre göreli bir yoldur. Uygulamanıza hiç verilmemiş bir URI not-found döndürür. lastModified, 1970'ten beri geçen milisaniyedir (sağlayıcı belirtmiyorsa 0).

WebToApkFS.writeAll yardımcı#

WebToApkFS.writeAll(pathOrUri, base64)

Örnek

Bir dosyanın tüm içeriğini sizin baytlarınızla değiştirir. Veriyi sizin için 4 MB'lık parçalara böler: ilk parça dosyayı kısaltır, geri kalanlar ondan sonra yamalanır.

Döndürür: (eşzamanlı) {ok:true, bytesWritten} veya başarısız olan ilk parçanın {ok:false, error} değeri. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

UTF-8 metni (her dil) kullanıcının seçtiği bir dosyaya geri kaydedin:

function bytesToBase64(bytes) {
  let s = '';
  for (let i = 0; i < bytes.length; i += 0x8000) {
    s += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
  }
  return btoa(s);
}

async function saveNote(text) {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('text/plain');
  if (!f.ok) return;
  const r = WebToApkFS.writeAll(f.uri, bytesToBase64(new TextEncoder().encode(text)));
  if (r.ok) console.log('saved', r.bytesWritten, 'bytes');
  else alert('Could not save: ' + r.error);
}

Bir canvas görüntüsünü izin verilen klasöre yazın (dosya yoksa oluşturulur):

const b64 = document.querySelector('canvas').toDataURL('image/png').split(',')[1];
const r = WebToApkFS.writeAll('drawings/sketch.png', b64);
if (!r.ok) alert('Could not save: ' + r.error);

Δ°zin verilen klasΓΆre bir JSON yedeΔi kaydedin; klasΓΆr yoksa ΓΆnce isteyin, sonra diskteki boyutunu doΔrulayΔ±n:

async function backup(data) {
  if (!window.WebToApkFS || !WebToApkFS.isAvailable()) return;
  if (!WebToApkFS.hasAccess()) {
    const a = await WebToApkFS.requestAccess();
    if (!a.success) return;
  }
  const bytes = new TextEncoder().encode(JSON.stringify(data));
  let bin = '';
  for (let i = 0; i < bytes.length; i += 0x8000) bin += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
  const r = WebToApkFS.writeAll('backup.json', btoa(bin));
  if (!r.ok) { alert('Backup failed: ' + r.error); return; }
  const info = WebToApkFS.stat('backup.json');
  console.log('backup is', info.ok ? info.size : '?', 'bytes');
}

Notlar: Yazılırken tüm girdi bellekte durur ve çağrı sayfayı bloke eder. Sonraki bir parça başarısız olursa dosyada önceki parçalar zaten bulunur - ok değerini kontrol edin ve kullanıcıya bildirin. Bir dosyayı gizli tutmak için bunun yerine WebToApkFS.writeSealed kullanın.

WebToApkFS.writeBytes yardımcı#

WebToApkFS.writeBytes(pathOrUri, base64, offset, mode)

Örnek

Bir dosyaya gerçek baytlar yazar ve kaç tanesinin yazıldığını söyler. Ortadaki baytları yamalayabilir, tüm dosyayı değiştirebilir veya sona ekleyebilir.

Döndürür: (eşzamanlı) {ok, bytesWritten, offset, size} - size, veriler diske yazıldıktan sonra dosyadan geri okunur - veya {ok:false, error}. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

Patch - 100. konumdaki 4 baytı değiştirin, dosyanın geri kalanını koruyun:

const r = WebToApkFS.writeBytes(fileUri, btoa('ABCD'), 100, 'patch');
if (r.ok) console.log('file is now', r.size, 'bytes');
else alert('Write failed: ' + r.error);

Truncate - tüm dosyayı değiştirir (offset yok sayılır):

const csv = 'name,score\nAsha,12\n';
const r = WebToApkFS.writeBytes('exports/scores.csv', btoa(csv), 0, 'truncate');
if (!r.ok) alert('Could not save: ' + r.error);

Append - sona ekler (offset yok sayılır):

const line = new Date().toISOString() + ' opened\n';
WebToApkFS.writeBytes('log.txt', btoa(line), 0, 'append');

Notlar: mode, 'patch' (varsayılan), 'truncate' veya 'append' olabilir. Çağrı başına en fazla 4 MB (WebToApkFS.maxChunkSize()); daha büyük veri için WebToApkFS.writeAll kullanın. İzin verilen klasördeki bir yol yoksa oluşturulur; seçilen bir content:// URI'si kullanıcının seçtiği biri olmalıdır. btoa yalnızca Latin-1 metnini kabul eder - diğer metinler için önce baytlara kodlayın (bkz. WebToApkFS.writeAll). Hatalar: disabled, foreign_origin (ekrandaki sayfa uygulamanın kendi sayfası değil), bad mode: x, bad base64, chunk too large (max N bytes), not-found, cannot-open, short write.

WebToApkFS.writeSealed yardımcı#

WebToApkFS.writeSealed(pathOrUri, base64, alias)

Örnek

AppMintKeystore içinden donanım destekli bir anahtarla şifrelenmiş bir dosya yazar. Bu anahtarı sonradan silmek dosyayı herkes için okunamaz hale getirir - telefondaki tek gerçek "güvenli silme".

Döndürür: (eşzamanlı) {ok:true, bytesWritten} (diskteki şifreli boyut) veya {ok:false, error} - 'keystore-disabled', 'no-such-key', 'auth-required', 'key-invalidated' gibi bir anahtar deposu kodu ya da bir dosya hatası. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) ve Güvenli Anahtarlar (şifreleme) seçeneklerini işaretleyin.

function saveSecret(text) {
  if (!window.WebToApkFS || !window.AppMintKeystore || !AppMintKeystore.isAvailable()) {
    alert('Secure storage is not available in this build.');
    return false;
  }
  if (!AppMintKeystore.hasKey('vault')) {
    const k = AppMintKeystore.generateKey('vault', {});
    if (!k.ok) { alert('Could not create key: ' + k.error); return false; }
  }
  const bytes = new TextEncoder().encode(text);
  let bin = '';
  for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
  const r = WebToApkFS.writeSealed('secret.bin', btoa(bin), 'vault');
  if (!r.ok) alert('Could not save: ' + r.error);
  return r.ok;
}

Gerçekten silin - anahtarı yok edin, sonra dosyayı kaldırın:

AppMintKeystore.deleteKey('vault');   // secret.bin can never be decrypted again
WebToApkFS.delete('secret.bin');

Kullanıcının seçtiği bir fotoğrafı ham bayt olarak, verdiği izinle erişilen klasöre mühürleyin:

async function sealPhoto(file) {                  // a File from <input type="file" accept="image/*">
  if (!window.WebToApkFS || !window.AppMintKeystore || !AppMintKeystore.isAvailable()) return false;
  if (!WebToApkFS.hasAccess()) {
    const g = await WebToApkFS.requestAccess();
    if (!g.success) return false;
  }
  if (!AppMintKeystore.hasKey('vault')) AppMintKeystore.generateKey('vault', {});
  const dataUrl = await new Promise(function (resolve, reject) {
    const fr = new FileReader();
    fr.onload = function () { resolve(fr.result); };
    fr.onerror = reject;
    fr.readAsDataURL(file);
  });
  const b64 = dataUrl.slice(dataUrl.indexOf(',') + 1);
  const r = WebToApkFS.writeSealed('photo-' + Date.now() + '.sealed', b64, 'vault');
  if (!r.ok) alert('Not saved: ' + r.error);
  return r.ok;
}

Telefonun kilidi açıldıktan sonra yalnızca 5 dakika çalışan bir günlük anahtarı, her hata açıklanmış olarak:

if (!AppMintKeystore.hasKey('diary')) {
  AppMintKeystore.generateKey('diary', { requireAuth: true, authValiditySeconds: 300 });
}

function saveDiary(entry) {
  const b64 = btoa(unescape(encodeURIComponent(JSON.stringify(entry))));   // UTF-8 safe
  const r = WebToApkFS.writeSealed('diary.bin', b64, 'diary');
  if (r.ok) return true;
  if (r.error === 'auth-required') showMessage('Unlock your phone again, then tap Save.');
  else if (r.error === 'key-invalidated') showMessage('Your screen lock changed, so this key no longer works.');
  else showMessage('Could not save: ' + r.error);
  return false;
}

Notlar: Silmek için "sıfırlarla üzerine yazma" yapmayın: telefonun flash depolaması eski blokları tutar. Dosya biçimi, uzunluk ön ekli AES-GCM parçalarının bir listesidir; yalnızca WebToApkFS.readSealed ve aynı takma adla geri okuyun. {requireAuth:true} ile oluşturulan bir anahtar, kullanıcı yakın zamanda kilidi açana kadar auth-required yanıtını verir.

downloads web standardı#

<a download> and Blob downloads

Örnek

Normal web indirme kodunuz uygulamada Chrome'daki gibi Γ§alΔ±ΕΔ±r. Bir WebView blob: ve data: indirmelerini kendiliΔinden yok sayar; uygulama onlarΔ± yakalar, dosyayΔ± telefonun Δ°ndirilenler klasΓΆrΓΌne sizin dosya adΔ±nΔ±zla kaydeder, sonra "<ad> indirildi Β· AΓ§" mesajΔ±nΔ± ve bir "Δ°ndirme tamamlandΔ±" bildirimini gΓΆsterir.

Döndürür: sayfaya hiçbir şey. Gerekir: açılacak bir şey yok. Web adresi indirmeleri (https://…) de İndirilenler klasörüne gider (bir PDF veya resim önce "Görüntüle veya İndir" diye sorar); uygulamanın içine paketlenmiş dosyalar da (<a href="manual.pdf" download>) öyle. Android 9 ve eskisinde uygulama önce depolama izni ister. Büyük dosyalar sorun değildir: dosya uygulamaya parçalar halinde aktarılır.

<a download> bir Blob ile - en yaygın desen:

<button id="save">Download report</button>
<script>
document.getElementById('save').addEventListener('click', function () {
  const blob = new Blob(['Name,Total\nAsha,120\n'], { type: 'text/csv' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'Report_2026-08.csv';     // this is the name the user sees
  document.body.appendChild(a);
  a.click();                             // caught by the app, saved to Downloads
  a.remove();
});
</script>

Bir data: URL'si, FileSaver.js ve navigator.msSaveBlob da çalışır:

const a = document.createElement('a');
a.href = canvas.toDataURL('image/png');
a.download = 'Drawing.png';
a.click();

if (typeof saveAs === 'function') saveAs(blob, 'Backup.json');   // FileSaver.js
if (navigator.msSaveBlob) navigator.msSaveBlob(blob, 'Backup.json');

window.open bir blob: veya data: URL'sinin anlamΔ± "gΓΆster"dir, "kaydet" deΔil: bir PDF uygulamanΔ±n PDF gΓΆrΓΌntΓΌleyicisinde, diΔer tΓΌrler o tΓΌr iΓ§in telefonun uygulamasΔ±nda aΓ§Δ±lΔ±r:

const pdfUrl = URL.createObjectURL(pdfBlob);
const w = window.open(pdfUrl);    // never null in the app, so no "popup blocked" message

Notlar: Ad olmadan dosya download.<ext> olarak kaydedilir; aynı adlı ikinci bir dosya name (1).ext olur. Bir File nesnesi kendi adını getirir (URL.createObjectURL(file)). Kendi sitenizdeki <a href="/files/x" download="Name.pdf">, Name.pdf adını korur (Chrome özniteliği başka sitelerin bağlantılarında yok sayar, uygulama da öyle). Telefonda bir window.open dosyasını gösterebilecek uygulama yoksa bunun yerine İndirilenler'e kaydedilir. Kullanıcının klasörü seçmesine izin vermek için showSaveFilePicker() kullanın. Bir <a> öğesi olmadan kaydetmek için AppMint.saveBlob(blob, name) veya AppMint.downloadFile(base64, name, mime).

input-file web standardı#

<input type="file"> — camera, photos and files

Örnek

Normal bir dosya girdisi uygulamada Chrome'daki gibi çalışır: fotoğraf seçiciyi, kamerayı veya dosya seçiciyi açar ve sayfanız sıradan File nesneleri alır.

Döndürür: girdinin change olayı ve input.files (kullanıcı iptal ettiğinde boş). Gerekir: fotoğraflar ve dosyalar için bir şey gerekmez. capture (kamera) için derleme yaparken Adım 4'te (Erişim) Kamera seçeneğini işaretleyin; uygulama kamera iznini ilk seferde ister.

Fotoğraflar - Android fotoğraf seçicisini açar (izin istemi yok; sayfa yalnızca kullanıcının seçtiğini alır):

<input type="file" id="pics" accept="image/*" multiple>
<div id="preview"></div>
<script>
document.getElementById('pics').addEventListener('change', function (e) {
  const box = document.getElementById('preview');
  for (let i = 0; i < e.target.files.length; i++) {
    const img = document.createElement('img');
    img.src = URL.createObjectURL(e.target.files[i]);
    img.style.width = '100px';
    box.appendChild(img);
  }
});
</script>

Kamera - capture seçiciyi atlar ve kamerayı doğrudan açar:

<input type="file" id="snap" accept="image/*" capture="environment">
<input type="file" id="clip" accept="video/*" capture="environment">
<script>
document.getElementById('snap').addEventListener('change', function (e) {
  const photo = e.target.files[0];
  if (!photo) return;                        // user closed the camera
  const form = new FormData();
  form.append('photo', photo);
  fetch('/api/upload', { method: 'POST', body: form });
});
</script>

Herhangi bir dosya veya birkaΓ§ tΓΌr (uzantΔ±lar da Γ§alΔ±ΕΔ±r):

<input type="file" accept=".pdf,.csv,application/json" multiple>
Siz yazarsınızNe açılır
accept="image/*" / "video/*" / "image/*,video/*"fotoğraf seçici (fotoğraflar, videolar veya her ikisi)
ekle multiplekullanıcı birkaç tane seçebilir
capture ekleyin (Kamera işaretliyken)doğrudan kamera - image/* için fotoğraf, video için video/*
accept="image/*,.pdf" veya hayır acceptsistem dosya seçicisi

Notlar: Kamera işaretli değilse capture yok sayılır ve seçici açılır. Yerel Klasör Erişimi (SAF) işaretliyse uygulama bunun yerine kendi "Yükleme Kaynağı Seç" menüsünü gösterir: Fotoğraf Çek / Video Kaydet (Kamera işaretliyse), Dosya Seç ve Klasör Yükle (bir klasördeki her dosyayı gönderir). Android 12 ve eskisinde Depolama işaretliyken fotoğraf olmayan bir dosya seçmek önce depolama izni isteyebilir. Fotoğraf seçicisi olmayan telefonlar aynı fotoğrafları gösteren dosya seçiciyi alır.

showOpenFilePicker web standardı#

showOpenFilePicker / showSaveFilePicker / showDirectoryPicker — File System Access

Örnek

Standart File System Access API, Android'in kendi belge seçicisi üzerinden uygulamanın içinde çalışır: bir dosya açın ve o DOSYANIN İÇİNE geri kaydedin, Farklı kaydet sayfasıyla bir dosya oluşturun veya dosyaları okuyup oluşturabileceğiniz ve silebileceğiniz tüm bir klasör alın.

Döndürür: showOpenFilePicker(options) → FileSystemFileHandle dizisinin Promise'i; showSaveFilePicker(options) → bir FileSystemFileHandle; showDirectoryPicker(options) → bir FileSystemDirectoryHandle. İptal edilen bir seçici AbortError ile reddedilir; kullanıcı hareketi yoksa SecurityError ile reddedilir; biri açıkken ikinci bir seçici NotAllowedError ile reddedilir. Gerekir: açılacak bir şey yok ve izin gerekmez - kullanıcı seçer.

let handle = null;

document.getElementById('open').addEventListener('click', async () => {
  try {
    [handle] = await showOpenFilePicker({
      types: [{ description: 'Text', accept: { 'text/plain': ['.txt', '.md'] } }]
    });
    const file = await handle.getFile();
    editor.value = await file.text();
    title.textContent = file.name;
  } catch (e) {
    if (e.name !== 'AbortError') alert('Could not open: ' + e.message);
  }
});

document.getElementById('save').addEventListener('click', async () => {
  if (!handle) handle = await showSaveFilePicker({ suggestedName: 'note.txt' });
  const w = await handle.createWritable();   // writes go to a copy…
  await w.write(editor.value);
  await w.close();                           // …that replaces the file only here
});

document.getElementById('export').addEventListener('click', async () => {
  const dir = await showDirectoryPicker({ mode: 'readwrite' });
  const out = await dir.getFileHandle('export.csv', { create: true });
  const w = await out.createWritable();
  await w.write(csvText);
  await w.close();
  for await (const [name, entry] of dir.entries()) console.log(entry.kind, name);
});

Birden fazla fotoğrafı aynı anda seçin, Resimler klasöründen başlayarak:

document.getElementById('add-photos').addEventListener('click', async () => {
  try {
    const handles = await showOpenFilePicker({
      multiple: true,
      startIn: 'pictures',
      types: [{ description: 'Images', accept: { 'image/*': ['.jpg', '.jpeg', '.png', '.webp'] } }]
    });
    for (const h of handles) {
      const img = document.createElement('img');
      img.src = URL.createObjectURL(await h.getFile());
      gallery.appendChild(img);
    }
  } catch (e) {
    if (e.name !== 'AbortError') alert('Could not open: ' + e.message);
  }
});

Zaten var olanı yeniden yazmadan bir günlük dosyasının sonuna bir satır ekleyin:

async function appendLine(handle, line) {      // handle from showOpenFilePicker / showSaveFilePicker
  const size = (await handle.getFile()).size;
  const w = await handle.createWritable({ keepExistingData: true });
  await w.seek(size);
  await w.write(line + '\n');
  await w.close();
}

Yedekleme klasörünü BİR KEZ seçin ve sonraki her açılışta otomatik olarak içine kaydedin - tutamacı tıpkı Chrome'da olduğu gibi IndexedDB'de saklayın. Uygulama klasörün iznini yeniden başlatmalar boyunca korur; bu yüzden queryPermission() 'granted' yanıtını verir ve yedekleme için dokunmaya ya da seçiciye gerek kalmaz:

// Tiny IndexedDB helpers (idb-keyval's get/set work the same way).
function db() {
  return new Promise((ok, fail) => {
    const r = indexedDB.open('backup-settings', 1);
    r.onupgradeneeded = () => r.result.createObjectStore('kv');
    r.onsuccess = () => ok(r.result);
    r.onerror = () => fail(r.error);
  });
}
async function kvGet(key) {
  const d = await db();
  return new Promise((ok, fail) => {
    const r = d.transaction('kv').objectStore('kv').get(key);
    r.onsuccess = () => ok(r.result);
    r.onerror = () => fail(r.error);
  });
}
async function kvSet(key, value) {
  const d = await db();
  return new Promise((ok, fail) => {
    const t = d.transaction('kv', 'readwrite');
    t.objectStore('kv').put(value, key);
    t.oncomplete = () => ok();
    t.onerror = () => fail(t.error);
  });
}

// "Choose backup folder" button (the picker needs a tap).
document.getElementById('choose-folder').addEventListener('click', async () => {
  try {
    const dir = await showDirectoryPicker({ mode: 'readwrite', startIn: 'documents' });
    await kvSet('backupDir', dir);
    status.textContent = 'Backups go to ' + dir.name;
  } catch (e) {
    if (e.name !== 'AbortError') status.textContent = 'Could not choose: ' + e.message;
  }
});

// Called after every workout, and on a timer: no tap needed once the folder is chosen.
async function autoBackup(data) {
  const dir = await kvGet('backupDir');
  if (!dir) return 'no folder chosen yet';
  if ((await dir.queryPermission({ mode: 'readwrite' })) !== 'granted') {
    return 'folder access was removed: tap "Choose backup folder" again';
  }
  const file = await dir.getFileHandle('backup-' + new Date().toISOString().slice(0, 10) + '.json', { create: true });
  const w = await file.createWritable();
  await w.write(JSON.stringify(data));
  await w.close();
  return 'saved ' + file.name;
}

İzin kaldırılmışsa (kullanıcı uygulamanın erişimini temizledi veya klasörü sildi), queryPermission() 'prompt' yanıtını verir. Bir düğme dokunuşundan requestPermission() klasör seçiciyi O klasörde yeniden açar; aynı klasörü seçmek 'granted' yanıtını verir, başka bir şey (veya iptal) 'denied':

document.getElementById('reconnect').addEventListener('click', async () => {
  const dir = await kvGet('backupDir');
  if (dir && (await dir.requestPermission({ mode: 'readwrite' })) === 'granted') {
    status.textContent = 'Backups go to ' + dir.name + ' again';
  } else {
    status.textContent = 'Choose a backup folder';
  }
});

Kullanıcının seçtiği bir klasörü temizleyin: eski .tmp dosyalarını ve adlı alt klasörleri silin cache:

document.getElementById('clean').addEventListener('click', async () => {
  const dir = await showDirectoryPicker({ mode: 'readwrite' });
  let removed = 0;
  for await (const entry of dir.values()) {
    if (entry.kind === 'file' && entry.name.endsWith('.tmp')) {
      await dir.removeEntry(entry.name);
      removed++;
    } else if (entry.kind === 'directory' && entry.name === 'cache') {
      await dir.removeEntry(entry.name, { recursive: true });
      removed++;
    }
  }
  alert(removed + ' item(s) removed');
});

Notlar: createWritable(), spesifikasyonun takas dosyası modelini izler - abort() (veya sayfanın kapanması) orijinale dokunmaz; { keepExistingData: true } mevcut içerikten başlar; write({type:'seek'|'truncate'|'write', …}), seek() ve truncate() çalışır. Uygulamasının yalnızca okunabilir sunduğu bir dosya queryPermission({mode:'readwrite'}) ile 'denied' yanıtını verir ve createWritable(), NoModificationAllowedError ile reddedilir. getFile() belgeyi akıtır, yani büyük dosyalar sorun değildir. Tutamaçlar IndexedDB'de saklanabilir (nesnelerin ve dizilerin içinde dahil) ve uygulama yeniden başladıktan sonra tekrar kullanılabilir: uygulama, bir seçicinin döndürdüğü her klasör ve dosya için Android'in iznini korur (en son 64'ü; en eskisi önce bırakılır). queryPermission(), bu izin tutulduğu sürece 'granted', izin kalmadığında 'prompt' yanıtını verir; requestPermission() o zaman bir dokunuş gerektirir ve seçiciyi tutamacın kendi klasöründe yeniden açar. 2026-10-03'ten önce derlenen uygulamalar yeniden başlatmada tutamacı kaybeder - yeniden derleyin, kullanıcı klasörü bir kez daha seçer. types seçicinin dosya filtresi olur. Seçiciler yalnızca üst düzey sayfada bulunur. Dahili aktarımlar: __fsPick (appmint:fs olayıyla yanıtlanır), __fsFile, __fsList, __fsChild, __fsRemove, __fsPermission, __fsWritableOpen/Write/Truncate/Close/Abort. Yapay zekayla oluşturulan uygulamalar şunu kontrol eder can('fileHandles').

__dlAbort köprü#

window.WebToApk.__dlAbort(token: String)

[token] indirmesini bırakır (sayfanın okuması başarısız oldu veya vazgeçti).

Örnek

WebToApk.__dlBegin ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

<a download>, Blob indirmeleri, window.open(blobUrl) ve AppMint.saveBlob / AppMint.downloadFile arkasındaki dahili aktarım: dosya sayfadan uygulamaya dilimler halinde akıtılır; böylece büyük bir dışa aktarma bellekte hiçbir zaman dev bir dize olmaz. Sayfalar standart indirme kodunu kullanır; bunları asla doğrudan çağırmayın, çünkü __ yöntemleri haber verilmeden değişebilir.

Döndürür: __dlBegin(name, type, size) bir token döndürür (telefonda yer yoksa ""); __dlChunk(token, base64) bir dilim yazılamadıysa false döndürür; __dlEnd(token, action) bitirir (save = İndirilenler, open = göster, share, copy). Gerekir: hiçbir şey.

Genel API'yi kullanın:

const blob = new Blob([csvText], { type: 'text/csv' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'Report.csv';
a.click();   // lands in Downloads as "Report.csv"

Bir canvas'taki çizimi aynı yoldan dışa aktarın:

canvas.toBlob(function (blob) {
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'drawing.png';
  a.click();
  setTimeout(function () { URL.revokeObjectURL(a.href); }, 10000);
}, 'image/png');

Kullanıcının saklayabileceği bir JSON yedeği kaydedin ve bittiğinde bir mesaj gösterin:

function exportBackup(data) {
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'backup-' + new Date().toISOString().slice(0, 10) + '.json';
  document.body.appendChild(a);
  a.click();
  a.remove();
  showMessage('Backup saved to Downloads.');
}

Notlar: Uygulama kabuğunda uygulanmıştır (ShellDownloads.kt).

__dlBegin köprü#

window.WebToApk.__dlBegin(name: String, type: String, size: Double): String

Sayfanın __dlChunk ile akıtacağı, [size] bayt boyutunda, [name] adlı ([type] = MIME türü) bir indirme açar. Bir token döndürür veya yer yoksa "" döndürür.

Örnek

<a download>, Blob indirmeleri, window.open(blobUrl) ve AppMint.saveBlob / AppMint.downloadFile arkasındaki dahili aktarım: dosya sayfadan uygulamaya dilimler halinde akıtılır; böylece büyük bir dışa aktarma bellekte hiçbir zaman dev bir dize olmaz. Sayfalar standart indirme kodunu kullanır; bunları asla doğrudan çağırmayın, çünkü __ yöntemleri haber verilmeden değişebilir.

Döndürür: __dlBegin(name, type, size) bir token döndürür (telefonda yer yoksa ""); __dlChunk(token, base64) bir dilim yazılamadıysa false döndürür; __dlEnd(token, action) bitirir (save = İndirilenler, open = göster, share, copy). Gerekir: hiçbir şey.

Genel API'yi kullanın:

const blob = new Blob([csvText], { type: 'text/csv' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'Report.csv';
a.click();   // lands in Downloads as "Report.csv"

Bir canvas'taki çizimi aynı yoldan dışa aktarın:

canvas.toBlob(function (blob) {
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'drawing.png';
  a.click();
  setTimeout(function () { URL.revokeObjectURL(a.href); }, 10000);
}, 'image/png');

Kullanıcının saklayabileceği bir JSON yedeği kaydedin ve bittiğinde bir mesaj gösterin:

function exportBackup(data) {
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'backup-' + new Date().toISOString().slice(0, 10) + '.json';
  document.body.appendChild(a);
  a.click();
  a.remove();
  showMessage('Backup saved to Downloads.');
}

Notlar: Uygulama kabuğunda uygulanmıştır (ShellDownloads.kt).

__dlChunk köprü#

window.WebToApk.__dlChunk(token: String, base64: String): Boolean

[token] indiriminin bir base64 dilimi; aktarım başarısız olduysa false.

Örnek

WebToApk.__dlBegin ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

<a download>, Blob indirmeleri, window.open(blobUrl) ve AppMint.saveBlob / AppMint.downloadFile arkasındaki dahili aktarım: dosya sayfadan uygulamaya dilimler halinde akıtılır; böylece büyük bir dışa aktarma bellekte hiçbir zaman dev bir dize olmaz. Sayfalar standart indirme kodunu kullanır; bunları asla doğrudan çağırmayın, çünkü __ yöntemleri haber verilmeden değişebilir.

Döndürür: __dlBegin(name, type, size) bir token döndürür (telefonda yer yoksa ""); __dlChunk(token, base64) bir dilim yazılamadıysa false döndürür; __dlEnd(token, action) bitirir (save = İndirilenler, open = göster, share, copy). Gerekir: hiçbir şey.

Genel API'yi kullanın:

const blob = new Blob([csvText], { type: 'text/csv' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'Report.csv';
a.click();   // lands in Downloads as "Report.csv"

Bir canvas'taki çizimi aynı yoldan dışa aktarın:

canvas.toBlob(function (blob) {
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'drawing.png';
  a.click();
  setTimeout(function () { URL.revokeObjectURL(a.href); }, 10000);
}, 'image/png');

Kullanıcının saklayabileceği bir JSON yedeği kaydedin ve bittiğinde bir mesaj gösterin:

function exportBackup(data) {
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'backup-' + new Date().toISOString().slice(0, 10) + '.json';
  document.body.appendChild(a);
  a.click();
  a.remove();
  showMessage('Backup saved to Downloads.');
}

Notlar: Uygulama kabuğunda uygulanmıştır (ShellDownloads.kt).

__dlEnd köprü#

window.WebToApk.__dlEnd(token: String, action: String): Boolean

[token] indirmesini tamamlar ve üzerinde işlem yapar: [action] "save" (İndirilenler, "İndirildi · Aç" çubuğu ve bildirimle), "open" (PDF görüntüleyici veya telefonun o tür için uygulaması), "share" (paylaşım sayfası) veya "copy" (resmi panoya).

Örnek

WebToApk.__dlBegin ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.

<a download>, Blob indirmeleri, window.open(blobUrl) ve AppMint.saveBlob / AppMint.downloadFile arkasındaki dahili aktarım: dosya sayfadan uygulamaya dilimler halinde akıtılır; böylece büyük bir dışa aktarma bellekte hiçbir zaman dev bir dize olmaz. Sayfalar standart indirme kodunu kullanır; bunları asla doğrudan çağırmayın, çünkü __ yöntemleri haber verilmeden değişebilir.

Döndürür: __dlBegin(name, type, size) bir token döndürür (telefonda yer yoksa ""); __dlChunk(token, base64) bir dilim yazılamadıysa false döndürür; __dlEnd(token, action) bitirir (save = İndirilenler, open = göster, share, copy). Gerekir: hiçbir şey.

Genel API'yi kullanın:

const blob = new Blob([csvText], { type: 'text/csv' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'Report.csv';
a.click();   // lands in Downloads as "Report.csv"

Bir canvas'taki çizimi aynı yoldan dışa aktarın:

canvas.toBlob(function (blob) {
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'drawing.png';
  a.click();
  setTimeout(function () { URL.revokeObjectURL(a.href); }, 10000);
}, 'image/png');

Kullanıcının saklayabileceği bir JSON yedeği kaydedin ve bittiğinde bir mesaj gösterin:

function exportBackup(data) {
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'backup-' + new Date().toISOString().slice(0, 10) + '.json';
  document.body.appendChild(a);
  a.click();
  a.remove();
  showMessage('Backup saved to Downloads.');
}

Notlar: Uygulama kabuğunda uygulanmıştır (ShellDownloads.kt).

__fsChild köprü#

window.WebToApk.__fsChild(uri: String, name: String, kind: String, create: Boolean): String

getFileHandle / getDirectoryHandle (`create` ile).

Örnek

getFileHandle(name, {create}) ve getDirectoryHandle(name, {create}) arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

Döndürür: JSON {ok, kind, name, uri, canWrite} veya {ok:false, error} (NotFoundError, TypeMismatchError, NoModificationAllowedError). Gerekir: şuradan alınan bir klasör showDirectoryPicker().

Genel API'yi kullanın:

const dir = await showDirectoryPicker();
const photos = await dir.getDirectoryHandle('photos', { create: true });
const file = await photos.getFileHandle('list.json', { create: true });

Bir dosyayı oluşturmadan önce klasörde zaten var olup olmadığını kontrol edin:

async function exists(dir, name) {
  try {
    await dir.getFileHandle(name);                     // no {create} = look up only
    return true;
  } catch (e) {
    if (e.name === 'NotFoundError') return false;
    if (e.name === 'TypeMismatchError') return true;   // a folder already has that name
    throw e;
  }
}

Uygulama ayarlarını "config" alt klasörüne JSON olarak kaydedin, gerekirse ikisini de oluşturarak:

async function saveSettings(root, settings) {
  const config = await root.getDirectoryHandle('config', { create: true });
  const file = await config.getFileHandle('settings.json', { create: true });
  const w = await file.createWritable();
  await w.write(JSON.stringify(settings, null, 2));
  await w.close();
}
// root = await showDirectoryPicker()  (from a button tap)

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt).

__fsFile köprü#

window.WebToApk.__fsFile(uri: String): String

getFile(): `{ok, name, type, size, lastModified, url}` (gΓΆvde url'den akar).

Örnek

FileSystemFileHandle.getFile() arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

Döndürür: JSON {ok, name, type, size, lastModified, url} - gövde url adresinden akar (uygulamanın kendi kaynağı, aralıklarla) - veya {ok:false, error} (NotFoundError, TypeMismatchError, foreign_origin). Gerekir: seçicilerden birinden alınan bir tutamaç.

Genel API'yi kullanın:

const [handle] = await showOpenFilePicker();
const file = await handle.getFile();
console.log(file.name, file.size, await file.text());

Seçilen bir resmi bir dizeye okumadan sayfada gösterin:

document.querySelector('#choose').addEventListener('click', async function () {
  const [handle] = await showOpenFilePicker({ types: [{ accept: { 'image/*': [] } }] });
  const file = await handle.getFile();                    // a real File, streamed from the app
  const img = document.querySelector('#preview');
  if (img.src) URL.revokeObjectURL(img.src);
  img.src = URL.createObjectURL(file);
  img.alt = file.name + ' (' + file.type + ')';
});

Daha önce açtığınız bir dosyayı yeniden okuyun ve değiştiğinde veya silindiğinde fark edin:

async function reloadIfChanged(handle, lastSeen) {
  try {
    const file = await handle.getFile();
    if (file.lastModified === lastSeen) return null;      // unchanged
    return { data: JSON.parse(await file.text()), lastModified: file.lastModified };
  } catch (e) {
    if (e.name === 'NotFoundError') alert(handle.name + ' was moved or deleted.');
    else throw e;
    return null;
  }
}

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt).

__fsList köprü#

window.WebToApk.__fsList(uri: String): String

Bir dizin tutamacının girdileri: `{ok, entries:[{kind, name, uri, canWrite}]}`.

Örnek

Bir FileSystemDirectoryHandle üzerinde gezinmenin (entries(), keys(), values(), for await) arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

Döndürür: JSON {ok, entries:[{kind, name, uri, canWrite}]} veya {ok:false, error}. Gerekir: şuradan alınan bir klasör showDirectoryPicker().

Genel API'yi kullanın:

const dir = await showDirectoryPicker();
for await (const [name, entry] of dir.entries()) console.log(entry.kind, name);

Bir klasördeki ve tüm alt klasörlerindeki dosyaları sayın:

async function countFiles(dir) {
  let n = 0;
  for await (const entry of dir.values()) {
    if (entry.kind === 'file') n++;
    else n += await countFiles(entry);                 // entry is a FileSystemDirectoryHandle
  }
  return n;
}
document.querySelector('#scan').addEventListener('click', async function () {
  const dir = await showDirectoryPicker();
  console.log(dir.name, 'holds', await countFiles(dir), 'files');
});

Kullanıcının seçtiği bir klasördeki fotoğrafların sıralı bir listesini oluşturun:

document.querySelector('#open-album').addEventListener('click', async function () {
  const dir = await showDirectoryPicker({ startIn: 'pictures' });
  const names = [];
  for await (const name of dir.keys()) {
    if (/\.(jpe?g|png|webp)$/i.test(name)) names.push(name);
  }
  names.sort(function (a, b) { return a.localeCompare(b); });
  renderAlbum(dir, names);                             // dir.getFileHandle(name) opens each one
});

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt).

__fsPermission köprü#

window.WebToApk.__fsPermission(rootUri: String, uri: String): String

queryPermission() / requestPermission(): seçilen kökü [rootUri] olan bir tutamaç için `{ok, state:'granted'|'prompt'}` - uygulama o seçimin iznini tuttuğu sürece, yeniden açılmalar boyunca 'granted' (izin kalıcıdır; tutamaç IndexedDB'den geri gelir).

Örnek

FileSystemHandle.queryPermission() ve requestPermission() arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

Döndürür: tutamacın geldiği seçilen klasörün veya dosyanın izni uygulamada olduğu sürece (yeniden başlatmalar boyunca korunur) JSON {ok:true, state:'granted'}, izin kalmadığında {ok:true, state:'prompt'} ya da {ok:false, error:'foreign_origin'}. Salt okunur bir dosya, sayfada çağrı yapılmadan {mode:'readwrite'} için 'denied' yanıtını verir. Gerekir: seçicilerden birinden alınan bir tutamaç; IndexedDB'den geri okunan da olur.

Genel API'yi kullanın:

const dir = await kvGet('backupDir');                 // a handle stored in IndexedDB earlier
if ((await dir.queryPermission({ mode: 'readwrite' })) === 'granted') {
  const f = await dir.getFileHandle('backup.json', { create: true });
  const w = await f.createWritable();
  await w.write(JSON.stringify(state));
  await w.close();
}

İzin kalmadığında bir dokunuştan yeniden isteyin - klasör seçici o klasörde açılır ve yalnızca aynı klasör izin alır:

document.getElementById('reconnect').addEventListener('click', async () => {
  const dir = await kvGet('backupDir');
  const state = await dir.requestPermission({ mode: 'readwrite' });   // 'granted' | 'denied'
  status.textContent = state === 'granted' ? 'Backups resumed' : 'Choose a backup folder';
});

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt). Tüm File System Access API için bkz. showOpenFilePicker.

__fsPick köprü#

window.WebToApk.__fsPick(requestId: String, kind: String, optionsJson: String): String

[kind] seçicisini açar (open | save | directory); tutamaçlar şu şekilde gelir: `appmint:fs`.

Örnek

showOpenFilePicker(), showSaveFilePicker() ve showDirectoryPicker() arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

Döndürür: seçici ekrana geldiğinde {ok:true}, ya da {ok:false, error} (biri zaten açıkken NotAllowedError, foreign_origin). Tutamaçlar appmint:fs olayı {requestId, handles:[{kind, name, uri, canWrite}]} veya {requestId, error:'cancelled'} olarak gelir. Gerekir: açılacak bir şey yok.

Genel API'yi kullanın:

const [handle] = await showOpenFilePicker({ types: [{ accept: { 'image/*': ['.png', '.jpg'] } }] });
const file = await handle.getFile();

Önerilen bir adla bir rapor kaydedin, bir düğme dokunuşundan (seçiciler kullanıcı hareketi gerektirir):

document.querySelector('#export').addEventListener('click', async function () {
  try {
    const handle = await showSaveFilePicker({
      suggestedName: 'report.csv',
      types: [{ description: 'CSV', accept: { 'text/csv': ['.csv'] } }]
    });
    const w = await handle.createWritable();
    await w.write(buildCsv());
    await w.close();
  } catch (e) {
    if (e.name !== 'AbortError') alert('Export failed: ' + e.message);   // AbortError = cancelled
  }
});

Kullanıcının birden fazla fotoğrafı aynı anda seçmesine izin verin, Resimler klasöründen başlayarak:

document.querySelector('#add-photos').addEventListener('click', async function () {
  let handles;
  try {
    handles = await showOpenFilePicker({
      multiple: true,
      startIn: 'pictures',
      types: [{ accept: { 'image/*': ['.jpg', '.jpeg', '.png', '.webp'] } }]
    });
  } catch (e) { return; }                                 // cancelled or already open
  for (const h of handles) addToGallery(await h.getFile());
});

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt). Aynı sayfa kodu masaüstü Chrome'da değişmeden çalışır.

__fsRemove köprü#

window.WebToApk.__fsRemove(uri: String, name: String, recursive: Boolean): String

removeEntry(name, {recursive}).

Örnek

FileSystemDirectoryHandle.removeEntry(name, {recursive}) arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

Döndürür: JSON {ok:true} veya {ok:false, error} (NotFoundError, recursive olmadan boş olmayan bir klasör için InvalidModificationError). Gerekir: şuradan alınan bir klasör showDirectoryPicker().

Genel API'yi kullanın:

const dir = await showDirectoryPicker();
await dir.removeEntry('old-drafts', { recursive: true });

Bir dosyayı silin ve "zaten yok" durumunu tamamlanmış sayın:

async function deleteFile(dir, name) {
  try {
    await dir.removeEntry(name);
  } catch (e) {
    if (e.name !== 'NotFoundError') throw e;   // NotFoundError: nothing left to delete
  }
}

Bir klasΓΆrΓΌ kaldΔ±rΔ±n; yalnΔ±zca iΓ§inde bir Εey varsa ΓΆnce sorun:

async function removeFolder(dir, name) {
  try {
    await dir.removeEntry(name);                          // succeeds for an empty folder
  } catch (e) {
    if (e.name !== 'InvalidModificationError') throw e;
    if (!confirm('"' + name + '" is not empty. Delete it and everything inside?')) return false;
    await dir.removeEntry(name, { recursive: true });
  }
  return true;
}

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt).

__fsWritableAbort köprü#

window.WebToApk.__fsWritableAbort(token: String)

abort(): takas bΔ±rakΔ±lΔ±r; belge deΔiΕmeden kalΔ±r.

Örnek

FileSystemWritableFileStream.abort() arkasındaki dahili aktarım - takas kopyası bırakılır ve dosya olduğu gibi kalır. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

Döndürür: hiçbir şey. Gerekir: açık bir yazılabilir akış.

Genel API'yi kullanın:

const w = await handle.createWritable();
try { await w.write(await buildExport()); await w.close(); }
catch (e) { await w.abort(); }

Kullanıcının uzun bir dışa aktarmayı iptal etmesine izin verin; yarım yazılmış hiçbir şey dosyaya ulaşmaz:

let cancelled = false;
document.getElementById('cancel').onclick = function () { cancelled = true; };

async function exportAll(handle, pages) {
  const w = await handle.createWritable();
  for (const page of pages) {
    if (cancelled) { await w.abort(); return 'cancelled'; }   // the file keeps its old content
    await w.write(await renderPage(page));
  }
  await w.close();
  return 'saved';
}

pipeTo ile doğrudan bir dosyaya indirin; indirmeyi durdurmak yazmayı iptal eder:

const ctl = new AbortController();
document.getElementById('stop').onclick = function () { ctl.abort(); };

async function downloadInto(handle, url) {
  const res = await fetch(url);
  if (!res.ok) throw new Error('HTTP ' + res.status);
  const w = await handle.createWritable();
  try {
    await res.body.pipeTo(w, { signal: ctl.signal });     // closes the file when the body ends
    return 'saved';
  } catch (e) {
    return e.name === 'AbortError' ? 'stopped, file unchanged' : 'failed: ' + e.message;
  }
}

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt).

__fsWritableClose köprü#

window.WebToApk.__fsWritableClose(token: String): String

close(): takas belgenin yerini alır.

Örnek

FileSystemWritableFileStream.close() arkasındaki dahili aktarım - takas kopyası dosyanın yerini alır. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

DΓΆner deΔer: JSON {ok:true} veya {ok:false, error} (NoModificationAllowedError, dosyanΔ±n uygulamasΔ± onu kΔ±saltamΔ±yorsa InvalidModificationError). Gerekenler: aΓ§Δ±k bir yazΔ±labilir akΔ±Ε.

Genel API'yi kullanın:

const w = await handle.createWritable();
await w.write(json);
await w.close();   // nothing changes on disk before this

Bir kaydın neden tamamlanmadığını kullanıcıya söyleyin (close() başarısız olabileceği yerdir):

async function saveText(handle, text) {
  const w = await handle.createWritable();
  await w.write(text);
  try {
    await w.close();
    return true;
  } catch (e) {
    if (e.name === 'InvalidModificationError') alert('This file\'s app cannot make it shorter. Save a copy instead.');
    else if (e.name === 'NoModificationAllowedError') alert('This file cannot be changed.');
    else alert('Save failed: ' + e.message);
    return false;
  }
}

Bir dosyayı birkaç parça halinde yazın; okuyucular, tek bir close():

async function saveRows(handle, rows) {
  const w = await handle.createWritable();
  await w.write('id,name,total\n');
  for (const r of rows) await w.write(r.id + ',' + r.name + ',' + r.total + '\n');
  console.log('before close:', (await handle.getFile()).size, 'bytes (old file)');
  await w.close();
  console.log('after close:', (await handle.getFile()).size, 'bytes');
}

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt).

__fsWritableOpen köprü#

window.WebToApk.__fsWritableOpen(uri: String, keepExisting: Boolean): String

createWritable(): [keepExisting] ise dosyadan kopyalanan bir takas dosyasΔ±.

Örnek

FileSystemFileHandle.createWritable({keepExistingData}) arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

Döndürür: özel bir takas kopyası için JSON {ok, token} veya {ok:false, error} (salt okunur bir dosya için NoModificationAllowedError). Gerekir: seçicilerden birinden alınan bir tutamaç.

Genel API'yi kullanın:

const handle = await showSaveFilePicker({ suggestedName: 'report.csv' });
const w = await handle.createWritable({ keepExistingData: false });
await w.write('a,b\n1,2\n');
await w.close();

İçindekileri koruyarak var olan bir günlük dosyasının sonuna bir satır ekleyin:

async function appendLine(handle, line) {
  const size = (await handle.getFile()).size;
  const w = await handle.createWritable({ keepExistingData: true });
  await w.seek(size);                                     // write after the old content
  await w.write(new Date().toISOString() + ' ' + line + '\n');
  await w.close();                                        // only now does the file change
}

Bir canvas'ı PNG olarak kaydedin ve bir şeyler ters giderse dosyaya dokunmayın:

document.querySelector('#save-png').addEventListener('click', async function () {
  const handle = await showSaveFilePicker({
    suggestedName: 'drawing.png',
    types: [{ accept: { 'image/png': ['.png'] } }]
  });
  const blob = await new Promise(function (r) { document.querySelector('canvas').toBlob(r, 'image/png'); });
  const w = await handle.createWritable();
  try {
    await w.write(blob);
    await w.close();
  } catch (e) {
    try { await w.abort(); } catch (x) {}                 // nothing reaches the file before close()
    alert('Could not save: ' + e.message);
  }
});

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt).

__fsWritableTruncate köprü#

window.WebToApk.__fsWritableTruncate(token: String, size: Double): String

Yazılabilir bir akışta truncate(size).

Örnek

FileSystemWritableFileStream.truncate(size) arkasındaki dahili aktarım. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

DΓΆner deΔer: JSON {ok:true} veya {ok:false, error}. Gerekenler: aΓ§Δ±k bir yazΔ±labilir akΔ±Ε.

Genel API'yi kullanın:

const w = await handle.createWritable({ keepExistingData: true });
await w.truncate(0);
await w.write('fresh start');
await w.close();

Bir dosyanın yalnızca ilk 1.000 baytını tutun ve sonrasını kesin:

async function keepFirstKilobyte(handle) {
  const w = await handle.createWritable({ keepExistingData: true });
  await w.truncate(1000);   // a file shorter than this grows instead (see the next example)
  await w.close();
}

Yer ayırın: daha büyük bir boyuta kısaltmak dosyayı büyütür ve yeni baytlar sıfır olarak okunur:

async function makeEmptySlotFile(handle, slots, slotSize) {
  const w = await handle.createWritable();
  await w.truncate(slots * slotSize);                  // e.g. 100 slots of 256 bytes, all zero
  await w.write({ type: 'write', position: 0, data: new Uint8Array([1, 0, 0, 0]) });
  await w.close();
  console.log('file is now', (await handle.getFile()).size, 'bytes');
}

Notlar: Uygulama kabuğunda uygulanmıştır (FileSystemAccess.kt).

__fsWritableWrite köprü#

window.WebToApk.__fsWritableWrite(token: String, position: Double, base64: String): String

Yazılabilir bir akışın write() çağrısının [position] konumundaki bir parçası.

Örnek

FileSystemWritableFileStream.write() arkasındaki dahili aktarım - takas kopyasında bir konumdaki tek bir parça. Sayfalar standart API'yi kullanır; bunu asla doğrudan çağırmayın - __ yöntemleri haber verilmeden değişebilir.

DΓΆner deΔer: JSON {ok:true} veya {ok:false, error}. Gerekenler: aΓ§Δ±k bir yazΔ±labilir akΔ±Ε.

Genel API'yi kullanın:

const w = await handle.createWritable({ keepExistingData: true });
await w.write({ type: 'write', position: 0, data: new Blob(['HEADER\n']) });
await w.close();

Kullanıcının seçtiği bir günlük dosyasının sonuna bir satır ekleyin (dosya seçici bir dokunuş gerektirir):

document.getElementById('addLine').addEventListener('click', async () => {
  const [handle] = await showOpenFilePicker({
    types: [{ description: 'Text', accept: { 'text/plain': ['.txt', '.log'] } }]
  });
  const size = (await handle.getFile()).size;
  const w = await handle.createWritable({ keepExistingData: true });
  await w.seek(size);                                   // start writing after the old content
  await w.write(new Date().toISOString() + ' checked in\n');
  await w.close();
});

Bir canvas çizimini "Farklı kaydet" ile yeni bir PNG olarak kaydedin (bir Blob ham baytları olarak yazılır):

document.getElementById('saveDrawing').addEventListener('click', async () => {
  const handle = await showSaveFilePicker({
    suggestedName: 'drawing.png',
    types: [{ description: 'PNG image', accept: { 'image/png': ['.png'] } }]
  });
  const blob = await new Promise(function (ok) { document.querySelector('canvas').toBlob(ok, 'image/png'); });
  const w = await handle.createWritable();
  await w.write(blob);
  await w.close();
});

Notlar: Uygulama kabuΔunda uygulanmΔ±ΕtΔ±r (FileSystemAccess.kt). BΓΌyΓΌk veriler parΓ§alar halinde gΓΆnderilir.

getMaxIoChunkSize köprü#

window.WebToApk.getMaxIoChunkSize(): Long

[readFileBase64] / [writeFileBase64] için çağrı başına bayt sınırı; parça hesabı için.

Örnek

readFileBase64 / writeFileBase64 için çağrı başına bayt sınırı (4 MB). Sayfalar normalde şunu kullanır WebToApkFS.maxChunkSize().

Döndürür: (eşzamanlı) bir sayı (4194304). Gerekir: çağırmak için hiçbir şey; tarif ettiği okuma/yazma yöntemleri için Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) işaretli olmalıdır.

Genel yol:

const step = window.WebToApkFS ? WebToApkFS.maxChunkSize() : 0;

Ham çağrı:

let step = 1048576;
if (window.WebToApk && typeof WebToApk.getMaxIoChunkSize === 'function') {
  step = Number(WebToApk.getMaxIoChunkSize()) || step;
}
console.log('read/write up to', step, 'bytes per call');

Bir dosyanın kaç köprü çağrısı alacağını, kopyalamadan önce bir ilerleme çubuğunun boyutunu ayarlamak için hesaplayın:

function plannedSteps(fileUri) {
  const step = WebToApkFS.maxChunkSize();          // 0 when the helper is not available
  const info = WebToApkFS.stat(fileUri);
  if (!step || !info.ok) return 0;
  const steps = Math.max(1, Math.ceil(info.size / step));
  document.querySelector('#progress').max = steps;
  return steps;
}

Bir dosyayı her seferinde en büyük boyutta bir parça olacak şekilde başka bir dosyaya kopyalayın:

function copyFile(fromUri, toPath) {
  const step = WebToApkFS.maxChunkSize() || 1048576;
  let offset = 0;
  for (;;) {
    const r = WebToApkFS.readBytes(fromUri, offset, step);
    if (!r.ok) return r;
    const w = WebToApkFS.writeBytes(toPath, r.base64, offset, offset === 0 ? 'truncate' : 'patch');
    if (!w.ok) return w;
    offset += r.bytesRead;
    if (r.eof || r.bytesRead <= 0) return { ok: true, bytes: offset };
  }
}
// copyFile(pickedUri, 'photo-backup.jpg')  (a file in the granted folder)

Notlar: Bundan büyük bir yazma chunk too large (max N bytes) ile reddedilir; daha büyük bir okuma bu boyuta kısaltılır.

pickFile köprü#

window.WebToApk.pickFile(requestId: String, mimeFilter: String)

Sistemin tek dosya seçicisini açar (ACTION_OPEN_DOCUMENT). [requestFolderAccess]'ten farklı olarak bu, tam olarak bir dosyaya erişim verir; bir düzenleyicinin veya imha aracının istemesi gereken budur; okuma/yazma izni kalıcıdır, böylece URI işlem sonlandırıldıktan sonra da çalışmaya devam eder. Yanıt `WebToApkOnFilePickResult(success, fileJson, requestId)` üzerinde gelir; bir hatanın `error` değeri "cancelled", "disabled", "no-picker", "foreign_origin" veya "busy"dir (daha önceki bir istek için zaten bir seçici açık, o kendi yanıtını korur). @param mimeFilter örn. "image/\*", "application/pdf" veya her dosya için "". (Yıldız yalnızca Kotlin blok yorumları iç içe geçtiği için ters eğik çizgiyle kaçırılmıştır.)

Örnek

TEK bir dosya iΓ§in sistem seΓ§icisini aΓ§ar. Sayfalar normalde Promise yardΔ±mcΔ±sΔ± WebToApkFS.pickFile(mimeFilter) kullanΔ±r; bu ham yΓΆntem bir geri Γ§aΔrΔ± ile yanΔ±tlar.

Döndürür: şimdilik hiçbir şey. Daha sonra kabuk window.WebToApkOnFilePickResult(success, fileJson, requestId) çağırır; burada fileJson başarıda bir JSON dizesi {ok, uri, name, size, mime} veya error = 'cancelled', 'disabled', 'no-picker', 'busy' ya da 'foreign_origin' ile {ok:false, uri:'', error}'dir. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

Genel yol (önerilen):

const f = await WebToApkFS.pickFile('image/*');
if (f.ok) console.log('picked', f.name, f.uri);

Ham çağrı, kendi eşleştirmenizle requestId:

function pickRaw(mime) {
  return new Promise(function (resolve) {
    if (!window.WebToApk || typeof WebToApk.pickFile !== 'function') { resolve(null); return; }
    const myId = 'pick_' + Date.now();
    window.WebToApkOnFilePickResult = function (success, fileJson, requestId) {
      if (requestId !== myId) return;
      const r = JSON.parse(fileJson);           // fileJson is a string
      resolve(success && r.ok ? r : null);      // null = cancelled or not allowed
    };
    WebToApk.pickFile(myId, mime || '');        // '' = any file
  });
}

Bir PDF ekleyin ve adını ve boyutunu gösterin; iptali gerçek bir sorundan ayırın:

document.querySelector('#attach').addEventListener('click', async function () {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('application/pdf');
  if (!f.ok) {
    if (f.error === 'cancelled' || f.error === 'busy') return;   // user closed it / one already open
    alert('Could not open the file picker: ' + f.error);         // 'disabled', 'no-picker', ...
    return;
  }
  document.querySelector('#attachment').textContent =
    f.name + ' (' + Math.round(f.size / 1024) + ' KB)';
  localStorage.setItem('attachmentUri', f.uri);   // the grant is kept across restarts
});

Bir metin dosyası açın ve içeriğini bir düzenleyiciye yükleyin:

async function openTextFile() {
  if (!window.WebToApkFS) return;
  const f = await WebToApkFS.pickFile('text/*');
  if (!f.ok) return;
  const r = WebToApkFS.readAll(f.uri);            // {ok, base64, size} or {ok:false, error}
  if (!r.ok) { alert('Could not read ' + f.name + ': ' + r.error); return; }
  const bytes = Uint8Array.from(atob(r.base64), function (c) { return c.charCodeAt(0); });
  document.querySelector('#editor').value = new TextDecoder('utf-8').decode(bytes);
  document.querySelector('#editor').dataset.uri = f.uri;   // save back to the same file later
}

Notlar: AynΔ± anda yalnΔ±zca bir seΓ§im aΓ§Δ±k olabilir: biri aΓ§Δ±kken yapΔ±lan bir Γ§aΔrΔ± hemen error:'busy' ile yanΔ±tlanΔ±r ve aΓ§Δ±k olan kendi yanΔ±tΔ±nΔ± korur. Δ°zin (okuma ve yazma) releaseFileAccess(uri) Γ§aΔrΔ±lana kadar korunur. Dosya burada okunmaz; readFileBase64 / WebToApkFS.readBytes ile kullanΔ±n: uri.

readFileBase64 köprü#

window.WebToApk.readFileBase64(pathOrUri: String, offset: Long, length: Long): String

[offset] konumundan baΕlayarak en fazla [length] ham bayt okur ve bunlarΔ± base64 olarak dΓΆndΓΌrΓΌr. @param pathOrUri [pickFile] ile alΔ±nan bir `content://` URI'si ya da [requestFolderAccess] ile izin verilen klasΓΆre gΓΆre gΓΆreli bir yol. @param length okunacak bayt; Γ§aΔrΔ± baΕΔ±na 4 MB ile sΔ±nΔ±rlanΔ±r. 0 veya negatif, "bir Γ§aΔrΔ±nΔ±n izin verdiΔi kadar" demektir. @return JSON `{ok, base64, bytesRead, offset, size, eof, error}`. `eof`, parΓ§a dΓΆngΓΌsΓΌne `size` ile kendisi karΕΔ±laΕtΔ±rmadan ne zaman duracaΔΔ±nΔ± sΓΆyler.

Örnek

Bir dosyadan bayt konumundan itibaren ham baytları okur ve base64 olarak döndürür. Sayfalar normalde WebToApkFS.readBytes(pathOrUri, offset, length) kullanır (aynı sonuç, zaten ayrıştırılmış) veya WebToApkFS.readAll(pathOrUri).

Döndürür: (eşzamanlı) bir JSON dizesi {ok:true, base64, bytesRead, offset, size, eof} veya {ok:false, error}. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

Genel yol:

const r = WebToApkFS.readBytes(fileUri, 0, 65536);
if (r.ok) console.log(r.bytesRead, 'bytes read, end of file:', r.eof);

Ham çağrı - sonucun çıplak base64 değil, JSON.parse yapmanız gereken bir dize olduğuna dikkat edin:

if (window.WebToApk && typeof WebToApk.readFileBase64 === 'function') {
  const r = JSON.parse(WebToApk.readFileBase64(fileUri, 0, 0));   // length 0 = up to 4 MB
  if (r.ok) {
    const bytes = Uint8Array.from(atob(r.base64), function (c) { return c.charCodeAt(0); });
    console.log('first byte', bytes[0], 'of', r.size);
  } else {
    console.log('read failed:', r.error);
  }
}

Seçilen bir dosyanın gerçekten PNG olduğunu yalnızca ilk 8 baytını okuyarak kontrol edin:

function isPng(fileUri) {
  const r = WebToApkFS.readBytes(fileUri, 0, 8);
  if (!r.ok || r.bytesRead < 8) return false;
  const head = atob(r.base64);
  const sig = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
  return sig.every(function (b, i) { return head.charCodeAt(i) === b; });
}

Büyük bir dosyayı ilerleme çubuğuyla parça parça okuyun, hiçbir zaman tamamını bellekte tutmadan:

function scanInChunks(fileUri, onChunk) {
  const step = WebToApkFS.maxChunkSize() || 1048576;
  let offset = 0;
  for (;;) {
    const r = WebToApkFS.readBytes(fileUri, offset, step);
    if (!r.ok) throw new Error('read failed: ' + r.error);
    onChunk(r.base64);                                    // process this piece, then drop it
    offset += r.bytesRead;
    document.querySelector('#progress').value = r.size ? offset / r.size : 1;
    if (r.eof || r.bytesRead <= 0) break;
  }
  return offset;                                          // total bytes read
}

Notlar: pathOrUri, pickFile ile alınan bir content:// URI'si veya requestFolderAccess ile alınan klasöre göreli bir yoldur. Çağrı başına en fazla 4 MB (getMaxIoChunkSize()); bytesRead üzerinde eof olana kadar döngü yapın. Hatalar: not-found, disabled, cannot-open, offset must be >= 0, offset past end of file (N bytes), foreign_origin (uygulamanın kendi sitesi dışındaki bir siteden gelen sayfa).

releaseFileAccess köprü#

window.WebToApk.releaseFileAccess(uriString: String): String

[pickFile] ile alınan kalıcı bir tek dosya iznini bırakır. Android, bir uygulamanın kaç URI iznini kalıcı tutabileceğini sınırlar; bu yüzden çok sayıda dosya seçen bir uygulama işi biten dosyaları bırakmalıdır.

Örnek

pickFile ile seçilen bir dosya için tutulan okuma/yazma iznini serbest bırakır. Sayfalar normalde gerçek bir boolean döndüren WebToApkFS.releaseFile(uri) kullanır.

Döndürür: (eşzamanlı) dize "true" veya "false". Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin (aksi halde her zaman "false").

Genel yol:

if (window.WebToApkFS) WebToApkFS.releaseFile(fileUri);

Ham çağrı:

if (window.WebToApk && typeof WebToApk.releaseFileAccess === 'function') {
  const released = WebToApk.releaseFileAccess(fileUri) === 'true';   // a string, not a boolean
  console.log(released ? 'access released' : 'nothing to release');
}

En fazla 10 öğelik bir "son dosyalar" listesi tutun ve listeden düşen her dosyanın iznini bırakın:

function rememberRecent(uri) {
  let recent = JSON.parse(localStorage.getItem('recentFiles') || '[]');
  recent = [uri].concat(recent.filter(function (u) { return u !== uri; }));
  while (recent.length > 10) {
    const dropped = recent.pop();
    if (window.WebToApkFS) WebToApkFS.releaseFile(dropped);   // Android caps kept grants
  }
  localStorage.setItem('recentFiles', JSON.stringify(recent));
}

Geçerli belgeyi kapatın: iznini bırakın ve kaydedilen URI'yi unutun:

document.querySelector('#close-doc').addEventListener('click', function () {
  const uri = localStorage.getItem('openDocUri');
  if (uri && window.WebToApkFS) {
    const released = WebToApkFS.releaseFile(uri);   // true / false, already a boolean
    console.log(released ? 'grant released' : 'no grant was held for this file');
  }
  localStorage.removeItem('openDocUri');
  document.querySelector('#editor').value = '';
});

Notlar: Android, bir uygulamanΔ±n tutabileceΔi dosya izni sayΔ±sΔ±nΔ± sΔ±nΔ±rlar; bu yΓΌzden artΔ±k gerek duymadΔ±ΔΔ±nΔ±z dosyalarΔ± serbest bΔ±rakΔ±n. DosyanΔ±n kendisine dokunulmaz.

statFile köprü#

window.WebToApk.statFile(pathOrUri: String): String

Hiçbirini okumadan dosya veya klasör meta verileri. Göreli bir yol, izin verilen klasörün bir dosyasını veya alt klasörünü adlandırır; bir klasör `size` 0 ve `mime` `vnd.android.document/directory` ile `isDirectory: true` yanıtını verir. @return JSON `{ok, name, size, mime, lastModified, uri, isDirectory, canWrite}` veya `not-found`, `disabled` ya da `foreign_origin` ile `{ok:false, error}`.

Örnek

Bir dosyanın meta verilerini (ad, boyut, tür) baytlarını okumadan okur. Sayfalar normalde JSON'u sizin için ayrıştıran WebToApkFS.stat(pathOrUri) kullanır.

Döndürür: (eşzamanlı) bir JSON dizesi {ok, name, size, mime, lastModified, uri, isDirectory, canWrite} veya {ok:false, error} ('not-found', 'disabled', 'foreign_origin'). Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

Genel yol:

const info = WebToApkFS.stat(fileUri);
if (info.ok) console.log(info.name, info.size);

Ham çağrı:

if (window.WebToApk && typeof WebToApk.statFile === 'function') {
  const info = JSON.parse(WebToApk.statFile(fileUri));   // or 'notes/today.txt' in the granted folder
  if (info.ok) console.log(info.name, info.size + ' bytes', info.mime, info.canWrite);
  else console.log('stat failed:', info.error);
}

Büyük bir dosyayı, hiçbir bölümünü okumadan önce reddedin:

const MAX_BYTES = 20 * 1024 * 1024;
function canImport(fileUri) {
  const info = WebToApkFS.stat(fileUri);
  if (!info.ok) { alert('File not available: ' + info.error); return false; }
  if (info.size > MAX_BYTES) {
    alert(info.name + ' is ' + Math.round(info.size / 1048576) + ' MB; the limit is 20 MB.');
    return false;
  }
  return true;
}

İzin verilen klasörün bir alt klasörünü arayın ve "Buraya kaydet" seçeneğini yalnızca yazılabilir olduğunda sunun:

function checkExportFolder() {
  const info = WebToApkFS.stat('exports');          // a path relative to the granted folder
  const btn = document.querySelector('#save-here');
  if (!info.ok) {                                    // 'not-found' = the folder is not there yet
    btn.textContent = 'Create "exports" and save';
    return WebToApkFS.mkdir('exports');
  }
  btn.disabled = !(info.isDirectory && info.canWrite);
  console.log('exports last changed', new Date(info.lastModified).toLocaleString());
  return true;
}

Notlar: pickFile ile alınan bir content:// URI'sini veya requestFolderAccess ile izin verilen klasöre göreli bir yolu kabul eder. Yalnızca uygulamanın kendi sayfalarına veya yapılandırılmış web sitesine yanıt verir; başka bir siteden gelen sayfa şunu alır foreign_origin.

writeFileBase64 köprü#

window.WebToApk.writeFileBase64(pathOrUri: String, base64: String, offset: Long, mode: String): String

Ham baytlarΔ± yazar ve kaΓ§Δ±nΔ±n ulaΕtΔ±ΔΔ±nΔ± doΔrular. @param mode `"patch"`, [offset] konumuna dosyanΔ±n geri kalanΔ±nΔ± bozmadan yazar (Γ§ok geΓ§iΕli ΓΌzerine yazma bunu gerektirir), `"truncate"` dosyanΔ±n yerini alΔ±r, `"append"` [offset] deΔerini yok sayar ve sona yazar. @return JSON `{ok, bytesWritten, offset, size, error}` - `size`, fsync sonrasΔ± dosyadan yeniden okunur; bΓΆylece Γ§aΔΔ±ran bir boolean'a gΓΌvenmek yerine yazmayΔ± gerΓ§ekten doΔrulayabilir.

Örnek

Base64 baytlarını bir dosyaya bir konumdan itibaren (patch), tüm dosyanın üzerine (truncate) veya sona (append) yazar. Sayfalar normalde WebToApkFS.writeBytes(pathOrUri, base64, offset, mode) veya şunu kullanır WebToApkFS.writeAll(pathOrUri, base64).

Döndürür: (eşzamanlı) bir JSON dizesi {ok, bytesWritten, offset, size} (size bir fsync sonrası yeniden okunur) veya {ok:false, error}. Gerekir: derleme yaparken Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) seçeneğini işaretleyin.

Genel yol:

const r = WebToApkFS.writeBytes(fileUri, btoa('hello'), 0, 'truncate');
if (r.ok) console.log('saved', r.bytesWritten, 'bytes');

Ham çağrı:

if (window.WebToApk && typeof WebToApk.writeFileBase64 === 'function') {
  const r = JSON.parse(WebToApk.writeFileBase64('notes/today.txt', btoa('Buy milk\n'), 0, 'append'));
  if (!r.ok) console.log('write failed:', r.error);
  else console.log('file is now', r.size, 'bytes');
}

Bir canvas çizimini, kullanıcının seçtiği bir dosyaya PNG olarak kaydedin ve oradakinin yerine koyun:

document.querySelector('#save').addEventListener('click', async function () {
  const f = await WebToApkFS.pickFile('image/png');
  if (!f.ok) return;
  const base64 = document.querySelector('canvas').toDataURL('image/png').split(',')[1];
  const r = WebToApkFS.writeAll(f.uri, base64);   // truncates, then writes in 4 MB chunks
  if (r.ok) console.log('saved', r.bytesWritten, 'bytes to', f.name);
  else alert('Save failed: ' + r.error);
});

Bir dosyayı silmeden önce yerinde sıfırlarla üzerine yazın ve her geçişin tamamlandığını doğrulayın:

function zeroFill(fileUri) {
  const info = WebToApkFS.stat(fileUri);
  if (!info.ok) return false;
  const slice = 32768;
  for (let at = 0; at < info.size; at += slice) {
    const len = Math.min(slice, info.size - at);
    const zeros = btoa(String.fromCharCode.apply(null, new Uint8Array(len)));
    const w = WebToApkFS.writeBytes(fileUri, zeros, at, 'patch');   // the rest of the file is kept
    if (!w.ok || w.bytesWritten !== len) { console.log('write failed at', at, w.error); return false; }
  }
  return true;   // each call is fsync'ed before it answers
}

Notlar: Mod 'patch' (boΕken varsayΔ±lan), 'truncate' veya 'append' olur. Bir Γ§aΔrΔ± en fazla 4 MB Γ§ΓΆzΓΌlmΓΌΕ bayt alΔ±r (getMaxIoChunkSize()). GΓ°receli bir yol, eksikse izin verilen klasΓΆrΓΌn iΓ§inde oluΕturulur. Hatalar: disabled, foreign_origin (ekrandaki sayfa uygulamanΔ±n kendisi deΔil), bad mode: x, offset must be >= 0, bad base64, chunk too large (max N bytes), not-found, cannot-open, short write.

Her belge derlemesinde uygulama çalışma zamanından ve örnek dosyalarından oluşturulur. Markdown olarak okuyun · Tüm aileler.

22.10.2026 tarihinde yayınlanan köprüyle karşılaştırılarak kontrol edildi.