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

De File System Access API stelt webapps in staat om rechtstreeks bestanden en mappen op het apparaat van de gebruiker te lezen of wijzigingen daarin op te slaan.

Gepubliceerd: 19 augustus 2024

De File System Access API stelt ontwikkelaars in staat krachtige webapplicaties te bouwen die interactie hebben met bestanden op het lokale apparaat van de gebruiker, zoals IDE's, foto- en videobewerkingsprogramma's, tekstverwerkers en meer. Nadat een gebruiker een webapplicatie toegang heeft verleend, stelt deze API de gebruiker in staat om rechtstreeks bestanden en mappen op het apparaat van de gebruiker te lezen of wijzigingen daarin op te slaan. Naast het lezen en schrijven van bestanden biedt de File System Access API ook de mogelijkheid om een ​​map te openen en de inhoud ervan te bekijken.

Als je al eerder met het lezen en schrijven van bestanden hebt gewerkt, zal veel van wat ik ga delen je bekend voorkomen. Ik raad je echter aan het toch te lezen, want niet alle systemen zijn hetzelfde.

De File System Access API wordt ondersteund door de meeste Chromium-browsers op Windows, macOS, ChromeOS, Linux en Android. Een opvallende uitzondering is Brave, waar deze momenteel alleen beschikbaar is achter een vlag .

Gebruikmaken van de File System Access API

Om de kracht en het nut van de File System Access API te demonstreren, heb ik een eenvoudige teksteditor geschreven. Hiermee kun je een tekstbestand openen, bewerken, de wijzigingen opslaan op de schijf of een nieuw bestand starten en de wijzigingen daarvan opslaan. Het is niets bijzonders, maar biedt voldoende functionaliteit om de concepten te begrijpen.

Browserondersteuning

Browser Support

  • Chrome: 86.
  • Rand: 86.
  • Firefox: niet ondersteund.
  • Safari: niet ondersteund.

Source

Kenmerkdetectie

Om te achterhalen of de File System Access API wordt ondersteund, controleer dan of de picker-methode waarin u geïnteresseerd bent, bestaat.

if ('showOpenFilePicker' in self) {
  // The `showOpenFilePicker()` method of the File System Access API is supported.
}

Probeer het eens.

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

Een bestand lezen uit het lokale bestandssysteem.

De eerste use case die ik wil behandelen, is de gebruiker vragen een bestand te kiezen, dat vervolgens te openen en van de schijf te lezen.

Vraag de gebruiker om een ​​bestand te kiezen om te lezen.

Het toegangspunt tot de File System Access API is window.showOpenFilePicker() . Wanneer deze functie wordt aangeroepen, verschijnt een dialoogvenster voor het selecteren van bestanden, waarin de gebruiker wordt gevraagd een bestand te selecteren. Nadat een bestand is geselecteerd, retourneert de API een array met bestandshandles. Een optionele parameter options ' stelt u in staat het gedrag van de bestandsselector te beïnvloeden, bijvoorbeeld door de gebruiker meerdere bestanden, mappen of verschillende bestandstypen te laten selecteren. Zonder opties kan de gebruiker slechts één bestand selecteren. Dit is ideaal voor een teksteditor.

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

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 met handles, in dit geval een array met één element, namelijk één FileSystemFileHandle die de eigenschappen en methoden bevat die nodig zijn om met het bestand te interageren.

Het is handig om een ​​verwijzing naar de bestandsdescriptor te bewaren, zodat u deze later kunt gebruiken. U hebt deze nodig om wijzigingen in het bestand op te slaan of om andere bestandsbewerkingen uit te voeren.

Een bestand lezen uit het bestandssysteem

Nu je een handle naar een bestand hebt, kun je de eigenschappen van het bestand opvragen of het bestand zelf benaderen. Voorlopig lees ik de inhoud ervan. Door handle.getFile() aan te roepen, krijg je een File object terug, dat een blob bevat. Om de gegevens uit de blob te halen, roep je 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 de schijf niet is gewijzigd. Als het bestand op de schijf wordt gewijzigd, wordt het File object onleesbaar en moet u getFile() opnieuw aanroepen om een ​​nieuw File object te verkrijgen waarmee u de gewijzigde gegevens kunt lezen.

Alles bij elkaar brengen

Wanneer gebruikers op de knop 'Openen' klikken, toont de browser een bestandsselector. 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 de wijzigingen terug naar het oorspronkelijke bestand met behulp van de eerder verkregen bestandsdescriptor. ' Opslaan als' daarentegen maakt een nieuw bestand aan en vereist dus een nieuwe bestandsdescriptor.

Een nieuw bestand maken

Om een ​​bestand op te slaan, roep je showSaveFilePicker() aan. Deze functie toont de bestandsselector in de "opslaan"-modus, zodat de gebruiker een nieuw bestand kan kiezen om op te slaan. Voor de teksteditor wilde ik ook dat er automatisch een .txt extensie werd toegevoegd, dus heb ik een aantal extra parameters opgegeven.

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

Sla de wijzigingen op de schijf op.

De volledige code voor het opslaan van wijzigingen in een bestand vind je in mijn demo van de teksteditor op GitHub . De belangrijkste interacties met het bestandssysteem staan ​​in fs-helpers.js . In de meest eenvoudige vorm ziet het proces er als volgt uit. Ik zal elke stap doorlopen 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();
}

Het schrijven van gegevens naar de schijf gebeurt met een FileSystemWritableFileStream -object, een subklasse van WritableStream . Maak de stream aan 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 schrijfrechten zijn verleend, vraagt ​​de browser de gebruiker om toestemming. Als er geen toestemming wordt verleend, gooit createWritable() een DOMException en kan de app niet naar het bestand schrijven. In de teksteditor worden de DOMException objecten afgehandeld in de saveFile() methode.

De write() -methode accepteert een string, wat nodig is voor een teksteditor. Maar de methode kan ook een BufferSource of een Blob accepteren. Je kunt er bijvoorbeeld een stream rechtstreeks naartoe sturen:

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.
}

Je kunt ook seek() of truncate() binnen de stream om het bestand op een specifieke positie bij te werken of de bestandsgrootte aan te passen.

Een voorgestelde bestandsnaam en startmap opgeven

In veel gevallen wilt u dat uw app een standaard bestandsnaam of locatie voorstelt. Een teksteditor wil bijvoorbeeld misschien een standaard bestandsnaam als Untitled Text.txt voorstellen in plaats van Untitled . Dit kunt u bereiken door een suggestedName eigenschap mee 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 je een teksteditor bouwt, wil je misschien dat het dialoogvenster voor het opslaan of openen van bestanden in de standaardmap ' documents begint, terwijl je voor een beeldbewerkingsprogramma misschien in de standaardmap ' pictures ' wilt beginnen. Je kunt een standaard startmap voorstellen door een ` startIn eigenschap door te geven aan de methoden showSaveFilePicker , showDirectoryPicker() of showOpenFilePicker , zoals hieronder weergegeven.

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

De lijst met bekende systeemdirectory's is als volgt:

  • desktop : De bureaubladmap van de gebruiker, indien deze bestaat.
  • documents : Map waarin documenten die door de gebruiker zijn gemaakt, doorgaans worden opgeslagen.
  • downloads : Map waar gedownloade bestanden doorgaans worden opgeslagen.
  • music : Map waar audiobestanden doorgaans worden opgeslagen.
  • pictures : Map waar foto's en andere stilstaande beelden doorgaans worden opgeslagen.
  • videos : Map waar video's of films doorgaans worden opgeslagen.

Naast de bekende systeemdirectory's kunt u ook een bestaande bestands- of directory-handle als waarde voor startIn doorgeven. Het dialoogvenster wordt dan in dezelfde directory geopend.

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

Het doel van verschillende bestandsselectoren specificeren

Soms hebben applicaties verschillende bestandsselectoren voor verschillende doeleinden. Een rich text-editor kan de gebruiker bijvoorbeeld de mogelijkheid bieden om tekstbestanden te openen, maar ook om afbeeldingen te importeren. Standaard opent elke bestandsselector de laatst onthouden locatie. Dit kunt u omzeilen door id waarden op te slaan voor elk type selector. Als een id is opgegeven, onthoudt de implementatie van de bestandsselector een aparte laatst gebruikte map voor die id .

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

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

Het opslaan van bestands- of map-handles in IndexedDB.

Bestands- en map-handles zijn serialiseerbaar, wat betekent dat je een bestands- of map-handle kunt opslaan in IndexedDB, of postMessage() kunt aanroepen om ze tussen dezelfde oorsprong op het hoogste niveau te verzenden.

Door bestands- of map-handles op te slaan in IndexedDB kun je de status bewaren, oftewel onthouden aan welke bestanden of mappen een gebruiker werkte. Dit maakt het mogelijk om een ​​lijst bij te houden van recent geopende of bewerkte bestanden, aan te bieden om het laatst geopende bestand opnieuw te openen wanneer de app wordt gestart, de vorige werkmap te herstellen, en meer. In de teksteditor sla ik een lijst op van de vijf meest recent geopende bestanden van de gebruiker, zodat deze bestanden later opnieuw toegankelijk zijn.

Het volgende codevoorbeeld laat zien hoe je een bestands- en een maphandle opslaat en ophaalt. Je kunt dit in actie zien op Glitch. (Ik gebruik de idb-keyval- bibliotheek voor de duidelijkheid.)

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

Opgeslagen bestands- of map-handles en machtigingen

Omdat machtigingen niet altijd tussen sessies behouden blijven , moet u controleren of de gebruiker toestemming heeft verleend voor het bestand of de map met behulp van queryPermission() . Zo niet, roep dan requestPermission() aan om de toestemming (opnieuw) aan te vragen. Dit werkt hetzelfde voor bestands- en map-handles. U moet respectievelijk fileOrDirectoryHandle.requestPermission(descriptor) of fileOrDirectoryHandle.queryPermission(descriptor) uitvoeren.

In de teksteditor heb ik een verifyPermission() methode gemaakt die controleert of de gebruiker al toestemming heeft gegeven 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 schrijfrechten tegelijk met het leesverzoek aan te vragen, heb ik het aantal toestemmingsprompts verminderd; de gebruiker ziet slechts één prompt bij het openen van het bestand en verleent daarmee zowel lees- als schrijfrechten.

Een map openen en de inhoud ervan weergeven

Om alle bestanden in een map op te sommen, roept u showDirectoryPicker() aan. De gebruiker selecteert een map in een picker, waarna een FileSystemDirectoryHandle wordt geretourneerd. Hiermee kunt u de bestanden in de map opsommen en openen. Standaard hebt u leesrechten voor de bestanden in de map, maar als u schrijfrechten nodig hebt, kunt u { mode: 'readwrite' } aan de methode doorgeven.

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

Als je daarnaast elk bestand afzonderlijk wilt benaderen met getFile() , bijvoorbeeld om de individuele bestandsgroottes te achterhalen, gebruik dan niet await op elk resultaat afzonderlijk, maar verwerk alle bestanden parallel, bijvoorbeeld met Promise.all() .

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

Het aanmaken of openen van bestanden en mappen in een directory.

Vanuit een map kunt u bestanden en mappen aanmaken of openen met behulp van de methoden getFileHandle() of getDirectoryHandle() . Door een optioneel options met de sleutel ' create en een booleaanse waarde ' true of false mee te geven, kunt u bepalen of een nieuw bestand of een nieuwe map moet worden aangemaakt als deze nog 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 map bepalen.

Bij het werken met bestanden of mappen in een directory kan het handig zijn om het pad van het betreffende item te achterhalen. Dit kan met de toepasselijk genaamde resolve() -methode. Voor het achterhalen van het pad kan het item een ​​directe of indirecte submap 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 directory verwijderen

Als je toegang hebt tot een map, kun je de bestanden en mappen daarin verwijderen met de methode removeEntry() . Voor mappen kan het verwijderen optioneel recursief zijn en alle submappen en de daarin aanwezige bestanden 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 je toegang hebt tot een bestands- of maphandle, roep dan 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 verplaatst naar een nieuwe locatie door de methode move() aan te roepen op de FileSystemHandle interface. FileSystemHandle heeft de subinterfaces FileSystemFileHandle en FileSystemDirectoryHandle . De move() -methode accepteert één of twee parameters. De eerste parameter kan een tekenreeks met de nieuwe naam zijn of een FileSystemDirectoryHandle naar de bestemmingsmap. In het laatste geval is de optionele tweede parameter een tekenreeks met de nieuwe naam, waardoor verplaatsen en hernoemen in één stap kunnen plaatsvinden.

// 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');

Drag and drop-integratie

De HTML Drag and Drop-interfaces stellen webapplicaties in staat om bestanden te accepteren die naar een webpagina worden gesleept en neergezet . Tijdens een sleepbewerking worden gesleepte bestanden en mappen respectievelijk gekoppeld aan bestandsvermeldingen en mapvermeldingen. De methode DataTransferItem.getAsFileSystemHandle() retourneert een promise met een FileSystemFileHandle -object als het gesleepte item een ​​bestand is, en een promise met een FileSystemDirectoryHandle -object als het gesleepte item een ​​map is. De volgende code laat dit in actie zien. 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 origin private file system is een opslag-eindpunt dat, zoals de naam al suggereert, privé is voor de oorsprong van de pagina. Hoewel browsers dit doorgaans implementeren door de inhoud van dit origin private file system ergens op de schijf op te slaan, is het niet de bedoeling dat de inhoud toegankelijk is voor de gebruiker. Evenmin wordt verwacht dat bestanden of mappen met namen die overeenkomen met de namen van submappen van het origin private file system bestaan. Hoewel de browser de indruk kan wekken dat er bestanden zijn, kan de browser deze "bestanden" intern – aangezien dit een origin private file system is – opslaan in een database of een andere datastructuur. Kortom, als u deze API gebruikt, moet u er niet vanuit gaan dat de aangemaakte bestanden één-op-één overeenkomen met de namen ergens op de harde schijf. U kunt zoals gebruikelijk werken met het origin private file system 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 });

Browser Support

  • Chrome: 86.
  • Rand: 86.
  • Firefox: 111.
  • Safari: 15.2.

Source

Toegang tot voor prestaties geoptimaliseerde bestanden vanuit het oorspronkelijke privébestandssysteem.

Het origin private file system biedt optionele toegang tot een speciaal type bestand dat sterk geoptimaliseerd is voor prestaties, bijvoorbeeld door in-place en exclusieve schrijftoegang tot de inhoud van een bestand te bieden. In Chromium 102 en later is er een extra methode op het origin private file system beschikbaar om bestandstoegang te vereenvoudigen: createSyncAccessHandle() (voor synchrone lees- en schrijfbewerkingen). Deze methode is beschikbaar via 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 });

Polyfilling

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

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

We hebben een bibliotheek ontwikkeld genaamd browser-fs-access die waar mogelijk gebruikmaakt van de File System Access API en in alle andere gevallen terugvalt op de op één na beste alternatieven.

Beveiliging en machtigingen

Het Chrome-team heeft de File System Access API ontworpen en geïmplementeerd op basis van de kernprincipes die zijn vastgelegd in Controlling Access to Powerful Web Platform Features , waaronder gebruikerscontrole, transparantie en gebruiksvriendelijkheid.

Een bestand openen of een nieuw bestand opslaan

Bestandsselector om een ​​bestand te openen en te lezen.
Een bestandsselector waarmee een bestaand bestand kan worden geopend om te lezen.

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

Bestandsselector om een ​​bestand op de schijf op te slaan.
Een bestandsselector waarmee een bestand op de schijf kan worden opgeslagen.

Op dezelfde manier toont de browser, wanneer een webapplicatie een nieuw bestand wil opslaan, een bestandsselector waarmee de gebruiker de naam en locatie van het nieuwe bestand kan opgeven. Omdat er een nieuw bestand op het apparaat wordt opgeslagen (in plaats van een bestaand bestand te overschrijven), geeft de bestandsselector de app toestemming om naar het bestand te schrijven.

Beperkte mappen

Om gebruikers en hun gegevens te beschermen, kan de browser de mogelijkheid van de gebruiker om bestanden op te slaan in bepaalde mappen beperken, bijvoorbeeld in de standaardmappen van het besturingssysteem zoals Windows, of in de bibliotheekmappen van macOS. In dat geval geeft de browser een melding weer en vraagt ​​de gebruiker een andere map te kiezen.

Een bestaand bestand of een bestaande map wijzigen

Een webapplicatie kan een bestand op de schijf niet wijzigen zonder expliciete toestemming van de gebruiker.

Toestemmingsverzoek

Als iemand wijzigingen wil opslaan in een bestand waaraan eerder leesrechten zijn verleend, toont de browser een toestemmingsverzoek waarin de site wordt gevraagd de wijzigingen naar de schijf te schrijven. Het toestemmingsverzoek kan alleen worden geactiveerd door een gebruikersactie, bijvoorbeeld door op een knop 'Opslaan' te klikken.

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

Een webapplicatie die meerdere bestanden bewerkt, zoals een IDE, kan er ook voor kiezen om bij het openen toestemming te vragen om de wijzigingen op te slaan.

Als de gebruiker 'Annuleren' kiest en geen schrijftoegang verleent, kan de webapplicatie de wijzigingen in het lokale bestand niet opslaan. De applicatie moet een alternatieve methode bieden om de gegevens op te slaan, bijvoorbeeld door een manier te bieden om het bestand te 'downloaden' of door gegevens in de cloud op te slaan.

Transparantie

Omnibox-pictogram
Het pictogram in de adresbalk geeft aan dat de gebruiker de website toestemming heeft gegeven om gegevens lokaal op te slaan.

Zodra een gebruiker een webapplicatie toestemming heeft gegeven om een ​​lokaal bestand op te slaan, verschijnt er een pictogram in de adresbalk van de browser. Door op dit pictogram te klikken, wordt een pop-upvenster geopend met een lijst van bestanden waartoe de gebruiker toegang heeft verleend. De gebruiker kan deze toegang te allen tijde intrekken.

Persistentie van toestemming

De webapplicatie kan wijzigingen in het bestand blijven opslaan zonder opnieuw om toegang te vragen totdat alle tabbladen van de betreffende website zijn gesloten. Zodra een tabblad is gesloten, verliest de website alle toegang. De volgende keer dat de gebruiker de webapplicatie gebruikt, wordt er opnieuw om toegang tot de bestanden gevraagd.

Feedback

We willen graag meer horen over uw ervaringen met de File System Access API.

Vertel ons iets over het API-ontwerp.

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

Probleem met de implementatie?

Heb je een bug gevonden in de implementatie van Chrome? Of wijkt de implementatie af van de specificatie?

  • Meld een bug op https://new.crbug.com . Vermeld daarbij zoveel mogelijk details, instructies voor het reproduceren van het probleem en stel de componenten in op Blink>Storage>FileSystem .

Ben je van plan de API te gebruiken?

Bent u van plan de File System Access API op uw website te gebruiken? Uw publieke steun helpt ons bij het prioriteren van 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 .