Wydajna pamięć aplikacji: Storage Foundation API

Platforma internetowa coraz częściej oferuje programistom narzędzia potrzebne do tworzenia dopracowanych, zaawansowanych aplikacji internetowych. Przede wszystkim dzięki projektowi WebAssembly (Wasm) powstało szybkie i zaawansowane aplikacje internetowe, a takie technologie jak Emscripten umożliwiają programistom ponowne wykorzystywanie sprawdzonego i przetestowanego kodu w internecie. Aby w pełni wykorzystać ten potencjał, deweloperzy muszą mieć taką samą moc i elastyczność w zakresie miejsca na dane.

Właśnie do tego służy Storage Foundation API. Storage Foundation API to nowy, szybki i niedostrzegalny interfejs API do przechowywania danych, który umożliwia korzystanie z nowych i bardzo oczekiwanych przypadków użycia w internecie, takich jak wdrażanie wydajnych baz danych i płynne zarządzanie dużymi plikami tymczasowymi. Ten nowy interfejs pozwala deweloperom „udostępniać własne miejsce na dane” w sieci, zmniejszając w ten sposób lukę między kodem sieciowym a kodem na poziomie platformy.

Interfejs Storage Foundation API przypomina bardzo podstawowy system plików. Zapewnia programistom elastyczność dzięki ogólnym, prostym i wydajnym elementom podstawowym, na których można tworzyć komponenty wyższego poziomu. Aplikacje mogą korzystać z najlepszego narzędzia dostosowanego do ich potrzeb, znajdując odpowiednią równowagę między łatwością obsługi, wydajnością i niezawodnością.

Dlaczego internet potrzebuje innego interfejsu API do przechowywania danych?

Platforma internetowa oferuje deweloperom wiele opcji miejsca na dane, z których każda jest zaprojektowana pod kątem konkretnych przypadków użycia.

  • Niektóre z tych opcji w żaden sposób nie mają związku z tą propozycją, ponieważ zezwalają na przechowywanie bardzo niewielkich ilości danych, np. plików cookie lub interfejsów Web Storage API, które obejmują mechanizmy sessionStorage i localStorage.
  • Inne opcje zostały już wycofane z różnych powodów, takich jak File and Directory Items API czy WebSQL.
  • Interfejs File System Access API ma podobną platformę interfejsu API, ale jego zastosowanie polega na komunikacji z systemem plików klienta i zapewnianiu dostępu do danych, które mogą wykraczać poza prawo własności do źródła lub nawet do przeglądarki. Takie rozróżnienie wiąże się z bardziej rygorystycznymi zasadami dotyczącymi bezpieczeństwa i wyższymi kosztami wydajności.
  • Interfejs IndexedDB API może służyć jako backend w niektórych przypadkach użycia interfejsu Storage Foundation API. Na przykład Emscripten zawiera IDBFS, czyli system plików trwałych oparty na IndexedDB. Ponieważ jednak IndexedDB to zasadniczo magazyn par klucz-wartość, wiąże się ze znacznymi ograniczeniami wydajności. Ponadto bezpośredni dostęp do podsekcji pliku jest jeszcze trudniejszy i wolniejszy w przypadku interfejsu IndexedDB.
  • Co więcej, interfejs CacheStorage jest powszechnie obsługiwany i dostosowany do przechowywania dużych danych, takich jak zasoby aplikacji internetowych, ale ich wartości są stałe.

Storage Foundation API ma na celu uzupełnienie poprzednich opcji pamięci masowej przez umożliwienie efektywnego przechowywania zmiennych dużych plików zdefiniowanych w źródle aplikacji.

Sugerowane przypadki użycia interfejsu Storage Foundation API

Przykłady witryn, które mogą korzystać z tego interfejsu API:

  • Aplikacje zwiększające produktywność lub pobudzające kreatywność, które działają na dużych ilościach danych wideo, audio lub graficznych. Takie aplikacje mogą przenieść segmenty na dysk, zamiast przechowywać je w pamięci.
  • Aplikacje korzystające z trwałego systemu plików dostępnego z poziomu Wasm, które wymagają większej wydajności, niż może zagwarantować IDBFS.

Czym jest interfejs Storage Foundation API?

Interfejs API składa się z 2 głównych części:

  • Wywołania systemu plików, które zapewniają podstawowe funkcje interakcji z plikami i ścieżkami plików.
  • Uchwyty plików, które zapewniają uprawnienia do odczytu i zapisu istniejącego pliku.

Wywołania systemu plików

Interfejs Storage Foundation API wprowadza nowy obiekt (storageFoundation), który znajduje się w obiekcie window i zawiera pewną liczbę funkcji:

  • storageFoundation.open(name): otwiera plik o podanej nazwie (jeśli istnieje) lub tworzy nowy plik. Zwraca obietnicę rozwiązaną z otwartym plikiem.
  • storageFoundation.delete(name): usuwa plik o podanej nazwie. Zwraca obietnicę, która obowiązuje w przypadku usunięcia pliku.
  • storageFoundation.rename(oldName, newName): następuje całkowita zmiana nazwy pliku ze starej nazwy na nową. Zwraca obietnicę spełniającą warunki po zmianie nazwy pliku.
  • storageFoundation.getAll(): zwraca obietnicę rozwiązaną z tablicą wszystkich istniejących nazw plików.
  • storageFoundation.requestCapacity(requestedCapacity): żąda nowej pojemności (w bajtach) na potrzeby wykorzystania w bieżącym kontekście wykonywania. Zwraca obietnicę, która została zrealizowana z pozostałą ilością dostępnej pojemności.
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): zwalnia określoną liczbę bajtów z bieżącego kontekstu wykonywania i zwraca obietnicę, która zostaje zrealizowana przy pozostałej pojemności.
  • storageFoundation.getRemainingCapacity(): zwraca obietnicę, której dostępność jest dostępna w bieżącym kontekście wykonania.

Uchwyty plików

Praca z plikami odbywa się za pomocą następujących funkcji:

  • NativeIOFile.close(): zamyka plik i zwraca obietnicę zakończoną po zakończeniu operacji.
  • NativeIOFile.flush(): synchronizuje (usuwa) plik znajdujący się w pamięci z urządzeniem pamięci masowej i zwraca obietnicę zrealizowana po zakończeniu operacji.
  • NativeIOFile.getLength(): zwraca obietnicę, której długość jest podana w bajtach.
  • NativeIOFile.setLength(length): ustawia długość pliku w bajtach i zwraca obietnicę realizowaną po zakończeniu operacji. Jeśli nowa długość jest mniejsza niż bieżąca, bajty są usuwane, zaczynając od końca pliku. W przeciwnym razie plik jest rozszerzany o bajty o zerowej wartości.
  • NativeIOFile.read(buffer, offset): odczytuje zawartość pliku z określonym przesunięciem przez bufor będący wynikiem przesłania danego bufora, który następnie zostaje odłączony. Zwraca NativeIOReadResult z przeniesionym buforem i liczbą bajtów, które zostały odczytane.

    NativeIOReadResult to obiekt składający się z 2 wpisów:

    • buffer: ArrayBufferView będący wynikiem przeniesienia bufora przekazywanego do read(). Ma taki sam typ i długość jak bufor źródłowy.
    • readBytes: liczba bajtów, które zostały odczytane przez buffer. Jeśli wystąpi błąd lub zakres odczytu wykracza poza koniec pliku, może on być mniejszy niż rozmiar bufora. Jeśli zakres odczytu wykracza poza koniec pliku, pole ma wartość 0.
  • NativeIOFile.write(buffer, offset): zapisuje zawartość danego bufora w pliku z podanym przesunięciem. Bufor jest przesyłany przed zapisaniem danych, więc zostaje odłączony. Zwraca NativeIOWriteResult z przeniesionym buforem i liczbą bajtów, które zostały zapisane. Jeśli zakres zapisu przekracza długość, plik zostanie przedłużony.

    NativeIOWriteResult to obiekt składający się z 2 wpisów:

    • buffer: ArrayBufferView będący wynikiem przeniesienia bufora przekazywanego do write(). Ma taki sam typ i długość jak bufor źródłowy.
    • writtenBytes: liczba bajtów, które zostały zapisane w folderze buffer. W przypadku błędu ta wartość może być mniejsza niż rozmiar bufora.

Pełne przykłady

Aby ułatwić zrozumienie przedstawionych koncepcji, podajemy tu 2 pełne przykłady, które przedstawiają różne etapy cyklu życia plików podstawowych funkcji Storage.

Otwieranie, pisanie, czytanie, zamykanie

// Open a file (creating it if needed).
const file = await storageFoundation.open('test_file');
try {
  // Request 100 bytes of capacity for this context.
  await storageFoundation.requestCapacity(100);

  const writeBuffer = new Uint8Array([64, 65, 66]);
  // Write the buffer at offset 0. After this operation, `result.buffer`
  // contains the transferred buffer and `result.writtenBytes` is 3,
  // the number of bytes written. `writeBuffer` is left detached.
  let result = await file.write(writeBuffer, 0);

  const readBuffer = new Uint8Array(3);
  // Read at offset 1. `result.buffer` contains the transferred buffer,
  // `result.readBytes` is 2, the number of bytes read. `readBuffer` is left
  // detached.
  result = await file.read(readBuffer, 1);
  // `Uint8Array(3) [65, 66, 0]`
  console.log(result.buffer);
} finally {
  file.close();
}

Otwieranie, wyświetlanie, usuwanie

// Open three different files (creating them if needed).
await storageFoundation.open('sunrise');
await storageFoundation.open('noon');
await storageFoundation.open('sunset');
// List all existing files.
// `["sunset", "sunrise", "noon"]`
await storageFoundation.getAll();
// Delete one of the three files.
await storageFoundation.delete('noon');
// List all remaining existing files.
// `["sunrise", "noon"]`
await storageFoundation.getAll();

Pokaz

Możesz wypróbować wersję demonstracyjną interfejsu Storage Foundation API widoczną poniżej. Pozwala tworzyć pliki, zmieniać ich nazwy, zapisywać w nich i odczytywać je, a także wyświetlać dostępną pojemność, o którą prosisz w przypadku wprowadzania zmian. Kod źródłowy wersji demonstracyjnej znajdziesz w usłudze Glitch.

Bezpieczeństwo i uprawnienia

Zespół Chromium zaprojektował i wdrożył interfejs Storage Foundation API zgodnie z podstawowymi zasadami określonymi w artykule Kontrola dostępu do zaawansowanych funkcji platformy internetowej, takimi jak kontrola użytkowników, przejrzystość i ergonomia.

Dostęp do interfejsu Storage Foundation API jest zgodny z wzorcem podanym w innych nowoczesnych interfejsach API do przechowywania danych w internecie, co oznacza, że źródło może uzyskiwać dostęp tylko do danych utworzonych samodzielnie. Ogranicza się też do bezpiecznych kontekstów.

Kontrola użytkowników

Limit miejsca na dane będzie używany do rozdzielania dostępu do miejsca na dysku i zapobiegania nadużyciom. Pamięć, którą chcesz zajmować, trzeba najpierw wysłać żądanie. Tak jak w przypadku innych interfejsów API do przechowywania danych, użytkownicy mogą zwolnić miejsce zajmowane przez interfejs Storage Foundation API w przeglądarce.

Przydatne linki

Podziękowania

Interfejs Storage Foundation API został określony i wdrożony przez Emanuela Krivoya i Richarda Stotza. Ten artykuł napisali Pete LePage i Joe Medley.

Baner powitalny z filmu Markus Spiske w Unsplash.