適合應用程式的高效能儲存空間:Storage Foundation API

網頁平台提供的工具越來越多,可協助開發人員建構微調過的高效能網頁應用程式。最值得一提的是,WebAssembly (Wasm) 為快速且功能強大的網路應用程式開啟了大門,而 Emscripten 等技術則讓開發人員能在網路上重複使用經過測試的程式碼。如要充分發揮這項潛力,開發人員在儲存空間方面也必須擁有相同的能力和彈性。

這時 Storage Foundation API 就能派上用場。Storage Foundation API 是全新的快速儲存空間 API,可支援網路上許多新用途,例如實作高效能資料庫,以及妥善管理大型暫時檔案。透過這個新介面,開發人員可以「自備儲存空間」到網頁,縮小網頁與平台專屬程式碼之間的差距。

Storage Foundation API 的設計類似於非常基本的檔案系統,因此可提供一般、簡單且高效能的基元,讓開發人員建構更高層級的元件,進而享有彈性。應用程式可根據需求選擇最合適的工具,在可用性、效能和穩定性之間取得適當平衡。

為什麼網頁需要其他儲存空間 API?

網頁平台為開發人員提供多種儲存空間選項,每種選項都是針對特定用途而建構。

  • 其中有些選項顯然與這項提案不重疊,因為這些選項只允許儲存極少量資料,例如 Cookie,或是由 sessionStoragelocalStorage 機制組成的 Web Storage API
  • 其他選項已因各種原因遭到淘汰,例如 File and Directory Entries APIWebSQL
  • 檔案系統存取 API 具有類似的 API 介面,但其用途是與用戶端的檔案系統介接,並提供對資料的存取權,這些資料可能不在來源或甚至瀏覽器的擁有權範圍內。這種不同的重點會帶來更嚴格的安全考量,以及更高的效能成本。
  • IndexedDB API 可做為部分 Storage Foundation API 用途的後端。舉例來說,Emscripten 包含以 IndexedDB 為基礎的持續性檔案系統 IDBFS。不過,由於 IndexedDB 本質上是鍵值儲存空間,因此有顯著的效能限制。此外,在 IndexedDB 中直接存取檔案的子區段更加困難,速度也較慢。
  • 最後,CacheStorage 介面受到廣泛支援,且經過調整,可儲存大型資料 (例如網頁應用程式資源),但值是不可變動的。

Storage Foundation API 旨在填補先前儲存空間選項的所有缺口,允許在應用程式來源中定義的可變動大型檔案,以高效能方式儲存。

Storage Foundation API 的建議用途

可能使用這項 API 的網站包括:

  • 處理大量影片、音訊或圖片資料的生產力或創意應用程式。這類應用程式可將區隔卸載至磁碟,而非保留在記憶體中。
  • 應用程式依賴可從 Wasm 存取的永久檔案系統,且需要的效能高於 IDBFS 保證的效能。

什麼是 Storage Foundation API?

這個 API 分為兩大部分:

  • 檔案系統呼叫:提供與檔案和檔案路徑互動的基本功能。
  • 檔案控制代碼:提供現有檔案的讀寫權限。

檔案系統呼叫

Storage Foundation API 導入了新的物件 storageFoundation,這個物件位於 window 物件上,並包含多項函式:

  • storageFoundation.open(name):開啟指定名稱的檔案 (如果存在),否則會建立新檔案。傳回以開啟的檔案解析的 Promise。
  • storageFoundation.delete(name):移除指定名稱的檔案。傳回在檔案刪除時解析的 Promise。
  • storageFoundation.rename(oldName, newName):以不可分割的方式將檔案從舊名稱重新命名為新名稱。傳回在檔案重新命名時解析的 Promise。
  • storageFoundation.getAll():傳回 Promise,並以所有現有檔案名稱的陣列解析。
  • storageFoundation.requestCapacity(requestedCapacity):要求目前執行環境的新容量 (以位元組為單位)。傳回承諾,並以剩餘可用容量解析。
  • storageFoundation.releaseCapacity(toBeReleasedCapacity):從目前的執行環境釋出指定位元組數,並傳回會以剩餘容量解析的 Promise。
  • storageFoundation.getRemainingCapacity():傳回 Promise,該 Promise 會解析目前執行環境可用的容量。

檔案控制代碼

您可以使用下列函式處理檔案:

  • NativeIOFile.close():關閉檔案,並傳回在作業完成時會解析的 Promise。
  • NativeIOFile.flush():將檔案的記憶體內狀態與儲存裝置同步 (即清除),並在作業完成時傳回已解決的 Promise。
  • NativeIOFile.getLength():傳回 Promise,並以檔案長度 (以位元組為單位) 解析。
  • NativeIOFile.setLength(length):以位元組為單位設定檔案長度,並傳回作業完成時會解析的 Promise。如果新長度小於目前長度,系統會從檔案結尾開始移除位元組。否則檔案會以零值位元組擴充。
  • NativeIOFile.read(buffer, offset):透過緩衝區讀取指定位移處的檔案內容,該緩衝區是轉移指定緩衝區的結果,之後會保持分離狀態。傳回 NativeIOReadResult,其中包含已轉移的緩衝區和成功讀取的位元組數。

    NativeIOReadResult 是由兩個項目組成的物件:

    • bufferArrayBufferView,這是將緩衝區傳遞至 read() 的結果。與來源緩衝區的類型和長度相同。
    • readBytes:成功讀取到 buffer 中的位元組數。如果發生錯誤,或讀取範圍超出檔案結尾,這個值可能會小於緩衝區大小。如果讀取範圍超出檔案結尾,則會設為零。
  • NativeIOFile.write(buffer, offset):將指定緩衝區的內容寫入指定偏移位置的檔案。緩衝區會在寫入任何資料前轉移,因此會保持分離狀態。傳回 NativeIOWriteResult,其中包含已轉移的緩衝區和成功寫入的位元組數。如果寫入範圍超出檔案長度,檔案就會擴增。

    NativeIOWriteResult 是由兩個項目組成的物件:

    • bufferArrayBufferView,這是將緩衝區傳遞至 write() 的結果。與來源緩衝區的類型和長度相同。
    • writtenBytes:成功寫入 buffer 的位元組數。如果發生錯誤,這個值可能會小於緩衝區大小。

完整範例

為清楚說明上述概念,以下提供兩個完整範例,逐步介紹 Storage Foundation 檔案生命週期的不同階段。

開啟、寫入、讀取、關閉

// 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();
}

開啟、列出、刪除

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

安全性和權限

Chromium 團隊採用「控管強大的網頁平台功能存取權」中定義的核心原則 (包括使用者控制權、透明度和人體工學),設計及實作 Storage Foundation API。

與網路上其他新式儲存空間 API 相同,Storage Foundation API 的存取權會受到來源限制,也就是說,來源只能存取自行建立的資料。此外,也僅限於安全環境。

使用者控制項

儲存空間配額用於分配磁碟空間存取權,並防止濫用。您必須先要求要占用的記憶體,與其他儲存空間 API 相同,使用者可以透過瀏覽器清除 Storage Foundation API 占用的空間。

實用連結

特別銘謝

Storage Foundation API 由 Emanuel KrivoyRichard Stotz 負責指定及實作。本文由 Pete LePageJoe Medley 審查。

主頁橫幅圖片由 Markus Spiske 提供,刊登於 Unsplash