Wydajna pamięć aplikacji: Storage Foundation API

Platforma internetowa oferuje deweloperom coraz więcej narzędzi do tworzenia dopracowanych, wydajnych aplikacji internetowych. Najważniejsze jest to, że WebAssembly (Wasm) otworzył drogę do szybkich i wydajnych aplikacji internetowych, a technologie takie jak Emscripten pozwalają deweloperom ponownie używać sprawdzonego kodu w internecie. Aby w pełni wykorzystać ten potencjał, deweloperzy muszą mieć taką samą moc i elastyczność w zakresie pamięci.

Właśnie w takich sytuacjach przydaje się interfejs 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. Dzięki temu nowemu interfejsowi deweloperzy mogą „wnosić własny magazyn” do sieci, zmniejszając różnice w funkcjach między kodem internetowym a kodem platformy.

Interfejs Storage Foundation API jest zaprojektowany tak, aby przypominać bardzo podstawowy system plików, co daje deweloperom elastyczność dzięki udostępnianiu ogólnych, prostych i wydajnych prymitywów, na których mogą budować komponenty wyższego poziomu. Aplikacje mogą korzystać z najlepszego narzędzia do swoich potrzeb, zapewniają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 wyraźnie nie pokrywają się z tą propozycją, ponieważ umożliwiają przechowywanie tylko bardzo małych ilości danych, np. pliki cookie lub interfejs Web Storage API, który składa się z mechanizmów sessionStoragelocalStorage.
  • Inne opcje zostały już wycofane z różnych powodów, np. interfejs API „File and Directory Entries” czy WebSQL.
  • Interfejs File System Access API ma podobną powierzchnię interfejsu API, ale służy do interakcji z systemem plików klienta i dostępu do danych, które mogą nie należeć do pochodzenia ani 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, trwały system plików oparty na IndexedDB. Jednak ponieważ IndexedDB jest zasadniczo magazynem klucz-wartość, wiąże się z nim znaczne ograniczenie wydajności. Co więcej, bezpośredni dostęp do podseekcji pliku jest jeszcze trudniejszy i wolniejszy w IndexedDB.
  • Na koniec warto wspomnieć, że interfejs CacheStorage jest szeroko obsługiwany i dostosowany do przechowywania dużych danych, takich jak zasoby aplikacji internetowej, ale wartości są niezmienne.

Interfejs Storage Foundation API to próba wyeliminowania wszystkich luk w poprzednich opcjach przechowywania danych poprzez umożliwienie wydajnego przechowywania dużych plików o zmiennej treści 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 do produktywności lub kreatywności, 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 poziomu Wasm i które wymagają większej wydajności niż ta, jaką gwarantuje 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 do 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 kilka funkcji:

  • storageFoundation.open(name): powoduje otwarcie pliku o podanej nazwie, jeśli istnieje, w przeciwnym razie tworzy nowy plik. Zwraca obietnicę, która jest wypeł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 z poprzedniej na nową. Zwraca obietnicę, która zostanie spełniona po zmianie nazwy pliku.
  • storageFoundation.getAll(): zwraca obietnicę, która zwraca tablicę wszystkich nazw plików.
  • storageFoundation.requestCapacity(requestedCapacity): żąda nowej pojemności (w bajtach) do wykorzystania przez bieżący kontekst wykonania. Zwraca obietnicę, która zwraca pozostałą ilość dostępnej pojemności.
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): zwalnia określoną liczbę bajtów z bieżącego kontekstu wykonania i zwraca obietnicę, która zwraca pozostałą pojemność.
  • storageFoundation.getRemainingCapacity(): zwraca obietnicę, która zwraca pojemność dostępną 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 (czyli czyści) stan pliku w pamięci z urządzeniem pamięci masowej i zwraca obietnicę, która zostanie spełniona po zakończeniu operacji.
  • NativeIOFile.getLength(): zwraca obietnicę, która zwraca długość pliku w bajtach.
  • NativeIOFile.setLength(length): ustawia długość pliku w bajtach i zwraca obietnicę, która zostanie spełniona po zakończeniu operacji. Jeśli nowa długość jest mniejsza niż bieżąca długość, bajty są usuwane od końca pliku. W przeciwnym razie plik jest rozszerzany o bajty o wartości 0.
  • NativeIOFile.read(buffer, offset): odczytuje zawartość pliku w danym przesunięciu za pomocą bufora, który jest wynikiem przeniesienia danego bufora, który jest następnie odłączony. Zwraca NativeIOReadResult z przekazanym buforem i liczbą bajtów, które zostały odczytane.

    NativeIOReadResult to obiekt, który składa się z 2 elementów:

    • buffer: ArrayBufferView, co jest wynikiem przeniesienia bufora przekazanego do read(). Ma on ten sam typ i długość co bufor źródłowy.
    • readBytes: liczba bajtów odczytanych do pliku 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 przekazanym buforem i liczbą bajtów, które zostały pomyślnie zapisane. Plik zostanie wydłużony, jeśli zakres zapisu przekroczy jego długość.

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

    • buffer: ArrayBufferView, który jest wynikiem przeniesienia bufora przekazanego do write(). Ma on ten sam typ i długość co bufor źródłowy.
    • writtenBytes: liczba bajtów zapisanych w pliku buffer. Jeśli wystąpi błąd, może to być mniejsze niż rozmiar bufora.

Pełne przykłady

Aby lepiej wyjaśnić omówione powyżej zagadnienia, podajemy 2 pełne 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, 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();

Prezentacja

Możesz zapoznać się z demo interfejsu Storage Foundation API, korzystając z załączonego poniżej kodu. Możesz 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 na stronie Glitch.

Zabezpieczenia i uprawnienia

Zespół Chromium zaprojektował i wdrżał interfejs Storage Foundation API, korzystając z podstawowych zasad określonych w artykule Kontrolowanie dostępu do zaawansowanych funkcji platformy internetowej, w tym kontroli użytkownika, przejrzystości i ergonomiki.

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. Jest też ograniczona do kontekstów niezabezpieczonych.

Kontrola użytkownika

Limit miejsca na dane będzie używany do dystrybucji dostępu do miejsca na dysku i zapobiegania nadużyciom. Pamięć, którą chcesz wykorzystać, musi być najpierw zarezerwowana. Podobnie jak w przypadku innych interfejsów API miejsca na dane użytkownicy mogą zwolnić miejsce zajęte przez interfejs Storage Foundation API w przeglądarce.

Przydatne linki

Podziękowania

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

Baner powitalny z filmu Markus Spiske w Unsplash.