Interfejs File System Access API: upraszcza dostęp do plików lokalnych

Interfejs File System Access API umożliwia aplikacjom internetowym odczytywanie lub zapisywanie zmian bezpośrednio w plikach i folderach na urządzeniu użytkownika.

Opublikowano: 19 sierpnia 2024 r.

File System Access API umożliwia programistom tworzenie zaawansowanych aplikacji internetowych, które wchodzą w interakcję z plikami na urządzeniu lokalnym użytkownika, takich jak IDE, edytory zdjęć i filmów, edytory tekstu i inne. Gdy użytkownik przyzna aplikacji internetowej dostęp, ten interfejs API umożliwi jej odczytywanie lub zapisywanie zmian bezpośrednio w plikach i folderach na urządzeniu użytkownika. Interfejs File System Access API umożliwia nie tylko odczytywanie i zapisywanie plików, ale też otwieranie katalogów i wyświetlanie ich zawartości.

Jeśli masz już doświadczenie w odczytywaniu i zapisywaniu plików, wiele z tego, co za chwilę powiem, będzie Ci znane. Zachęcam Cię jednak do zapoznania się z nim, ponieważ nie wszystkie systemy są takie same.

Interfejs File System Access API jest obsługiwany w większości przeglądarek Chromium w systemach Windows, macOS, ChromeOS, Linux i Android. Wyjątkiem jest przeglądarka Brave, w której funkcja ta jest obecnie dostępna tylko za flagą.

Korzystanie z interfejsu File System Access API

Aby pokazać możliwości i przydatność interfejsu File System Access API, napisałem edytor tekstu w jednym pliku. Umożliwia otwieranie plików tekstowych, edytowanie ich, zapisywanie zmian na dysku lub tworzenie nowych plików i zapisywanie zmian na dysku. Nie jest to nic wyszukanego, ale wystarczy, aby pomóc Ci zrozumieć koncepcje.

Obsługa przeglądarek

Browser Support

  • Chrome: 86.
  • Edge: 86.
  • Firefox: not supported.
  • Safari: not supported.

Source

Wykrywanie cech

Aby sprawdzić, czy interfejs File System Access API jest obsługiwany, zobacz, czy istnieje interesująca Cię metoda selektora.

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

Wypróbuj

Zobacz interfejs File System Access API w działaniu w demonstracji edytora tekstu.

odczytywać pliki z lokalnego systemu plików,

Pierwszy przypadek użycia, którym chcę się zająć, to poproszenie użytkownika o wybranie pliku, a następnie otwarcie i odczytanie tego pliku z dysku.

Poproś użytkownika o wybranie pliku do odczytania

Punktem wejściowym interfejsu File System Access API jest window.showOpenFilePicker(). Po wywołaniu wyświetla okno wyboru pliku i prosi użytkownika o wybranie pliku. Gdy użytkownik wybierze plik, interfejs API zwróci tablicę uchwytów plików. Opcjonalny parametr options pozwala wpływać na działanie selektora plików, np. umożliwiając użytkownikowi wybieranie wielu plików, katalogów lub różnych typów plików. Jeśli nie określisz żadnych opcji, selektor plików umożliwi użytkownikowi wybranie jednego pliku. Jest to idealne rozwiązanie w przypadku edytora tekstu.

Podobnie jak w przypadku wielu innych zaawansowanych interfejsów API, wywoływanie funkcji showOpenFilePicker() musi odbywać się w bezpiecznym kontekście i w ramach gestu użytkownika.

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

Gdy użytkownik wybierze plik, showOpenFilePicker() zwraca tablicę uchwytów, w tym przypadku tablicę jednoelementową z jednym elementem FileSystemFileHandle, który zawiera właściwości i metody potrzebne do interakcji z plikiem.

Warto zachować odniesienie do uchwytu pliku, aby można go było użyć później. Będzie on potrzebny do zapisania zmian w pliku lub wykonania innych operacji na pliku.

odczytywać pliki z systemu plików,

Teraz, gdy masz uchwyt do pliku, możesz pobrać jego właściwości lub uzyskać do niego dostęp. Na razie przeczytam jego zawartość. Wywołanie handle.getFile() zwraca obiekt File, który zawiera obiekt binarny. Aby pobrać dane z obiektu blob, wywołaj jedną z jego metod (slice(), stream(), text() lub arrayBuffer()).

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

Obiekt File zwracany przez FileSystemFileHandle.getFile() jest dostępny do odczytu tylko wtedy, gdy plik na dysku nie uległ zmianie. Jeśli plik na dysku zostanie zmodyfikowany, obiekt File stanie się nieczytelny i konieczne będzie ponowne wywołanie funkcji getFile(), aby uzyskać nowy obiekt File i odczytać zmienione dane.

Łączę wszystko w całość

Gdy użytkownicy klikną przycisk Otwórz, w przeglądarce pojawi się selektor plików. Po wybraniu pliku aplikacja odczytuje jego zawartość i umieszcza ją w <textarea>.

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

Zapisz plik w lokalnym systemie plików.

W edytorze tekstu plik można zapisać na 2 sposoby: Zapisz i Zapisz jako. Zapisz zapisuje zmiany w oryginalnym pliku przy użyciu wcześniej pobranego uchwytu pliku. Jednak Zapisz jako tworzy nowy plik, a tym samym wymaga nowego uchwytu pliku.

Utwórz nowy plik

Aby zapisać plik, wywołaj funkcję showSaveFilePicker(), która wyświetla selektor plików w trybie „zapisywania”, umożliwiając użytkownikowi wybranie nowego pliku, który ma być użyty do zapisania. W przypadku edytora tekstu chciałem też, aby automatycznie dodawał rozszerzenie .txt, więc podałem dodatkowe parametry.

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

Zapisywanie zmian na dysku

Cały kod do zapisywania zmian w pliku znajdziesz w mojej wersji demonstracyjnej edytora tekstu na GitHub. Podstawowe interakcje z systemem plików znajdują się w fs-helpers.js. W najprostszej postaci proces wygląda tak, jak w poniższym kodzie. Omówię każdy krok i go wyjaśnię.

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

Do zapisywania danych na dysku używany jest obiekt FileSystemWritableFileStream, który jest podklasą obiektu WritableStream. Utwórz strumień, wywołując createWritable() na obiekcie uchwytu pliku. Gdy wywoływana jest funkcja createWritable(), przeglądarka najpierw sprawdza, czy użytkownik przyznał uprawnienia do zapisu w pliku. Jeśli uprawnienia do zapisu nie zostały przyznane, przeglądarka poprosi użytkownika o ich przyznanie. Jeśli uprawnienia nie zostaną przyznane, createWritable() zgłosi wyjątek DOMException, a aplikacja nie będzie mogła zapisywać danych w pliku. W edytorze tekstu obiekty DOMException są obsługiwane w metodzie saveFile().

Metoda write() przyjmuje ciąg znaków, który jest potrzebny w edytorze tekstu. Może też przyjmować BufferSource lub Blob. Możesz na przykład przekierować strumień bezpośrednio do niego:

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

Możesz też seek() lub truncate() w strumieniu, aby zaktualizować plik w określonym miejscu lub zmienić jego rozmiar.

Określanie sugerowanej nazwy pliku i katalogu początkowego

W wielu przypadkach warto, aby aplikacja sugerowała domyślną nazwę pliku lub lokalizację. Na przykład edytor tekstu może sugerować domyślną nazwę pliku Untitled Text.txt zamiast Untitled. Możesz to zrobić, przekazując usługę suggestedName w ramach opcji showSaveFilePicker.

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

To samo dotyczy domyślnego katalogu początkowego. Jeśli tworzysz edytor tekstu, możesz chcieć, aby okno dialogowe zapisywania lub otwierania pliku było domyślnie otwierane w folderze documents, a w przypadku edytora obrazów – w folderze pictures. Możesz zaproponować domyślny katalog początkowy, przekazując właściwość startIn do metody showSaveFilePicker, showDirectoryPicker() lub showOpenFilePicker w ten sposób.

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

Lista znanych katalogów systemowych:

  • desktop: katalog na komputerze użytkownika, jeśli taki istnieje.
  • documents: katalog, w którym zwykle są przechowywane dokumenty utworzone przez użytkownika.
  • downloads: katalog, w którym zwykle są przechowywane pobrane pliki.
  • music: katalog, w którym zwykle są przechowywane pliki audio.
  • pictures: Katalog, w którym zwykle są przechowywane zdjęcia i inne obrazy statyczne.
  • videos: katalog, w którym zwykle są przechowywane filmy.

Oprócz znanych katalogów systemowych możesz też przekazać istniejący uchwyt pliku lub katalogu jako wartość parametru startIn. Okno dialogowe otworzy się w tym samym katalogu.

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

Określanie przeznaczenia różnych selektorów plików

Czasami aplikacje mają różne selektory do różnych celów. Na przykład edytor tekstu sformatowanego może umożliwiać użytkownikowi otwieranie plików tekstowych, ale także importowanie obrazów. Domyślnie każdy selektor plików otwiera się w ostatnio zapamiętanej lokalizacji. Możesz to obejść, przechowując id wartości dla każdego typu selektora. Jeśli określono id, implementacja selektora plików zapamiętuje oddzielny ostatnio używany katalog dla tego id.

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

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

przechowywanie uchwytów plików lub katalogów w IndexedDB;

Uchwyty plików i uchwyty katalogów można serializować, co oznacza, że możesz zapisać uchwyt pliku lub katalogu w IndexedDB albo wywołać postMessage(), aby wysłać je między tymi samymi źródłami najwyższego poziomu.

Zapisywanie uchwytów plików lub katalogów w IndexedDB oznacza, że możesz przechowywać stan lub zapamiętywać, nad którymi plikami lub katalogami pracował użytkownik. Dzięki temu można zachować listę ostatnio otwartych lub edytowanych plików, zaproponować ponowne otwarcie ostatniego pliku po otwarciu aplikacji, przywrócić poprzedni katalog roboczy itp. W edytorze tekstu przechowuję listę 5 ostatnio otwartych plików, dzięki czemu użytkownik może ponownie uzyskać do nich dostęp.

Poniższy przykład kodu pokazuje przechowywanie i pobieranie uchwytu pliku i uchwytu katalogu. Możesz zobaczyć, jak to działa w Glitchu. (Dla zwięzłości używam biblioteki idb-keyval).

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

Zapisane uchwyty i uprawnienia do plików lub katalogów

Ponieważ uprawnienia nie zawsze są zachowywane między sesjami, musisz sprawdzić, czy użytkownik przyznał uprawnienia do pliku lub katalogu, używając queryPermission(). Jeśli nie, zadzwońrequestPermission(), aby (ponownie) poprosić o kod. Działa to tak samo w przypadku uchwytów plików i katalogów. Musisz uruchomić odpowiednio fileOrDirectoryHandle.requestPermission(descriptor) lub fileOrDirectoryHandle.queryPermission(descriptor).

W edytorze tekstu utworzyłem metodę verifyPermission(), która sprawdza, czy użytkownik przyznał już uprawnienia, a w razie potrzeby wysyła żądanie.

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

Prosząc o uprawnienia do zapisu wraz z prośbą o uprawnienia do odczytu, zmniejszyłem liczbę wyświetlanych próśb o uprawnienia. Użytkownik widzi jedną prośbę podczas otwierania pliku i przyznaje uprawnienia do odczytu i zapisu.

otwieranie katalogu i wyliczanie jego zawartości;

Aby wyliczyć wszystkie pliki w katalogu, wywołaj funkcję showDirectoryPicker(). Użytkownik wybiera katalog w selektorze, po czym zwracany jest FileSystemDirectoryHandle, który umożliwia wyliczanie plików w katalogu i uzyskiwanie do nich dostępu. Domyślnie będziesz mieć dostęp do odczytu plików w katalogu, ale jeśli potrzebujesz dostępu do zapisu, możesz przekazać do metody wartość { mode: 'readwrite' }.

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

Jeśli dodatkowo musisz uzyskać dostęp do każdego pliku za pomocą getFile(), np. aby uzyskać rozmiary poszczególnych plików, nie używaj await w przypadku każdego wyniku po kolei, ale raczej przetwarzaj wszystkie pliki równolegle, np. za pomocą 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));
});

tworzenie plików i folderów w katalogu lub uzyskiwanie do nich dostępu,

W katalogu możesz tworzyć pliki i foldery oraz uzyskiwać do nich dostęp za pomocą metody getFileHandle() lub getDirectoryHandle(). Przekazując opcjonalny obiekt options z kluczem create i wartością logiczną true lub false, możesz określić, czy nowy plik lub folder ma zostać utworzony, jeśli nie istnieje.

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

Rozwiązywanie ścieżki elementu w katalogu

Podczas pracy z plikami lub folderami w katalogu może być przydatne rozwiązanie ścieżki danego elementu. Można to zrobić za pomocą metody resolve(). Aby rozwiązać problem, element może być bezpośrednim lub pośrednim elementem podrzędnym katalogu.

// 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"]

Usuwanie plików i folderów w katalogu

Jeśli masz dostęp do katalogu, możesz usunąć zawarte w nim pliki i foldery za pomocą metody removeEntry(). W przypadku folderów usuwanie może być opcjonalnie rekurencyjne i obejmować wszystkie podfoldery oraz zawarte w nich pliki.

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

Bezpośrednie usuwanie pliku lub folderu

Jeśli masz dostęp do uchwytu pliku lub katalogu, wywołaj funkcję remove() na obiekcie FileSystemFileHandle lub FileSystemDirectoryHandle, aby go usunąć.

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

zmienianie nazw i przenoszenie plików oraz folderów,

Pliki i foldery można przenosić do nowej lokalizacji lub zmieniać ich nazwy, wywołując funkcję move() w interfejsie FileSystemHandle. FileSystemHandle ma interfejsy podrzędne FileSystemFileHandleFileSystemDirectoryHandle. Metoda move() przyjmuje 1 lub 2 parametry. Pierwszy argument może być ciągiem znaków z nową nazwą lub FileSystemDirectoryHandle do folderu docelowego. W tym drugim przypadku opcjonalny drugi parametr to ciąg znaków z nową nazwą, więc przenoszenie i zmiana nazwy mogą nastąpić w jednym kroku.

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

Integracja przez przeciąganie i upuszczanie

Interfejsy HTML Drag and Drop umożliwiają aplikacjom internetowym akceptowanie przeciąganych i upuszczanych plików na stronie internetowej. Podczas operacji przeciągania i upuszczania przeciągane pliki i katalogi są powiązane odpowiednio z wpisami plików i wpisami katalogów. Metoda DataTransferItem.getAsFileSystemHandle() zwraca obietnicę z obiektem FileSystemFileHandle, jeśli przeciągany element jest plikiem, oraz obietnicę z obiektem FileSystemDirectoryHandle, jeśli przeciągany element jest katalogiem. Poniższy przykład pokazuje to w praktyce. Pamiętaj, że interfejs przeciągania i upuszczania DataTransferItem.kind jest "file" zarówno w przypadku plików, jak i katalogów, natomiast FileSystemHandle.kind interfejsu File System Access API jest "file" w przypadku plików i "directory" w przypadku katalogów.

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

Dostęp do prywatnego systemu plików pochodzenia

Pochodny prywatny system plików to punkt końcowy pamięci masowej, który, jak sama nazwa wskazuje, jest prywatny dla pochodzenia strony. Przeglądarki zwykle implementują to rozwiązanie, zapisując zawartość tego prywatnego systemu plików pochodzenia na dysku, ale nie jest to przeznaczone do tego, aby zawartość była dostępna dla użytkownika. Podobnie nie oczekuje się, że pliki lub katalogi o nazwach zgodnych z nazwami elementów podrzędnych pochodzącego z prywatnego systemu plików będą istniały. Chociaż przeglądarka może sprawiać wrażenie, że istnieją pliki, wewnętrznie – ponieważ jest to prywatny system plików pochodzenia – może przechowywać te „pliki” w bazie danych lub innej strukturze danych. Jeśli używasz tego interfejsu API, nie oczekuj, że utworzone pliki będą w stosunku 1:1 odpowiadać plikom na dysku twardym. Gdy uzyskasz dostęp do katalogu głównego FileSystemDirectoryHandle, możesz normalnie korzystać z prywatnego systemu plików pochodzenia.

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.
  • Edge: 86.
  • Firefox: 111.
  • Safari: 15.2.

Source

uzyskiwanie dostępu do plików zoptymalizowanych pod kątem wydajności z prywatnego systemu plików pochodzenia;

Pochodny prywatny system plików zapewnia opcjonalny dostęp do specjalnego rodzaju pliku, który jest wysoce zoptymalizowany pod kątem wydajności, np. poprzez oferowanie dostępu do zapisu w miejscu i na wyłączność do treści pliku. W Chromium w wersji 102 i nowszych w prywatnym systemie plików pochodzenia dostępna jest dodatkowa metoda upraszczająca dostęp do plików: createSyncAccessHandle() (do synchronicznych operacji odczytu i zapisu). Jest ona dostępna w FileSystemFileHandle, ale tylko w 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 });

Wypełnianie

Nie można w pełni uzupełnić metod interfejsu File System Access API.

  • Metodę showOpenFilePicker() można przybliżyć za pomocą elementu <input type="file">.
  • Metodę showSaveFilePicker() można symulować za pomocą elementu <a download="file_name">, ale powoduje to programowe pobieranie i nie pozwala na zastępowanie istniejących plików.
  • Metodę showDirectoryPicker() można w pewnym stopniu naśladować za pomocą niestandardowego elementu <input type="file" webkitdirectory>.

Opracowaliśmy bibliotekę o nazwie browser-fs-access, która w miarę możliwości korzysta z interfejsu File System Access API, a w pozostałych przypadkach używa kolejnych najlepszych opcji.

Zabezpieczenia i uprawnienia

Zespół Chrome zaprojektował i wdrożył interfejs File System Access API zgodnie z podstawowymi zasadami określonymi w artykule Controlling Access to Powerful Web Platform Features, w tym z zasadami dotyczącymi kontroli i przejrzystości dla użytkowników oraz ergonomii.

Otwieranie pliku lub zapisywanie nowego pliku

Okno wyboru plików do otwierania plików do odczytu
Okno wyboru plików służące do otwierania istniejącego pliku w celu odczytania.

Podczas otwierania pliku użytkownik przyznaje uprawnienia do odczytu pliku lub katalogu za pomocą selektora plików. Otwarty selektor plików można wyświetlić tylko za pomocą gestu użytkownika, gdy jest on obsługiwany w bezpiecznym kontekście. Jeśli użytkownicy zmienią zdanie, mogą anulować wybór w selektorze plików, a witryna nie uzyska dostępu do żadnych danych. Działa tak samo jak element <input type="file">.

selektor plików do zapisywania plików na dysku;
Selektor plików używany do zapisywania pliku na dysku.

Podobnie, gdy aplikacja internetowa chce zapisać nowy plik, przeglądarka wyświetla selektor zapisywania pliku, który umożliwia użytkownikowi określenie nazwy i lokalizacji nowego pliku. Ponieważ zapisują nowy plik na urządzeniu (zamiast zastępować istniejący), selektor plików przyznaje aplikacji uprawnienia do zapisu w pliku.

Foldery z ograniczonym dostępem

Aby chronić użytkowników i ich dane, przeglądarka może ograniczyć możliwość zapisywania plików w określonych folderach, np. w folderach systemu operacyjnego, takich jak Windows czy foldery biblioteki macOS. W takiej sytuacji przeglądarka wyświetli prośbę o wybranie innego folderu.

Modyfikowanie istniejącego pliku lub katalogu

Aplikacja internetowa nie może modyfikować pliku na dysku bez wyraźnej zgody użytkownika.

Prośba o uprawnienia

Jeśli osoba chce zapisać zmiany w pliku, do którego wcześniej przyznała dostęp do odczytu, w przeglądarce wyświetli się prośba o uprawnienia, w której strona poprosi o możliwość zapisania zmian na dysku. Żądanie uprawnień może być wywołane tylko przez działanie użytkownika, np. kliknięcie przycisku Zapisz.

Przed zapisaniem pliku wyświetla się prośba o przyznanie uprawnień.
Prośba wyświetlana użytkownikom, zanim przeglądarce zostaną przyznane uprawnienia do zapisu w istniejącym pliku.

Aplikacja internetowa, która edytuje wiele plików, np. zintegrowane środowisko programistyczne, może też poprosić o zezwolenie na zapisywanie zmian w momencie otwierania.

Jeśli użytkownik kliknie Anuluj i nie przyzna uprawnień do zapisu, aplikacja internetowa nie będzie mogła zapisać zmian w pliku lokalnym. Powinna ona udostępniać użytkownikowi alternatywną metodę zapisywania danych, na przykład umożliwiać „pobranie” pliku lub zapisywanie danych w chmurze.

Przejrzystość

Ikona omniboksu
Ikona na pasku adresu wskazująca, że użytkownik przyznał witrynie uprawnienia do zapisywania w pliku lokalnym.

Gdy użytkownik przyzna aplikacji internetowej uprawnienia do zapisywania pliku lokalnego, w pasku adresu przeglądarki pojawi się ikona. Kliknięcie ikony otwiera wyskakujące okienko z listą plików, do których użytkownik przyznał dostęp. Użytkownik może w każdej chwili cofnąć ten dostęp.

Trwałość uprawnień

Aplikacja internetowa może nadal zapisywać zmiany w pliku bez wyświetlania prośby, dopóki nie zostaną zamknięte wszystkie karty jej pochodzenia. Po zamknięciu karty witryna traci do niej dostęp. Gdy użytkownik następnym razem użyje aplikacji internetowej, ponownie pojawi się prośba o dostęp do plików.

Prześlij opinię

Chcemy poznać Twoje doświadczenia związane z API dostępu do systemu plików.

Opisz projekt interfejsu API

Czy w API jest coś, co nie działa tak, jak oczekujesz? Czy brakuje metod lub właściwości, które są potrzebne do realizacji Twojego pomysłu? Masz pytania lub uwagi dotyczące modelu zabezpieczeń?

Masz problem z implementacją?

Czy w implementacji Chrome występuje błąd? A może implementacja różni się od specyfikacji?

  • Zgłoś błąd na stronie https://new.crbug.com. Podaj jak najwięcej szczegółów i instrukcji odtwarzania oraz ustaw Komponenty na Blink>Storage>FileSystem.

Planujesz korzystać z interfejsu API?

Planujesz używać interfejsu File System Access API w swojej witrynie? Twoje publiczne poparcie pomaga nam ustalać priorytety dotyczące funkcji i pokazuje innym dostawcom przeglądarek, jak ważne jest ich obsługiwanie.

Przydatne linki

Podziękowania

Specyfikację interfejsu File System Access API napisał Marijn Kruisselbrink.