Wydajna pamięć aplikacji: Storage Foundation API

Platforma internetowa coraz częściej oferuje deweloperom narzędzia potrzebne do tworzenia precyzyjnie dostrojonych aplikacji internetowych o wysokiej wydajności. Najważniejszym z nich jest WebAssembly (Wasm), który otworzył drogę do szybkich i wydajnych aplikacji internetowych, a technologie takie jak Emscripten umożliwiają teraz programistom ponowne wykorzystywanie w internecie sprawdzonego kodu. Aby w pełni wykorzystać ten potencjał, deweloperzy muszą mieć taką samą moc i elastyczność w zakresie przechowywania danych.

W tym miejscu przydaje się interfejs Storage Foundation API. Storage Foundation API to nowy, szybki i niezależny interfejs API pamięci masowej, który umożliwia nowe i często oczekiwane zastosowania w internecie, takie jak wdrażanie wydajnych baz danych i sprawne zarządzanie dużymi plikami tymczasowymi. Dzięki temu nowemu interfejsowi programiści mogą „przenieść własną pamięć masową” do internetu, co zmniejsza różnicę między kodem internetowym a kodem na konkretną platformę.

Interfejs Storage Foundation API został zaprojektowany tak, aby przypominał bardzo prosty system plików, dzięki czemu zapewnia programistom elastyczność, udostępniając ogólne, proste i wydajne elementy podstawowe, na których mogą oni budować komponenty wyższego poziomu. Aplikacje mogą korzystać z najlepszego narzędzia do swoich potrzeb, znajdując odpowiednią równowagę między łatwością obsługi, wydajnością i niezawodnością.

Dlaczego w internecie potrzebny jest kolejny interfejs API do przechowywania danych?

Platforma internetowa oferuje programistom wiele opcji przechowywania danych, z których każda została opracowana z myślą o konkretnych przypadkach użycia.

  • Niektóre z tych opcji wyraźnie nie pokrywają się z tą propozycją, ponieważ pozwalają na przechowywanie bardzo małych ilości danych, takich jak pliki cookie lub interfejs Web Storage API, który składa się z mechanizmów sessionStoragelocalStorage.
  • Inne opcje są już wycofane z różnych powodów, np. File and Directory Entries API czy WebSQL.
  • File System Access API ma podobny interfejs API, ale służy do komunikacji z systemem plików klienta i zapewnia dostęp do danych, które mogą znajdować się poza własnością pochodzenia lub nawet przeglądarki. To inne podejście wiąże się z większymi wymaganiami dotyczącymi bezpieczeństwa i wyższymi kosztami wydajności.
  • Interfejs IndexedDB API może być używany jako backend w niektórych przypadkach użycia interfejsu Storage Foundation API. Na przykład Emscripten zawiera IDBFS, czyli trwały system plików oparty na IndexedDB. Jednak ponieważ IndexedDB jest w zasadzie magazynem klucz-wartość, ma on znaczne ograniczenia wydajności. Ponadto bezpośredni dostęp do podsekcji pliku jest w przypadku IndexedDB jeszcze trudniejszy i wolniejszy.
  • Interfejs CacheStorage jest powszechnie obsługiwany i dostosowany do przechowywania dużych ilości danych, takich jak zasoby aplikacji internetowych, ale wartości są niezmienne.

Interfejs Storage Foundation API ma na celu wyeliminowanie wszystkich luk w poprzednich opcjach przechowywania danych poprzez umożliwienie wydajnego przechowywania dużych, modyfikowalnych 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 kreatywność, które działają na dużych ilościach danych wideo, audio lub obrazów. Takie aplikacje mogą przenosić segmenty na dysk zamiast przechowywać je w pamięci.
  • Aplikacje, które korzystają z trwałego systemu plików dostępnego z Wasm i wymagają większej wydajności niż ta, którą 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 do plików.
  • Uchwyty plików, które zapewniają dostęp 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 szereg funkcji:

  • storageFoundation.open(name): Otwiera plik o podanej nazwie, jeśli istnieje, a w przeciwnym razie tworzy nowy plik. Zwraca obietnicę, która jest spełniana po otwarciu pliku.
  • storageFoundation.delete(name): usuwa plik o podanej nazwie. Zwraca obietnicę, która zostanie spełniona po usunięciu pliku.
  • storageFoundation.rename(oldName, newName): Zmienia nazwę pliku ze starej na nową w sposób niepodzielny. Zwraca obietnicę, która jest spełniana po zmianie nazwy pliku.
  • storageFoundation.getAll(): Zwraca obietnicę, która jest spełniana z tablicą wszystkich istniejących nazw plików.
  • storageFoundation.requestCapacity(requestedCapacity): żąda nowej pojemności (w bajtach) na potrzeby bieżącego kontekstu wykonania. Zwraca obietnicę, która została rozwiązana z pozostałą ilością dostępnej pojemności.
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): zwalnia określoną liczbę bajtów z bieżącego kontekstu wykonania i zwraca obietnicę, która jest realizowana z pozostałą pojemnością.
  • storageFoundation.getRemainingCapacity(): zwraca obietnicę, która jest spełniana z pojemnością dostępną w bieżącym kontekście wykonania.

Uchwyty plików

Praca z plikami odbywa się za pomocą tych funkcji:

  • NativeIOFile.close(): zamyka plik i zwraca obietnicę, która zostanie spełniona po zakończeniu operacji.
  • NativeIOFile.flush(): synchronizuje (czyli zapisuje) stan pliku w pamięci z urządzeniem pamięci masowej i zwraca obietnicę, która jest spełniana po zakończeniu operacji.
  • NativeIOFile.getLength(): zwraca obietnicę, która jest spełniana z długością pliku w bajtach.
  • NativeIOFile.setLength(length): ustawia długość pliku w bajtach i zwraca obietnicę, która jest spełniana po zakończeniu operacji. Jeśli nowa długość jest mniejsza od obecnej, bajty są usuwane od końca pliku. W przeciwnym razie plik jest rozszerzany o bajty o wartości zero.
  • NativeIOFile.read(buffer, offset): odczytuje zawartość pliku przy danym przesunięciu za pomocą bufora, który jest wynikiem przeniesienia danego bufora, a następnie pozostaje odłączony. Zwraca wartość 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, która jest wynikiem przeniesienia bufora przekazanego do read(). Jest tego samego typu i długości co bufor źródłowy.
    • readBytes: liczba bajtów, które zostały odczytane do buffer. Może być mniejsza niż rozmiar bufora, jeśli wystąpi błąd lub zakres odczytu wykracza poza koniec pliku. Jeśli zakres odczytu wykracza poza koniec pliku, wartość ta jest ustawiana na zero.
  • NativeIOFile.write(buffer, offset): zapisuje zawartość danego bufora w pliku w podanym przesunięciu. Bufor jest przenoszony przed zapisaniem jakichkolwiek danych, dlatego pozostaje odłączony. Zwraca NativeIOWriteResult z przeniesionym buforem i liczbą bajtów, które zostały zapisane. Jeśli zakres zapisu przekracza długość pliku, zostanie on wydłużony.

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

    • buffer: ArrayBufferView, która jest wynikiem przeniesienia bufora przekazanego do write(). Jest tego samego typu i długości co bufor źródłowy.
    • writtenBytes: liczba bajtów, które zostały zapisane w buffer. Jeśli wystąpi błąd, może to być wartość mniejsza niż rozmiar bufora.

Pełne przykłady

Aby lepiej wyjaśnić powyższe koncepcje, przedstawiamy 2 przykłady, które pokazują różne etapy cyklu życia plików Storage Foundation.

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 i 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();

Zabezpieczenia i uprawnienia

Zespół Chromium zaprojektował i wdrożył interfejs Storage Foundation API zgodnie z podstawowymi zasadami określonymi w dokumencie Controlling Access to Powerful Web Platform Features, w tym z zasadami dotyczącymi kontroli użytkownika, przejrzystości i ergonomii.

Zgodnie z wzorcem innych nowoczesnych interfejsów API pamięci masowej w internecie dostęp do interfejsu Storage Foundation API jest ograniczony do źródła, co oznacza, że źródło może uzyskiwać dostęp tylko do utworzonych przez siebie danych. Jest ona też ograniczona do bezpiecznych kontekstów.

Kontrola sprawowana przez użytkowników

Limit miejsca na dane będzie używany do dystrybucji dostępu do miejsca na dysku i zapobiegania nadużyciom. Pamięć, którą chcesz zająć, musi zostać najpierw zażądana. Podobnie jak w przypadku innych interfejsów API do przechowywania danych użytkownicy mogą wyczyścić miejsce zajmowane przez Storage Foundation API w przeglądarce.

Przydatne linki

Podziękowania

Interfejs Storage Foundation API został opracowany i wdrożony przez Emanuela KrivoyaRicharda Stotza. Ten artykuł został sprawdzony przez Pete’a LePage’a i Joe Medleya.

Baner powitalny: Markus Spiske na stronie Unsplash.