De File System Access API: vereenvoudigt de toegang tot lokale bestanden

Met de File System Access API kunnen webapps wijzigingen rechtstreeks in bestanden en mappen op het apparaat van de gebruiker lezen of opslaan.

Wat is de bestandssysteemtoegang-API?

De File System Access API (voorheen bekend als Native File System API en daarvoor heette het Writeable Files API) stelt ontwikkelaars in staat krachtige webapps te bouwen die communiceren met bestanden op het lokale apparaat van de gebruiker, zoals IDE's, foto- en video-editors, teksteditors en meer. Nadat een gebruiker een webapp toegang heeft verleend, stelt deze API hem in staat wijzigingen rechtstreeks in bestanden en mappen op het apparaat van de gebruiker te lezen of op te slaan. Naast het lezen en schrijven van bestanden biedt de File System Access API de mogelijkheid om een ​​map te openen en de inhoud ervan op te sommen.

Als u eerder met het lezen en schrijven van bestanden hebt gewerkt, zal veel van wat ik ga delen u bekend voorkomen. Ik moedig je aan om het toch te lezen, omdat niet alle systemen hetzelfde zijn.

De File System Access API wordt momenteel ondersteund in de meeste Chromium-browsers op Windows, macOS, ChromeOS en Linux. Een opmerkelijke uitzondering is Brave, waar het momenteel alleen achter een vlag verkrijgbaar is. Android ondersteunt het oorspronkelijke privébestandssysteemgedeelte van de API vanaf Chromium 109. Er zijn momenteel geen plannen voor kiezermethoden, maar je kunt de potentiële voortgang volgen door crbug.com/1011535 in de hoofdrol te zetten.

Met behulp van de File System Access API

Om de kracht en het nut van de File System Access API te laten zien, heb ik een teksteditor voor één bestand geschreven. Hiermee kunt u een tekstbestand openen, bewerken, de wijzigingen weer op schijf opslaan, of een nieuw bestand starten en de wijzigingen op schijf opslaan. Het is niets bijzonders, maar biedt voldoende om u te helpen de concepten te begrijpen.

Browser-ondersteuning

Browserondersteuning

  • 86
  • 86
  • X
  • X

Bron

Probeer het

Zie de File System Access API in actie in de demo van de teksteditor .

Een bestand uit het lokale bestandssysteem lezen

Het eerste gebruiksscenario dat ik wil aanpakken, is de gebruiker vragen een bestand te kiezen en dat bestand vervolgens vanaf schijf te openen en te lezen.

Vraag de gebruiker een bestand te kiezen om te lezen

Het toegangspunt tot de File System Access API is window.showOpenFilePicker() . Wanneer het wordt aangeroepen, wordt een dialoogvenster voor het kiezen van bestanden weergegeven en wordt de gebruiker gevraagd een bestand te selecteren. Nadat ze een bestand hebben geselecteerd, retourneert de API een reeks bestandsingangen. Met een optionele options kunt u het gedrag van de bestandskiezer beïnvloeden, bijvoorbeeld door de gebruiker toe te staan ​​meerdere bestanden, mappen of verschillende bestandstypen te selecteren. Zonder dat er opties zijn opgegeven, kan de gebruiker met de bestandskiezer één enkel bestand selecteren. Dit is perfect voor een teksteditor.

Net als veel andere krachtige API's moet het aanroepen showOpenFilePicker() worden gedaan in een veilige context en moet het worden aangeroepen vanuit een gebruikersgebaar.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  // Destructure the one-element array.
  [fileHandle] = await window.showOpenFilePicker();
  // Do something with the file handle.
});

Zodra de gebruiker een bestand selecteert, retourneert showOpenFilePicker() een array van handvatten, in dit geval een array van één element met één FileSystemFileHandle die de eigenschappen en methoden bevat die nodig zijn om met het bestand te communiceren.

Het is handig om een ​​verwijzing naar de bestandsingang te bewaren, zodat deze later kan worden gebruikt. Het is nodig om wijzigingen in het bestand op te slaan of om andere bestandsbewerkingen uit te voeren.

Een bestand uit het bestandssysteem lezen

Nu u toegang heeft tot een bestand, kunt u de eigenschappen van het bestand opvragen of toegang krijgen tot het bestand zelf. Voorlopig lees ik gewoon de inhoud ervan. Het aanroepen van handle.getFile() retourneert een File object, dat een blob bevat. Om de gegevens van de blob op te halen, roept u een van de methoden ervan aan, ( slice() , stream() , text() of arrayBuffer() ).

const file = await fileHandle.getFile();
const contents = await file.text();

Het File object dat wordt geretourneerd door FileSystemFileHandle.getFile() is alleen leesbaar zolang het onderliggende bestand op schijf niet is gewijzigd. Als het bestand op schijf wordt gewijzigd, wordt het File object onleesbaar en moet u getFile() opnieuw aanroepen om een ​​nieuw File object te krijgen dat de gewijzigde gegevens kan lezen.

Alles op een rij zetten

Wanneer gebruikers op de knop Openen klikken, toont de browser een bestandskiezer. Zodra ze een bestand hebben geselecteerd, leest de app de inhoud en plaatst deze in een <textarea> .

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  [fileHandle] = await window.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.text();
  textArea.value = contents;
});

Schrijf het bestand naar het lokale bestandssysteem

In de teksteditor zijn er twee manieren om een ​​bestand op te slaan: Opslaan en Opslaan als . Opslaan schrijft eenvoudigweg de wijzigingen terug naar het originele bestand met behulp van de eerder opgehaalde bestandsingang. Maar Opslaan als creëert een nieuw bestand en vereist dus een nieuwe bestandsingang.

Maak een nieuw bestand

Om een ​​bestand op te slaan, roept u showSaveFilePicker() aan, waarmee de bestandskiezer in de modus "opslaan" wordt weergegeven, zodat de gebruiker een nieuw bestand kan kiezen dat hij/zij wil gebruiken om op te slaan. Voor de teksteditor wilde ik ook dat deze automatisch een .txt extensie zou toevoegen, dus heb ik enkele aanvullende parameters opgegeven.

async function getNewFileHandle() {
  const options = {
    types: [
      {
        description: 'Text Files',
        accept: {
          'text/plain': ['.txt'],
        },
      },
    ],
  };
  const handle = await window.showSaveFilePicker(options);
  return handle;
}

Wijzigingen op schijf opslaan

Je kunt alle code voor het opslaan van wijzigingen in een bestand vinden in mijn demo van de teksteditor op GitHub . De kernbestandssysteeminteracties bevinden zich in fs-helpers.js . In zijn eenvoudigste vorm ziet het proces eruit als de onderstaande code. Ik zal elke stap doornemen en uitleggen.

// fileHandle is an instance of FileSystemFileHandle..
async function writeFile(fileHandle, contents) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Write the contents of the file to the stream.
  await writable.write(contents);
  // Close the file and write the contents to disk.
  await writable.close();
}

Bij het schrijven van gegevens naar schijf wordt een FileSystemWritableFileStream object gebruikt, een subklasse van WritableStream . Maak de stream door createWritable() aan te roepen op het bestandshandle-object. Wanneer createWritable() wordt aangeroepen, controleert de browser eerst of de gebruiker schrijfrechten voor het bestand heeft verleend. Als er geen toestemming om te schrijven is verleend, vraagt ​​de browser de gebruiker om toestemming. Als er geen toestemming wordt verleend, genereert createWritable() een DOMException en kan de app niet naar het bestand schrijven. In de teksteditor worden de DOMException objecten verwerkt in de methode saveFile() .

De methode write() heeft een string nodig, die nodig is voor een teksteditor. Maar er kan ook een BufferSource of een Blob voor nodig zijn. U kunt er bijvoorbeeld rechtstreeks een stream naartoe leiden:

async function writeURLToFile(fileHandle, url) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Make an HTTP request for the contents.
  const response = await fetch(url);
  // Stream the response into the file.
  await response.body.pipeTo(writable);
  // pipeTo() closes the destination pipe by default, no need to close it.
}

U kunt ook seek() of truncate() binnen de stream gebruiken om het bestand op een specifieke positie bij te werken of de grootte van het bestand te wijzigen.

Een voorgestelde bestandsnaam en startmap opgeven

In veel gevallen wilt u mogelijk dat uw app een standaardbestandsnaam of -locatie voorstelt. Een teksteditor wil bijvoorbeeld mogelijk een standaardbestandsnaam Untitled Text.txt voorstellen in plaats van Untitled . U kunt dit bereiken door een suggestedName eigenschap door te geven als onderdeel van de showSaveFilePicker opties.

const fileHandle = await self.showSaveFilePicker({
  suggestedName: 'Untitled Text.txt',
  types: [{
    description: 'Text documents',
    accept: {
      'text/plain': ['.txt'],
    },
  }],
});

Hetzelfde geldt voor de standaard startmap. Als u een teksteditor aan het bouwen bent, wilt u wellicht het dialoogvenster voor het opslaan of openen van bestanden starten in de standaardmap documents , terwijl u bij een afbeeldingseditor mogelijk in de standaardmap pictures wilt starten. U kunt een standaard startmap voorstellen door een eigenschap startIn door te geven aan de methoden showSaveFilePicker , showDirectoryPicker() of showOpenFilePicker .

const fileHandle = await self.showOpenFilePicker({
  startIn: 'pictures'
});

De lijst met de bekende systeemmappen is:

  • desktop : De bureaubladmap van de gebruiker, als zoiets bestaat.
  • documents : Directory waarin documenten die door de gebruiker zijn gemaakt, doorgaans worden opgeslagen.
  • downloads : map waar gedownloade bestanden doorgaans worden opgeslagen.
  • music : map waar audiobestanden normaal gesproken worden opgeslagen.
  • pictures : map waarin foto's en andere stilstaande beelden normaal gesproken worden opgeslagen.
  • videos : map waar video's/films normaal gesproken worden opgeslagen.

Naast bekende systeemmappen kunt u ook een bestaand bestand of directory-handle als waarde voor startIn doorgeven. Het dialoogvenster wordt dan in dezelfde map geopend.

// Assume `directoryHandle` is a handle to a previously opened directory.
const fileHandle = await self.showOpenFilePicker({
  startIn: directoryHandle
});

Het doel van verschillende bestandskiezers specificeren

Soms hebben applicaties verschillende pickers voor verschillende doeleinden. Met een rich text-editor kan de gebruiker bijvoorbeeld tekstbestanden openen, maar ook afbeeldingen importeren. Standaard wordt elke bestandskiezer geopend op de laatst onthouden locatie. U kunt dit omzeilen door voor elk type kiezer id waarden op te slaan. Als er een id is opgegeven, onthoudt de implementatie van de bestandskiezer een afzonderlijke laatst gebruikte map voor die id .

const fileHandle1 = await self.showSaveFilePicker({
  id: 'openText',
});

const fileHandle2 = await self.showSaveFilePicker({
  id: 'importImage',
});

Bestandshandvatten of maphandvatten opslaan in IndexedDB

Bestandshandvatten en maphandvatten zijn serialiseerbaar, wat betekent dat u een bestand of maphandle kunt opslaan in IndexedDB, of postMessage() kunt aanroepen om ze tussen dezelfde oorsprong op het hoogste niveau te verzenden.

Het opslaan van bestands- of maphandles in IndexedDB betekent dat u de status kunt opslaan of kunt onthouden aan welke bestanden of mappen een gebruiker heeft gewerkt. Dit maakt het mogelijk om een ​​lijst bij te houden van recent geopende of bewerkte bestanden, aan te bieden om het laatste bestand opnieuw te openen wanneer de app wordt geopend, de vorige werkmap te herstellen en meer. In de teksteditor bewaar ik een lijst met de vijf meest recente bestanden die de gebruiker heeft geopend, zodat je deze bestanden gemakkelijk opnieuw kunt openen.

Het onderstaande codevoorbeeld toont het opslaan en ophalen van een bestandsingang en een mapingang. Je kunt dit in actie zien op Glitch. (Ik gebruik de idb-keyval- bibliotheek voor beknoptheid.)

import { get, set } from 'https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js';

const pre1 = document.querySelector('pre.file');
const pre2 = document.querySelector('pre.directory');
const button1 = document.querySelector('button.file');
const button2 = document.querySelector('button.directory');

// File handle
button1.addEventListener('click', async () => {
  try {
    const fileHandleOrUndefined = await get('file');
    if (fileHandleOrUndefined) {
      pre1.textContent = `Retrieved file handle "${fileHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const [fileHandle] = await window.showOpenFilePicker();
    await set('file', fileHandle);
    pre1.textContent = `Stored file handle for "${fileHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

// Directory handle
button2.addEventListener('click', async () => {
  try {
    const directoryHandleOrUndefined = await get('directory');
    if (directoryHandleOrUndefined) {
      pre2.textContent = `Retrieved directroy handle "${directoryHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const directoryHandle = await window.showDirectoryPicker();
    await set('directory', directoryHandle);
    pre2.textContent = `Stored directory handle for "${directoryHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

Handvatten en machtigingen voor opgeslagen bestanden of mappen

Omdat machtigingen momenteel niet tussen sessies worden gehandhaafd, moet u verifiëren of de gebruiker machtiging voor het bestand of de map heeft verleend met behulp van queryPermission() . Als dat niet het geval is, roept u requestPermission() aan om het (opnieuw) aan te vragen. Dit werkt hetzelfde voor bestands- en maphandles. U moet respectievelijk fileOrDirectoryHandle.requestPermission(descriptor) fileOrDirectoryHandle.queryPermission(descriptor) uitvoeren.

In de teksteditor heb ik een methode verifyPermission() gemaakt die controleert of de gebruiker al toestemming heeft verleend en indien nodig het verzoek indient.

async function verifyPermission(fileHandle, readWrite) {
  const options = {};
  if (readWrite) {
    options.mode = 'readwrite';
  }
  // Check if permission was already granted. If so, return true.
  if ((await fileHandle.queryPermission(options)) === 'granted') {
    return true;
  }
  // Request permission. If the user grants permission, return true.
  if ((await fileHandle.requestPermission(options)) === 'granted') {
    return true;
  }
  // The user didn't grant permission, so return false.
  return false;
}

Door bij het leesverzoek schrijftoestemming te vragen, heb ik het aantal toestemmingsprompts verminderd; de gebruiker ziet één prompt bij het openen van het bestand en verleent toestemming om er zowel naar te lezen als te schrijven.

Een map openen en de inhoud ervan opsommen

Als u alle bestanden in een map wilt opsommen, roept u showDirectoryPicker() aan. De gebruiker selecteert een map in een kiezer, waarna een FileSystemDirectoryHandle wordt geretourneerd, waarmee u de bestanden van de map kunt opsommen en openen. Standaard heeft u leestoegang tot de bestanden in de map, maar als u schrijftoegang nodig heeft, kunt u { mode: 'readwrite' } doorgeven aan de methode.

const butDir = document.getElementById('butDirectory');
butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  for await (const entry of dirHandle.values()) {
    console.log(entry.kind, entry.name);
  }
});

Als u bovendien elk bestand via getFile() moet benaderen om bijvoorbeeld de individuele bestandsgroottes te verkrijgen, gebruik dan niet await op elk resultaat opeenvolgend, maar verwerk alle bestanden parallel, bijvoorbeeld via Promise.all() .

const butDir = document.getElementById('butDirectory');
butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  const promises = [];
  for await (const entry of dirHandle.values()) {
    if (entry.kind !== 'file') {
      continue;
    }
    promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`));
  }
  console.log(await Promise.all(promises));
});

Bestanden en mappen in een map maken of openen

Vanuit een map kunt u bestanden en mappen maken of openen met behulp van de getFileHandle() - of respectievelijk de getDirectoryHandle() methode. Door een optioneel options door te geven met de sleutel create en de booleaanse waarde true of false , kunt u bepalen of er een nieuw bestand of een nieuwe map moet worden gemaakt als deze niet bestaat.

// In an existing directory, create a new directory named "My Documents".
const newDirectoryHandle = await existingDirectoryHandle.getDirectoryHandle('My Documents', {
  create: true,
});
// In this new directory, create a file named "My Notes.txt".
const newFileHandle = await newDirectoryHandle.getFileHandle('My Notes.txt', { create: true });

Het pad van een item in een directory bepalen

Bij het werken met bestanden of mappen in een map kan het handig zijn om het pad van het betreffende item vast te stellen. Dit kan gedaan worden met de toepasselijk genoemde methode resolve() . Voor het oplossen kan het item een ​​direct of indirect onderliggend item van de directory zijn.

// Resolve the path of the previously created file called "My Notes.txt".
const path = await newDirectoryHandle.resolve(newFileHandle);
// `path` is now ["My Documents", "My Notes.txt"]

Bestanden en mappen in een map verwijderen

Als u toegang heeft gekregen tot een map, kunt u de daarin opgenomen bestanden en mappen verwijderen met de methode removeEntry() . Voor mappen kan het verwijderen optioneel recursief zijn en alle submappen en de bestanden daarin omvatten.

// Delete a file.
await directoryHandle.removeEntry('Abandoned Projects.txt');
// Recursively delete a folder.
await directoryHandle.removeEntry('Old Stuff', { recursive: true });

Een bestand of map rechtstreeks verwijderen

Als u toegang hebt tot een bestands- of mapingang, roept u remove() aan op een FileSystemFileHandle of FileSystemDirectoryHandle om deze te verwijderen.

// Delete a file.
await fileHandle.remove();
// Delete a directory.
await directoryHandle.remove();

Bestanden en mappen hernoemen en verplaatsen

Bestanden en mappen kunnen worden hernoemd of naar een nieuwe locatie worden verplaatst door move() aan te roepen op de FileSystemHandle interface. FileSystemHandle heeft de onderliggende interfaces FileSystemFileHandle en FileSystemDirectoryHandle . De move() methode heeft een of twee parameters nodig. De eerste kan een tekenreeks zijn met de nieuwe naam of een FileSystemDirectoryHandle voor de doelmap. In het laatste geval is de optionele tweede parameter een string met de nieuwe naam, zodat verplaatsen en hernoemen in één stap kan gebeuren.

// Rename the file.
await file.move('new_name');
// Move the file to a new directory.
await file.move(directory);
// Move the file to a new directory and rename it.
await file.move(directory, 'newer_name');

Integratie via slepen en neerzetten

Dankzij de HTML Drag and Drop-interfaces kunnen webapplicaties gesleepte en neergezette bestanden op een webpagina accepteren. Tijdens slepen en neerzetten worden gesleepte bestands- en directory-items gekoppeld aan respectievelijk bestandsitems en directory-items. De methode DataTransferItem.getAsFileSystemHandle() retourneert een belofte met een FileSystemFileHandle object als het gesleepte item een ​​bestand is, en een belofte met een FileSystemDirectoryHandle object als het gesleepte item een ​​map is. De onderstaande lijst toont dit in actie. Merk op dat DataTransferItem.kind van de Drag and Drop-interface "file" is voor zowel bestanden als mappen, terwijl FileSystemHandle.kind van de File System Access API "file" is voor bestanden en "directory" voor mappen.

elem.addEventListener('dragover', (e) => {
  // Prevent navigation.
  e.preventDefault();
});

elem.addEventListener('drop', async (e) => {
  e.preventDefault();

  const fileHandlesPromises = [...e.dataTransfer.items]
    .filter((item) => item.kind === 'file')
    .map((item) => item.getAsFileSystemHandle());

  for await (const handle of fileHandlesPromises) {
    if (handle.kind === 'directory') {
      console.log(`Directory: ${handle.name}`);
    } else {
      console.log(`File: ${handle.name}`);
    }
  }
});

Toegang tot het oorspronkelijke privébestandssysteem

Het oorspronkelijke privébestandssysteem is een opslageindpunt dat, zoals de naam al doet vermoeden, privé is voor de oorsprong van de pagina. Hoewel browsers dit doorgaans implementeren door de inhoud van dit oorspronkelijke privébestandssysteem ergens op een schijf te bewaren, is het niet de bedoeling dat de inhoud gemakkelijk toegankelijk is voor de gebruiker. Op dezelfde manier is er geen verwachting dat er bestanden of mappen bestaan ​​met namen die overeenkomen met de namen van onderliggende bestanden van het oorspronkelijke privébestandssysteem. Hoewel de browser de indruk kan wekken dat er bestanden zijn, kan de browser deze "bestanden" intern (aangezien dit een oorspronkelijk privébestandssysteem is) opslaan in een database of een andere gegevensstructuur. Als u deze API gebruikt, verwacht dan niet dat u de aangemaakte bestanden ergens op de harde schijf één-op-één zult vinden. U kunt zoals gewoonlijk op het originele privébestandssysteem werken zodra u toegang hebt tot de root FileSystemDirectoryHandle .

const root = await navigator.storage.getDirectory();
// Create a new file handle.
const fileHandle = await root.getFileHandle('Untitled.txt', { create: true });
// Create a new directory handle.
const dirHandle = await root.getDirectoryHandle('New Folder', { create: true });
// Recursively remove a directory.
await root.removeEntry('Old Stuff', { recursive: true });

Browserondersteuning

  • 86
  • 86
  • 111
  • 15.2

Bron

Toegang tot bestanden die zijn geoptimaliseerd voor prestaties vanuit het oorspronkelijke privébestandssysteem

Het originele privébestandssysteem biedt optionele toegang tot een speciaal soort bestand dat sterk is geoptimaliseerd voor prestaties, bijvoorbeeld door ter plaatse exclusieve schrijftoegang tot de inhoud van een bestand aan te bieden. In Chromium 102 en hoger is er een extra methode op het oorspronkelijke privébestandssysteem om de toegang tot bestanden te vereenvoudigen: createSyncAccessHandle() (voor synchrone lees- en schrijfbewerkingen). Het wordt weergegeven in FileSystemFileHandle , maar uitsluitend in Web Workers .

// (Read and write operations are synchronous,
// but obtaining the handle is asynchronous.)
// Synchronous access exclusively in Worker contexts.
const accessHandle = await fileHandle.createSyncAccessHandle();
const writtenBytes = accessHandle.write(buffer);
const readBytes = accessHandle.read(buffer, { at: 1 });

Polyvulling

Het is niet mogelijk om de File System Access API-methoden volledig te polyfillen.

  • De methode showOpenFilePicker() kan worden benaderd met een <input type="file"> element.
  • De methode showSaveFilePicker() kan worden gesimuleerd met een <a download="file_name"> element, hoewel dit een programmatische download activeert en het overschrijven van bestaande bestanden niet mogelijk maakt.
  • De methode showDirectoryPicker() kan enigszins worden geëmuleerd met het niet-standaard <input type="file" webkitdirectory> element.

We hebben een bibliotheek ontwikkeld met de naam browser-fs-access die waar mogelijk gebruik maakt van de File System Access API en die in alle andere gevallen terugvalt op de volgende beste opties.

Beveiliging en machtigingen

Het Chrome-team heeft de File System Access API ontworpen en geïmplementeerd met behulp van de kernprincipes die zijn gedefinieerd in Toegangscontrole tot krachtige webplatformfuncties , inclusief gebruikerscontrole en transparantie, en gebruikersergonomie.

Een bestand openen of een nieuw bestand opslaan

Bestandskiezer om een ​​bestand te openen om te lezen
Een bestandskiezer die wordt gebruikt om een ​​bestaand bestand te openen om te lezen.

Bij het openen van een bestand geeft de gebruiker toestemming om een ​​bestand of map te lezen via de bestandskiezer. De geopende bestandskiezer kan alleen via een gebruikersgebaar worden weergegeven wanneer deze vanuit een beveiligde context wordt bediend. Als gebruikers van gedachten veranderen, kunnen ze de selectie in de bestandskiezer annuleren en krijgt de site nergens toegang toe. Dit is hetzelfde gedrag als dat van het <input type="file"> element.

Bestandskiezer om een ​​bestand op schijf op te slaan.
Een bestandskiezer die wordt gebruikt om een ​​bestand op schijf op te slaan.

Op dezelfde manier toont de browser, wanneer een webapp een nieuw bestand wil opslaan, de kiezer voor het opslaan van bestanden, waardoor de gebruiker de naam en locatie van het nieuwe bestand kan opgeven. Omdat ze een nieuw bestand op het apparaat opslaan (in plaats van een bestaand bestand te overschrijven), verleent de bestandskiezer de app toestemming om naar het bestand te schrijven.

Beperkte mappen

Om gebruikers en hun gegevens te helpen beschermen, kan de browser de mogelijkheid van de gebruiker beperken om in bepaalde mappen op te slaan, bijvoorbeeld de mappen van het kernbesturingssysteem zoals Windows, de macOS-bibliotheekmappen, enz. Wanneer dit gebeurt, geeft de browser een prompt weer en vraagt gebruiker om een ​​andere map te kiezen.

Een bestaand bestand of map wijzigen

Een webapp kan een bestand op schijf niet wijzigen zonder expliciete toestemming van de gebruiker te krijgen.

Toestemmingsprompt

Als een persoon wijzigingen wil opslaan in een bestand waartoe hij eerder leestoegang heeft verleend, toont de browser een toestemmingsprompt, waarin toestemming wordt gevraagd aan de site om wijzigingen naar de schijf te schrijven. Het toestemmingsverzoek kan alleen worden geactiveerd door een gebruikersgebaar, bijvoorbeeld door op de knop Opslaan te klikken.

Er wordt een toestemmingsprompt weergegeven voordat een bestand wordt opgeslagen.
Prompt getoond aan gebruikers voordat de browser schrijfrechten krijgt voor een bestaand bestand.

Als alternatief kan een webapp die meerdere bestanden bewerkt, zoals een IDE, ook bij het openen om toestemming vragen om wijzigingen op te slaan.

Als de gebruiker Annuleren kiest en geen schrijftoegang verleent, kan de web-app geen wijzigingen in het lokale bestand opslaan. Het moet de gebruiker een alternatieve methode bieden om zijn gegevens op te slaan, bijvoorbeeld door een manier te bieden om het bestand te "downloaden" , gegevens op te slaan in de cloud, enz.

Transparantie

Omnibox-pictogram
Omnibox-pictogram dat aangeeft dat de gebruiker de website toestemming heeft gegeven om op te slaan in een lokaal bestand.

Zodra een gebruiker toestemming heeft verleend aan een webapp om een ​​lokaal bestand op te slaan, toont de browser een pictogram in de URL-balk. Als u op het pictogram klikt, wordt een pop-over geopend met de lijst met bestanden waartoe de gebruiker toegang heeft gegeven. De gebruiker kan die toegang eenvoudig intrekken als hij of zij dat wenst.

Persistentie van toestemming

De webapp kan wijzigingen in het bestand blijven opslaan zonder te vragen totdat alle tabbladen voor de oorsprong ervan zijn gesloten. Zodra een tabblad wordt gesloten, verliest de site alle toegang. De volgende keer dat de gebruiker de webapp gebruikt, wordt hem opnieuw gevraagd om toegang tot de bestanden.

Feedback

We zijn benieuwd naar uw ervaringen met de File System Access API.

Vertel ons over het API-ontwerp

Is er iets aan de API dat niet werkt zoals je had verwacht? Of ontbreken er methoden of eigenschappen die je nodig hebt om je idee te implementeren? Heeft u een vraag of opmerking over het beveiligingsmodel?

Probleem met de implementatie?

Heeft u een bug gevonden in de implementatie van Chrome? Of wijkt de uitvoering af van de specificaties?

  • Dien een bug in op https://new.crbug.com . Zorg ervoor dat u zoveel mogelijk details opneemt, evenals eenvoudige instructies voor het reproduceren, en stel Componenten in op Blink>Storage>FileSystem . Glitch werkt uitstekend voor het delen van snelle en gemakkelijke reproducties.

Bent u van plan de API te gebruiken?

Bent u van plan de File System Access API op uw site te gebruiken? Uw publieke steun helpt ons prioriteit te geven aan functies en laat andere browserleveranciers zien hoe belangrijk het is om deze te ondersteunen.

Handige Links

Dankbetuigingen

De File System Access API-specificatie is geschreven door Marijn Kruisselbrink .