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ć te same możliwości i elastyczność w zakresie pamięci.

Właśnie w takich sytuacjach przydaje się interfejs Storage Foundation API. Interfejs Storage Foundation API to nowy, szybki interfejs API do przechowywania danych, który nie jest powiązany z żadną konkretną usługą. Umożliwia on tworzenie nowych, często proszonych oczekiwań dotyczących internetu, takich jak wdrażanie wydajnych baz danych i sprawne 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 kolejnego interfejsu API do obsługi pamięci?

Platforma internetowa oferuje deweloperom kilka opcji przechowywania danych, z których każda została stworzona z myślą o konkretnych zastosowaniach.

  • 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. Ta zmiana wiąże się z większym naciskiem na bezpieczeństwo i wydajność.
  • Interfejs IndexedDB API może służyć jako backend w przypadku niektórych zastosowań 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 zastosowania 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ą tych funkcji:

  • NativeIOFile.close(): zamyka plik i zwraca obietnicę, która zostanie spełniona 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 składający 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. Może być mniejszy niż rozmiar bufora, jeśli wystąpi błąd lub jeśli zakres odczytu wykracza poza koniec pliku. Jest ona ustawiana na 0, jeśli zakres odczytu wykracza poza koniec pliku.
  • NativeIOFile.write(buffer, offset): zapisuje zawartość danego bufora w pliku w danym przesunięciu. Bufor jest przenoszony przed zapisaniem jakichkolwiek danych, dlatego pozostaje 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 elementó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 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();

Prezentacja

Możesz wypróbować demo interfejsu Storage Foundation API, korzystając z poniżej zamieszczonego kodu. Twórz, zmieniaj nazwy, zapisuj i odczytuj pliki oraz sprawdzaj dostępną pojemność, o którą poproszono w ramach aktualizacji, gdy wprowadzasz zmiany. Kod źródłowy wersji demonstracyjnej znajdziesz na 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.

Zgodnie z tym samym wzorcem co w przypadku 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 danych utworzonych przez siebie. 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 Krivoya i Richarda Stotza. Ten artykuł został sprawdzony przez Pete’a LePage’a i Joe Medleya.

Baner powitalny autorstwa Markus Spiske na Unsplash.