Bộ nhớ hiệu suất cao cho ứng dụng: Storage Foundation API

Nền tảng web ngày càng cung cấp cho nhà phát triển những công cụ cần thiết để xây dựng các ứng dụng có hiệu suất cao được tinh chỉnh cho web. Đáng chú ý nhất là WebAssembly (Wasm) đã mở ra cánh cửa cho các ứng dụng web nhanh và mạnh mẽ, trong khi các công nghệ như Emscripten hiện cho phép nhà phát triển sử dụng lại mã đã được thử nghiệm và thử nghiệm trên web. Để thực sự tận dụng được tiềm năng này, các nhà phát triển phải có được sức mạnh và sự linh hoạt giống nhau khi nói đến bộ nhớ.

Đây là lúc dùng Storage Foundation API. Storage Foundation API là một API lưu trữ mới, nhanh và không bị thay đổi, giúp mở ra các trường hợp sử dụng mới và được yêu cầu nhiều trên web, chẳng hạn như triển khai các cơ sở dữ liệu hiệu suất cao và quản lý linh hoạt các tệp tạm thời có dung lượng lớn. Với giao diện mới này, nhà phát triển có thể "mang bộ nhớ của riêng mình" lên web, giảm khoảng cách về tính năng giữa mã dành riêng cho web và nền tảng.

Storage Foundation API được thiết kế giống với một hệ thống tệp rất cơ bản để giúp nhà phát triển linh hoạt hơn bằng cách cung cấp các dữ liệu gốc chung, đơn giản và có hiệu suất cao để xây dựng các thành phần cấp cao hơn. Các ứng dụng có thể tận dụng công cụ tốt nhất theo nhu cầu của mình, tìm ra sự cân bằng phù hợp giữa khả năng hữu dụng, hiệu suất và độ tin cậy.

Tại sao web cần một API lưu trữ khác?

Nền tảng web này cung cấp một số tuỳ chọn bộ nhớ cho các nhà phát triển, mỗi tuỳ chọn được xây dựng cho các trường hợp sử dụng cụ thể.

  • Một số tuỳ chọn rõ ràng không trùng lặp với đề xuất này vì chúng chỉ cho phép lưu trữ lượng dữ liệu rất nhỏ, như cookie hoặc API Bộ nhớ web bao gồm sessionStorage và cơ chế localStorage.
  • Các tuỳ chọn khác đã ngừng hoạt động vì nhiều lý do, chẳng hạn như File and Directory Items API (API Mục nhập tệp và thư mục) hoặc WebSQL.
  • File System Access API (API Truy cập hệ thống tệp) có nền tảng API tương tự, nhưng mục đích sử dụng API này là để giao tiếp với hệ thống tệp của ứng dụng và cung cấp quyền truy cập vào dữ liệu có thể không thuộc quyền sở hữu của nguồn gốc hoặc thậm chí của trình duyệt. Trọng tâm khác này đi kèm với các cân nhắc nghiêm ngặt hơn về bảo mật và chi phí hiệu suất cao hơn.
  • Bạn có thể sử dụng IndexedDB API làm phần phụ trợ cho một số trường hợp sử dụng của Storage Foundation API. Ví dụ: Emscripten bao gồm IDBFS, một hệ thống tệp ổn định dựa trên IndexedDB. Tuy nhiên, vì về cơ bản, IndexedDB là một kho khoá-giá trị nên đi kèm với những hạn chế đáng kể về hiệu suất. Hơn nữa, việc truy cập trực tiếp vào các phần phụ của tệp thậm chí còn khó khăn và chậm hơn trong IndexedDB.
  • Cuối cùng, giao diện Bộ nhớ đệm được hỗ trợ rộng rãi và được điều chỉnh để lưu trữ dữ liệu có kích thước lớn, chẳng hạn như tài nguyên ứng dụng web, nhưng các giá trị là không thể thay đổi.

Storage Foundation API là một nỗ lực nhằm thu hẹp tất cả các khoảng trống của các tuỳ chọn bộ nhớ trước đó bằng cách cho phép lưu trữ hiệu suất của các tệp lớn có thể thay đổi được xác định trong nguồn gốc của ứng dụng.

Các trường hợp sử dụng được đề xuất cho Storage Foundation API

Ví dụ về những trang web có thể sử dụng API này bao gồm:

  • Những ứng dụng cải thiện hiệu suất hoặc sáng tạo, hoạt động trên một lượng lớn dữ liệu video, âm thanh hoặc hình ảnh. Những ứng dụng như vậy có thể giảm tải các phân đoạn vào ổ đĩa thay vì lưu chúng trong bộ nhớ.
  • Các ứng dụng dựa vào một hệ thống tệp cố định có thể truy cập được từ Wasm và cần hiệu suất cao hơn mức mà IDBFS có thể đảm bảo.

Storage Foundation API là gì?

API có hai phần chính:

  • Lệnh gọi hệ thống tệp, cung cấp chức năng cơ bản để tương tác với tệp và đường dẫn tệp.
  • Xử lý tệp, cung cấp quyền đọc và ghi đối với tệp hiện có.

Lệnh gọi hệ thống tệp

Storage Foundation API ra mắt một đối tượng mới, storageFoundation, nằm trên đối tượng window và bao gồm một số hàm:

  • storageFoundation.open(name): Mở tệp bằng tên đã cung cấp (nếu có) rồi tạo một tệp mới. Trả về một lời hứa sẽ phân giải với tệp đã mở.
  • storageFoundation.delete(name): Xoá tệp có tên đã cho. Trả về một lời hứa sẽ giải quyết khi tệp bị xoá.
  • storageFoundation.rename(oldName, newName): Đổi tên tệp từ tên cũ thành tên mới một cách chi tiết. Trả về một lời hứa sẽ phân giải khi tệp được đổi tên.
  • storageFoundation.getAll(): Trả về một lời hứa mà sẽ phân giải bằng một mảng gồm tất cả các tên tệp hiện có.
  • storageFoundation.requestCapacity(requestedCapacity): Yêu cầu dung lượng mới (tính bằng byte) để sử dụng theo ngữ cảnh thực thi hiện tại. Trả về một lời hứa đã giải quyết với dung lượng còn lại.
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): Giải phóng số lượng byte được chỉ định khỏi ngữ cảnh thực thi hiện tại và trả về một lời hứa sẽ phân giải bằng dung lượng còn lại.
  • storageFoundation.getRemainingCapacity(): Trả về một lời hứa giải quyết với dung lượng có sẵn cho ngữ cảnh thực thi hiện tại.

Xử lý tệp

Thao tác với tệp diễn ra thông qua các hàm sau:

  • NativeIOFile.close(): Đóng tệp và trả về một lời hứa sẽ giải quyết khi thao tác hoàn tất.
  • NativeIOFile.flush(): Đồng bộ hoá (nghĩa là xả dữ liệu) trạng thái trong bộ nhớ của tệp với thiết bị lưu trữ và trả về một lời hứa sẽ phân giải khi thao tác hoàn tất.
  • NativeIOFile.getLength(): Trả về một lời hứa phân giải với độ dài của tệp tính bằng byte.
  • NativeIOFile.setLength(length): Đặt độ dài của tệp tính bằng byte và trả về một lời hứa sẽ giải quyết khi thao tác hoàn tất. Nếu độ dài mới nhỏ hơn độ dài hiện tại, thì các byte sẽ bị xoá kể từ cuối tệp. Nếu không, tệp sẽ được mở rộng bằng các byte có giá trị bằng 0.
  • NativeIOFile.read(buffer, offset): Đọc nội dung của tệp tại độ lệch đã cho thông qua một vùng đệm là kết quả của việc chuyển vùng đệm đã cho, sau đó vùng đệm này được tách rời. Trả về NativeIOReadResult có vùng đệm đã chuyển và số byte đã được đọc thành công.

    NativeIOReadResult là một đối tượng bao gồm hai mục nhập:

    • buffer: ArrayBufferView, là kết quả của việc chuyển vùng đệm được truyền sang read(). Lớp này có cùng kiểu và độ dài với vùng đệm nguồn.
    • readBytes: Số byte đã được đọc thành công vào buffer. Dung lượng này có thể nhỏ hơn dung lượng bộ nhớ đệm, nếu xảy ra lỗi hoặc nếu phạm vi đọc vượt quá phần cuối của tệp. Giá trị này được đặt thành 0 nếu phạm vi đọc nằm ngoài phần cuối tệp.
  • NativeIOFile.write(buffer, offset): Ghi nội dung của vùng đệm nhất định vào tệp tại độ lệch đã cho. Vùng đệm được chuyển trước khi ghi bất kỳ dữ liệu nào và do đó được tách rời. Trả về một NativeIOWriteResult có vùng đệm đã chuyển và số byte đã được ghi thành công. Tệp sẽ được mở rộng nếu phạm vi ghi vượt quá độ dài.

    NativeIOWriteResult là một đối tượng bao gồm hai mục nhập:

    • buffer: ArrayBufferView là kết quả của việc chuyển vùng đệm được truyền sang write(). Lớp này có cùng kiểu và độ dài với vùng đệm nguồn.
    • writtenBytes: Số byte đã được ghi thành công vào buffer. Dung lượng này có thể nhỏ hơn dung lượng bộ nhớ đệm nếu xảy ra lỗi.

Ví dụ đầy đủ

Để làm rõ các khái niệm nêu trên, sau đây là 2 ví dụ hoàn chỉnh sẽ hướng dẫn bạn thực hiện các giai đoạn trong vòng đời của tệp Storage Foundation.

Mở, viết, đọc, đóng

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

Mở, lập danh sách, xoá

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

Bản minh hoạ

Bạn có thể chơi bản minh hoạ Storage Foundation API trong phần nhúng bên dưới. Tạo, đổi tên, ghi vào và đọc từ các tệp, đồng thời xem dung lượng sẵn có mà bạn đã yêu cầu cập nhật khi thực hiện thay đổi. Bạn có thể tìm thấy mã nguồn của bản minh hoạ trên Glitch.

Tính bảo mật và quyền

Nhóm Chromium đã thiết kế và triển khai Storage Foundation API theo các nguyên tắc cốt lõi nêu trong phần Kiểm soát quyền truy cập vào các tính năng của nền tảng web mạnh mẽ, bao gồm cả quyền kiểm soát người dùng, tính minh bạch và tính công thái học.

Tuân theo mô hình tương tự như các API lưu trữ hiện đại khác trên web, quyền truy cập vào Storage Foundation API bị ràng buộc theo nguyên gốc, tức là một nguồn gốc chỉ có thể truy cập vào dữ liệu tự tạo. Tính năng này cũng bị giới hạn trong các bối cảnh an toàn.

Quyền kiểm soát của người dùng

Hạn mức bộ nhớ sẽ được dùng để phân phối quyền truy cập vào dung lượng ổ đĩa và ngăn chặn việc sử dụng sai mục đích. Bộ nhớ bạn muốn chiếm trước tiên phải được yêu cầu. Giống như các API lưu trữ khác, người dùng có thể giải phóng dung lượng mà Storage Foundation API đã sử dụng thông qua trình duyệt.

Các đường liên kết hữu ích

Xác nhận

Storage Foundation API do Emanuel KrivoyRichard Stotz chỉ định và triển khai. Bài viết này được Pete LePageJoe Medley đánh giá.

Hình ảnh chính qua Markus Spiske trên Unsplash.