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

Interfejs API dostępu do systemu plików umożliwia aplikacjom internetowym odczytywanie lub zapisywanie zmian bezpośrednio w plikach i folderach na urządzeniu użytkownika.

Czym jest interfejs File System Access API?

Interfejs File System Access API umożliwia deweloperom tworzenie zaawansowanych aplikacji internetowych, które mogą wchodzić w interakcje z plikami na urządzeniu lokalnym użytkownika, np. z IDE, edytorami zdjęć i filmów czy edytorami tekstu. Gdy użytkownik przyzna aplikacji internetowej dostęp, interfejs API pozwoli mu odczytywać lub zapisywać zmiany bezpośrednio w plikach i folderach na urządzeniu. Oprócz odczytu i zapisu plików interfejs File System Access API umożliwia otwieranie katalogów i wyliczanie ich zawartości.

Jeśli wcześniej pracowałeś/pracowałaś z czytaniem i pisaniem plików, wiele z tych informacji będzie Ci znajomych. Zachęcam jednak do jego przeczytania, ponieważ nie wszystkie systemy są takie same.

Interfejs API dostępu do systemu plików jest obsługiwany w większości przeglądarek Chromium w systemach Windows, macOS, ChromeOS i Linux. Wyjątkiem jest przeglądarka Brave, w której obecnie ta funkcja jest dostępna tylko za pomocą flagi. Pracujemy nad obsługą Androida w ramach zgłoszenia crbug.com/1011535.

Korzystanie z interfejsu File System Access API

Aby pokazać potęgę i użyteczność interfejsu File System Access API, napisałem pojedynczy edytor tekstu. Umożliwia otwieranie plików tekstowych, ich edytowanie i zapisywanie zmian na dysku lub tworzenie nowych plików i zapisywanie zmian na dysku. Nie jest to nic wyszukanego, ale wystarczy, aby zrozumieć pojęcia.

Obsługa przeglądarek

Obsługa przeglądarek

  • Chrome: 86.
  • Edge: 86.
  • Firefox: nieobsługiwane.
  • Safari: nieobsługiwane.

Źródło

Wykrywanie cech

Aby dowiedzieć się, czy interfejs File System API jest obsługiwany, sprawdź, czy interesująca Cię metoda selektora istnieje.

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 prezentacji edytowania tekstu.

Odczytywanie pliku z lokalnego systemu plików

Pierwszy przypadek użycia, który chcę omówić, polega na tym, że użytkownik musi wybrać plik, a potem otworzyć go i odczytać z dysku.

Poproś użytkownika o wybranie pliku do odczytania

Punkt wejścia do interfejsu File System Access API to: window.showOpenFilePicker(). Gdy zostanie wywołany, wyświetla okno wyboru pliku i prosi użytkownika o wybranie pliku. Po wybraniu pliku interfejs API zwraca tablicę identyfikatorów plików. Opcjonalny parametr options pozwala wpływać na działanie selektora plików, na przykład poprzez umożliwienie użytkownikowi wybrania wielu plików, katalogów lub różnych typów plików. Bez żadnych określonych opcji selektor plików pozwala użytkownikowi wybrać jeden plik. To idealne rozwiązanie dla edytora tekstu.

Podobnie jak w przypadku wielu innych zaawansowanych interfejsów API, wywołanie showOpenFilePicker() musi być wykonane w bezpiecznym kontekście i wymaga 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, funkcja showOpenFilePicker() zwraca tablicę uchwytów, w tym przypadku tablicę o jednym elemencie z jednym FileSystemFileHandle, która zawiera właściwości i metody potrzebne do interakcji z plikiem.

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

Odczyt pliku z systemu plików

Teraz, gdy masz uchwyt pliku, możesz uzyskać jego właściwości lub uzyskać dostęp do samego pliku. Na razie przeczytam jego treść. Wywołanie handle.getFile() zwraca obiekt File zawierający blob. Aby uzyskać dane z bloba, wywołaj jedną z metod (slice(), stream(), text() lub arrayBuffer()).

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

Obiekt File zwracany przez funkcję FileSystemFileHandle.getFile() jest czytelny tylko wtedy, gdy podstawowy plik na dysku nie uległ zmianie. Jeśli plik na dysku zostanie zmodyfikowany, obiekt File stanie się nieczytelny i musisz ponownie wywołać funkcję getFile(), aby uzyskać nowy obiekt File, który odczyta zmienione dane.

Podsumowanie

Gdy użytkownicy klikną przycisk Otwórz, przeglądarka wyświetli selektor plików. Po wybraniu pliku aplikacja odczytuje jego zawartość i przekaże ją do <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 możesz zapisać plik na 2 sposoby: ZapiszZapisz jako. Zapisz zapisuje zmiany w pierwotnym pliku, używając wcześniej pobranego uchwytu pliku. Natomiast opcja Zapisz jako tworzy nowy plik, co wymaga użycia nowego uchwytu pliku.

Utwórz nowy plik

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

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

Zapisz zmiany na dysku

Cały kod służący do zapisywania zmian w pliku znajdziesz w mojej wersji demonstracyjnej edytora tekstu na GitHub. Interakcje z systemem plików znajdują się w pliku fs-helpers.js. W najprostszej formie proces wygląda tak: Przeprowadzę Cię przez 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 służy obiekt FileSystemWritableFileStream, który jest podklasą obiektu WritableStream. Utwórz strumień, wywołując funkcję createWritable() obiektu filehandle. Gdy wywoływana jest funkcja createWritable(), przeglądarka najpierw sprawdza, czy użytkownik przyznał uprawnienia do zapisu do pliku. Jeśli nie przyznano uprawnień do zapisu, przeglądarka poprosi użytkownika o je przyznanie. Jeśli uprawnienia nie zostaną przyznane, createWritable() rzuci błąd DOMException, a aplikacja nie będzie mogła zapisać pliku. W edytorze tekstu obiekty DOMException są obsługiwane w metodie saveFile().

Metoda write() przyjmuje ciąg znaków, który jest potrzebny edytorowi tekstu. Może ona też przyjmować BufferSource lub Blob. Możesz na przykład przesłać dane bezpośrednio do:

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ż użyć seek() lub truncate() w strumieniu, aby zaktualizować plik w określonej pozycji lub zmienić jego rozmiar.

Podawanie sugerowanej nazwy pliku i katalogu początkowego

W wielu przypadkach możesz chcieć, aby aplikacja sugerowała domyślną nazwę pliku lub lokalizację. Na przykład edytor tekstu może zaproponować domyślną nazwę pliku Untitled Text.txt zamiast Untitled. Aby to zrobić, możesz przekazać właściwość suggestedName jako część 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 startowego. Jeśli tworzysz edytor tekstu, możesz uruchomić okno zapisywania lub otwierania pliku w domyślnym folderze documents, a w przypadku edytora obrazów – w domyślnym folderze pictures. Możesz zasugerować domyślny katalog startowy, przekazując właściwość startIn do metod showSaveFilePicker, showDirectoryPicker() lub showOpenFilePicker.

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

Lista znanych katalogów systemowych:

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

Oprócz znanych katalogów systemowych możesz też przekazać istniejący plik lub uchwyt katalogu jako wartość parametru startIn. Okno 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 celu 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 też importowanie obrazów. Domyślnie każdy selektor plików otwiera się w ostatnio zapamiętanej lokalizacji. Możesz to obejść, przechowując wartości id dla każdego typu selektora. Jeśli określono wartość id, implementacja selektora plików zapamięta osobny 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;

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

Zapisywanie uchwytów plików lub katalogów w IndexedDB umożliwia przechowywanie stanu lub zapamiętywanie informacji o tym, nad jakimi plikami lub katalogami pracował użytkownik. Dzięki temu możesz zachować listę ostatnio otwieranych lub edytowanych plików, zaoferować ponowne otwarcie ostatniego pliku po uruchomieniu aplikacji, przywrócić poprzedni katalog roboczy i wykonywać inne czynności. W edytorze tekstu przechowuję listę 5 najnowszych plików, które użytkownik otworzył, dzięki czemu można do nich ponownie uzyskać dostęp.

Poniższy przykład kodu pokazuje przechowywanie i pobieranie uchwytu pliku i uchwytu katalogu. Możesz sprawdzić, jak to działa na stronie Glitch. (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);
  }
});

Złożone uchwyty i uprawnienia do pliku lub katalogu

Ponieważ uprawnienia nie są zawsze zachowywane między sesjami, należy sprawdzić, czy użytkownik przyznał uprawnienia do pliku lub katalogu za pomocą queryPermission(). Jeśli nie, zadzwoń pod numer requestPermission(), aby poprosić o jego ponowne wysłanie. To samo dotyczy uchwytów plików i katalogów. W tym celu musisz uruchomić odpowiednio fileOrDirectoryHandle.requestPermission(descriptor) lub fileOrDirectoryHandle.queryPermission(descriptor).

W edytorze tekstu utworzyłem metodę verifyPermission(), która sprawdza, czy użytkownik już udzielił uprawnień, i 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;
}

Poprzez złożenie prośby o uprawnienia do zapisu wraz z prośbą o odczytanie zmniejszyłem liczbę wyświetlanych próśb o uprawnienia. Użytkownik widzi jedną prośbę o uprawnienia 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 element FileSystemDirectoryHandle, który umożliwia zliczanie plików w katalogu i dostęp do nich. Domyślnie masz uprawnienia tylko do odczytu plików w katalogu, ale jeśli potrzebujesz uprawnień do zapisu, możesz przekazać parametr { mode: 'readwrite' } do metody.

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 potrzebujesz dostępu do każdego pliku za pomocą funkcji getFile(), aby na przykład uzyskać rozmiary poszczególnych plików, nie używaj funkcji await do poszczególnych wyników sekwencyjnie, ale przetwarzaj wszystkie pliki równolegle, na przykład za pomocą funkcji 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 w przypadku braku nowego pliku lub folderu należy go utworzyć.

// 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 określenie ścieżki do danego elementu. Można to zrobić za pomocą metody o odpowiedniej nazwie resolve(). Podczas rozwiązywania adresu element może być bezpośrednim lub pośrednim podkatalogiem 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ąć znajdujące się w nim pliki i foldery za pomocą metody removeEntry(). W przypadku folderów usuwanie może być opcjonalnie rekurencyjne i obejmować wszystkie podfoldery oraz pliki w nich zawarte.

// 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 remove() w FileSystemFileHandle lub FileSystemDirectoryHandle, aby go usunąć.

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

Zmienianie nazw i przenoszenie plików i folderów

Pliki i foldery można zmienić nazwę lub przenieść do nowej lokalizacji, wywołując move() w interfejsie FileSystemHandle. Grupa FileSystemHandle ma interfejsy podrzędne FileSystemFileHandleFileSystemDirectoryHandle. Metoda move() przyjmuje 1 lub 2 parametry. Pierwszy 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ą, dzięki czemu 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 z przeciąganiem i upuszczaniem

Interfejsy HTML do przeciągania i upuszczania umożliwiają aplikacjom internetowym akceptowanie przeciągniętych i upuszczonych plików na stronie internetowej. Podczas operacji przeciągania i upuszczania przeciągnięte pliki i elementy katalogu są powiązane odpowiednio z rekordami plików i rekordami 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żej znajdziesz przykładową listę. Pamiętaj, że interfejs przeciągania i upuszczaniaDataTransferItem.kind jest"file" zarówno w przypadku plików, jak i katalogów, a 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 źródłowego

Prywatny system plików źródła to punkt końcowy pamięci, który, jak sama nazwa wskazuje, jest prywatny dla źródła strony. Przeglądarki zwykle implementują to, zapisując zawartość prywatnego systemu plików źródłowego na dysku, ale nie jest to przeznaczone do użytku przez użytkownika. Podobnie nie oczekuje się, że istnieją pliki lub katalogi o nazwach pasujących do nazw podrzędnych elementów prywatnego systemu plików źródłowego. Chociaż przeglądarka może sprawiać wrażenie, że są to pliki, to wewnętrznie – ponieważ jest to prywatny system plików pochodzenia – przeglądarka może przechowywać te „pliki” w bazie danych lub innej strukturze danych. Jeśli korzystasz z tego interfejsu API, nie oczekuj, że utworzone pliki będą znajdować się w jednej parze na dysku twardym. Gdy masz dostęp do katalogu głównego FileSystemDirectoryHandle, możesz normalnie korzystać z prywatnego systemu plików źródłowego.

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

Obsługa przeglądarek

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

Źródło

uzyskiwać dostęp do plików zoptymalizowanych pod kątem wydajności z prywatnego systemu plików punktu początkowego;

Prywatny system plików źródła zapewnia opcjonalny dostęp do specjalnego rodzaju pliku, który jest w wysokim stopniu zoptymalizowany pod kątem wydajności, np. oferując dostęp do zapisu na miejscu i wyłącznie do zawartości pliku. W Chromium 102 i nowszych wersjach w systemie plików prywatnych pochodzenia jest dostępna dodatkowa metoda upraszczająca dostęp do plików: createSyncAccessHandle() (do synchronicznych operacji odczytu i zapisu). Jest ona dostępna na FileSystemFileHandle, ale tylko w elementach Worker.

// (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 treścią zastępczą,

Nie można całkowicie zastąpić metod interfejsu File System Access API.

  • Metodę showOpenFilePicker() można zastąpić przybliżeniem za pomocą elementu <input type="file">.
  • Metodę showSaveFilePicker() można symulować za pomocą elementu <a download="file_name">, ale powoduje to wywołanie pobierania programowego i nie pozwala na zastąpienie istniejących plików.
  • Metodę showDirectoryPicker() można w pewnym stopniu emulować 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 wybiera inne opcje.

Zabezpieczenia i uprawnienia

Zespół Chrome zaprojektował i wdrożył interfejs File System Access API, korzystając z podstawowych zasad określonych w artykule Kontrolowanie dostępu do zaawansowanych funkcji platformy internetowej, w tym kontroli i przejrzystości użytkownika oraz ergonomii.

otwierać pliki ani zapisywać nowych.

Okno wyboru plików do otwarcia pliku do odczytu
Wybór pliku służący do otwarcia istniejącego pliku do odczytu.

Podczas otwierania pliku użytkownik przyznaje uprawnienia do odczytu pliku lub katalogu za pomocą selektora plików. Wybór otwartego pliku może być wyświetlany tylko za pomocą gestu użytkownika, gdy jest wyświetlany 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 niczego. Jest to takie samo działanie jak w przypadku elementu <input type="file">.

selektor plików do zapisywania plików na dysku;
Wybór pliku służący do zapisywania pliku na dysku.

Podobnie, gdy aplikacja internetowa chce zapisać nowy plik, przeglądarka wyświetla selektor zapisywania pliku, który pozwala użytkownikowi określić nazwę i lokalizację nowego pliku. Ponieważ użytkownik zapisuje nowy plik na urządzeniu (a nie zastępuje istniejącego pliku), selektor plików przyznaje aplikacji uprawnienia do zapisu do pliku.

Foldery z ograniczeniami

Aby chronić użytkowników i ich dane, przeglądarka może ograniczyć możliwość zapisywania w określonych folderach, np. w podstawowych folderach systemu operacyjnego, takich jak foldery Biblioteka w systemie macOS. W takim przypadku przeglądarka wyświetla komunikat z prośbą o wybranie innego folderu.

Modyfikowanie dotychczasowego pliku lub katalogu

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

Prośba o uprawnienia

Jeśli użytkownik chce zapisać zmiany w pliku, do którego wcześniej przyznał dostęp do odczytu, przeglądarka wyświetla prośbę o uprawnienia, aby strona mogła zapisać zmiany na dysku. Prośba o uprawnienia może być wywołana tylko przez użytkownika, na przykład przez kliknięcie przycisku Zapisz.

Prośba o uprawnienia wyświetlana przed zapisaniem pliku.
Pytanie wyświetlane użytkownikom przed przyznaniem przeglądarce uprawnień do zapisu w dotychczasowym pliku.

Aplikacja internetowa, która edytuje wiele plików, np. środowisko IDE, może też prosić o zezwolenie na zapisanie 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. Powinien on zawierać alternatywną metodę zapisywania danych, na przykład „pobieranie” 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, przeglądarka wyświetli ikonę na pasku adresu. Kliknięcie ikony powoduje wyświetlenie wyskakującego okienka z listą plików, do których użytkownik ma dostęp. Użytkownik może w każdej chwili cofnąć ten dostęp.

Trwałość uprawnień

Aplikacja internetowa może zapisywać zmiany w pliku bez pytania o potwierdzenie, dopóki nie zamkniesz wszystkich kart jego pochodzenia. Po zamknięciu karty witryna traci dostęp do wszystkich zasobów. Gdy użytkownik następnym razem użyje aplikacji internetowej, ponownie zostanie poproszony o dostęp do plików.

Prześlij opinię

Chcemy poznać Twoje wrażenia z korzystania z interfejsu API dostępu do systemu plików.

Poinformuj nas o projektowaniu interfejsu API

Czy coś w interfejsie API nie działa zgodnie z oczekiwaniami? A może brakuje metod lub właściwości, których potrzebujesz do wdrożenia swojego pomysłu? Masz pytania lub uwagi dotyczące modelu bezpieczeństwa?

  • Zgłoś problem ze specyfikacją w repozytorium GitHub WICG File System Access lub dodaj swoje uwagi do istniejącego problemu.

Problem z implementacją?

Czy znalazłeś/znalazłaś błąd w implementacji Chrome? 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, instrukcje odtwarzania błędu i ustaw wartość Components na Blink>Storage>FileSystem. Glitch świetnie sprawdza się do udostępniania szybkich reprosów.

Planujesz korzystać z interfejsu API?

Zamierzasz używać w swojej witrynie interfejsu File System Access API? Twoje publiczne wsparcie pomaga nam ustalać priorytety funkcji i pokazuje innym dostawcom przeglądarek, jak ważne jest dla nich wsparcie.

Przydatne linki

Podziękowania

Specyfikację File System Access API napisał Marijn Kruisselbrink.