Interfejs File System Access API 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 współpracują 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 masz już doświadczenie w czytaniu i pisaniu plików, część tego, co przedstawię, będzie Ci już znajoma. 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.
Używanie 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 zbyt wymyślne, ale zawiera wystarczającą ilość informacji, aby ułatwić zrozumienie pojęć.
Obsługa przeglądarek
Wykrywanie cech
Aby dowiedzieć się, czy interfejs File System Access API jest obsługiwany, sprawdź, czy istnieje wybrana 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 edytowania tekstu.
Odczytywanie pliku z lokalnego systemu plików
Pierwszy przypadek użycia, który chcę omówić, polega na tym, że użytkownik wybiera plik, a następnie otwiera go i odczytuje z dysku.
Poproś użytkownika o wybranie pliku do odczytania
Punkt wejścia do interfejsu File System Access API to:
window.showOpenFilePicker()
. Po wywołaniu wyświetla się okno wyboru plików i zawiera prośbę o wybranie pliku. Po wybraniu pliku interfejs API zwraca tablicę uchwytó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.
Jeśli nie określisz żadnych opcji, selektor plików umożliwi użytkownikowi wybranie pojedynczego pliku. Jest to idealne rozwiązanie dla edytora tekstu.
Podobnie jak wiele innych zaawansowanych interfejsów API wywołanie showOpenFilePicker()
musi się odbywać w bezpiecznym kontekście i trzeba je wywoływać z poziomu 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 jest to tablica o jednym elemencie z jednym uchwytem FileSystemFileHandle
, który 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 tylko jego treść. Wywołanie handle.getFile()
zwraca obiekt File
, który zawiera 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 się nie zmienił. Jeśli plik na dysku zostanie zmodyfikowany, obiekt File
stanie się nieczytelny i trzeba będzie ponownie wywołać obiekt getFile()
, aby uzyskać nowy obiekt File
do odczytu zmienionych danych.
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 dwa sposoby: Zapisz lub Zapisz jako. Zapisz zapisuje zmiany w pierwotnym pliku, używając wcześniej pobranego uchwytu pliku. Opcja Zapisz jako tworzy jednak nowy plik i dlatego wymaga nowego uchwytu.
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 głównym systemem plików znajdują się w pliku fs-helpers.js
. Najprościej wygląda to tak, jak pokazano poniżej.
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 wykorzystywany jest obiekt FileSystemWritableFileStream
, podklasa klasy WritableStream
. Utwórz strumień, wywołując funkcję createWritable()
w obiekcie uchwytu pliku. Gdy wywoływana jest funkcja createWritable()
, przeglądarka najpierw sprawdza, czy użytkownik przyznał uprawnienia do zapisu do pliku. Jeśli uprawnienia do zapisu nie zostały przyznane, przeglądarka prosi użytkownika o pozwolenie. Jeśli nie przyznasz uprawnień, 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 też jednak obejmować 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.
Określanie 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
. Możesz to osiągnąć, przekazując właściwość suggestedName
w opcjach 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 są przechowywane materiały wideo.
Oprócz znanych katalogów systemowych możesz też przekazać istniejący plik lub uchwyt katalogu jako wartość parametru startIn
. Okno zostanie otwarte 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 podano 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 uchwytów 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.
Zapisanie w IndexedDB uchwytów plików lub katalogów oznacza, że można zapisywać stan lub zapamiętać, nad którymi 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 tak nie jest, zadzwoń pod numer requestPermission()
, aby (ponownie) poprosić o wprowadzenie zmian. To samo działa w przypadku 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 przyznał już uprawnienia 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;
}
Prosząc o uprawnienia do zapisu wraz z żądaniem do odczytu, zmniejszyłem liczbę próśb o uprawnienia. Podczas otwierania pliku użytkownik widzi 1 prompt oraz przyznaje uprawnienia do odczytu i zapisu w pliku.
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 dostęp do odczytu plików w katalogu, ale jeśli potrzebujesz uprawnień do zapisu, możesz przekazać metodę { 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 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ą getFileHandle()
lub odpowiednio metody getDirectoryHandle()
. Przekazując opcjonalny obiekt options
z kluczem create
i wartością logiczną true
lub false
, możesz określić, czy należy utworzyć nowy plik lub folder, jeśli ten 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 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 przenosić do nowej lokalizacji lub zmieniać ich nazwy, wywołując funkcję move()
w interfejsie FileSystemHandle
. FileSystemHandle
ma interfejsy podrzędne FileSystemFileHandle
i FileSystemDirectoryHandle
. 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 polegająca na przeciąganiu i upuszczaniu
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ładowy kod. 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 masowej, 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 uzyskasz dostęp do katalogu głównego FileSystemDirectoryHandle
, możesz normalnie działać w prywatnym systemie plików punktu początkowego.
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 });
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 dostępne FileSystemFileHandle
, ale tylko w środowiskach internetowych.
// (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 wywołuje to automatyczne pobieranie i nie pozwala na zastępowanie dotychczasowych 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.
otwieranie pliku lub zapisywanie nowego pliku.
Po otwarciu pliku użytkownik przyznaje uprawnienia do odczytu pliku lub katalogu za pomocą selektora plików.
Wybór pliku do otwarcia 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">
.
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. Zapisują na urządzeniu nowy plik (a nie zastępują istniejący plik), więc selektor plików przyznaje aplikacji uprawnienia do zapisu w 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 Library 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 zgodę
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 zostać wywołana tylko przez użytkownika, na przykład przez kliknięcie przycisku Zapisz.
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 zapewniać użytkownikowi alternatywną metodę zapisywania danych, na przykład możliwość pobrania” pliku lub zapisywania danych w chmurze.
Przejrzystość
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 do momentu zamknięcia 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 File System Access API.
Opowiedz nam o konstrukcji 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 podziel się opinią na temat 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 i ustaw opcję Komponenty 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.
- W wątku na forum Discourse dotyczącej WGICh opisz, jak zamierzasz go używać.
- Wyślij tweeta na adres @ChromiumDev, używając hashtagu
#FileSystemAccess
, i daj nam znać, gdzie i jak go używasz.
Przydatne linki
- Publiczny film wyjaśniający
- Specyfikacja dostępu do systemu plików i specyfikacja pliku
- Śledzenie błędu
- Wpis na stronie ChromeStatus.com
- Definicje TypeScript
- File System Access API – model zabezpieczeń Chromium
- Składnik Blink:
Blink>Storage>FileSystem
Podziękowania
Specyfikację File System Access API napisał Marijn Kruisselbrink.