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 các 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, WebAssembly (Wasm) đã mở ra cơ hội 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ã đã thử và kiểm thử trên web. Để thực sự tận dụng tiềm năng này, các nhà phát triển phải có sức mạnh và sự linh hoạt tương đương khi sử dụng bộ nhớ.

Đây là lúc API nền tảng lưu trữ phát huy tác dụng. Storage Foundation API là một API lưu trữ nhanh và tiện dụng mới, giúp mở khoá các trường hợp sử dụng mới và được yêu cầu nhiều cho web, chẳng hạn như triển khai cơ sở dữ liệu có hiệu suất tốt và quản lý linh hoạt các tệp lớn tạm thời. Với giao diện mới này, nhà phát triển có thể "cung cấp bộ nhớ riêng" cho 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. Vì vậy, API này giúp nhà phát triển linh hoạt bằng cách cung cấp các dữ liệu gốc chung, đơn giản và hiệu quả để họ có thể 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 để đáp ứng nhu cầu của ứng dụng, tìm được sự cân bằng hợp lý 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 cung cấp một số lựa chọn về bộ nhớ cho nhà phát triển, mỗi lựa chọn được xây dựng theo những trường hợp sử dụng cụ thể.

  • Một số lựa chọn trong số này 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ữ một lượng dữ liệu rất nhỏ, chẳng hạn như cookie hoặc API Bộ nhớ web bao gồm cơ chế sessionStoragelocalStorage.
  • Các tuỳ chọn khác hiện không còn được dùng nữa vì nhiều lý do, chẳng hạn như File and Directory Methods API hoặc WebSQL.
  • File System Access API có giao diện API tương tự, nhưng công dụng của API này là để tương tác với hệ thống tệp của ứng dụng và cấp quyền truy cập vào dữ liệu có thể nằm ngoài quyền sở hữu của nguồn gốc hoặc thậm chí là 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 bảo mật nghiêm ngặt hơn và chi phí hiệu suất cao hơn.
  • Bạn có thể 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 bền vững dựa trên IndexedDB. Tuy nhiên, vì về cơ bản, IndexedDB là một kho lưu trữ khoá-giá trị, nên nó đi kèm với một số 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 mục con của tệp thậm chí còn khó và chậm hơn trong IndexedDB.
  • Cuối cùng, giao diện CacheStorage đượ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à nỗ lực nhằm thu hẹp mọi khoảng trống của các tuỳ chọn lưu trữ trước đây bằng cách cho phép lưu trữ hiệu quả các tệp lớn có thể thay đổi và được xác định trong nguồn gốc ứng dụng.

Các trường hợp sử dụng nên dùng cho Storage Foundation API

Ví dụ về các 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 giữ các phân đoạn đó trong bộ nhớ.
  • Những ứng dụng dựa vào một hệ thống tệp bền vững có thể truy cập được từ Wasm và cần hiệu suất cao hơn so với khả năng đảm bảo của IDBFS.

Storage Foundation API là gì?

Có hai phần chính của API:

  • 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 các tệp và đường dẫn tệp.
  • Tên người dùng tệp, 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 giới thiệu một đối tượng mới là storageFoundation, nằm trên đối tượng window và có một số hàm:

  • storageFoundation.open(name): Mở tệp bằng tên đã cho (nếu có) và tạo tệp mới. Trả về lời hứa phân giải với tệp đã mở.
  • storageFoundation.delete(name): Xoá tệp có tên đã cho. Trả về lời hứa sẽ phân giải khi tệp bị xoá.
  • storageFoundation.rename(oldName, newName): Đổi tên tệp từ tên cũ thành tên mới theo từng tỷ lệ. Trả về lời hứa sẽ phân giải khi tệp được đổi tên.
  • storageFoundation.getAll(): Trả về lời hứa 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 đã được giải quyết với lượng 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ẽ giải quyết dung lượng còn lại.
  • storageFoundation.getRemainingCapacity(): Trả về một lời hứa phân giải với dung lượng hiện có cho ngữ cảnh thực thi hiện tại.

Tên người dùng trong 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ề lời hứa sẽ phân giải khi hoạt động hoàn tất.
  • NativeIOFile.flush(): Đồng bộ hoá (tức là xoá) trạng thái trong bộ nhớ của tệp với thiết bị lưu trữ và trả về lời hứa sẽ phân giải khi thao tác hoàn tất.
  • NativeIOFile.getLength(): Trả về một hàm xác thực có độ dài tệp tính bằng byte.
  • NativeIOFile.setLength(length): Thiết lập độ dài của tệp tính bằng byte và trả về một lời hứa sẽ phân giải 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 với các byte có giá trị bằng 0.
  • NativeIOFile.read(buffer, offset): Đọc nội dung của tệp ở độ lệch nhất định thông qua một vùng đệm là kết quả của quá trình chuyển vùng đệm nhất định, sau đó vùng đệm này sẽ được tách ra. Trả về một NativeIOReadResult với vùng đệm được chuyển và số byte đã được đọc thành công.

    NativeIOReadResult là một đối tượng bao gồm 2 mục:

    • buffer: ArrayBufferView, là kết quả của quá trình chuyển vùng đệm được truyền đến read(). Vùng đệm 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. Giá trị 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á cuối tệp. Giá trị này được đặt thành 0 nếu phạm vi đọc vượt quá khoảng cuối tệp.
  • NativeIOFile.write(buffer, offset): Ghi nội dung của vùng đệm nhất định vào tệp ở độ lệch nhất định. Vùng đệm được chuyển trước khi bất kỳ dữ liệu nào được ghi và do đó bị tách ra. Trả về một NativeIOWriteResult với vùng đệm được 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 2 mục:

    • buffer: ArrayBufferView là kết quả của quá trình chuyển vùng đệm được truyền đến write(). Vùng đệm 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 đủ

Để giúp các khái niệm được giới thiệu ở trên rõ ràng hơn, dưới đây là hai ví dụ hoàn chỉnh hướng dẫn bạn qua các giai đoạn khác nhau trong vòng đời của tệp Storage Foundation.

Mở, ghi, đọ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ở, liệt kê, 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ể xem bản minh hoạ API Storage Foundation 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 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 bài viết Kiểm soát quyền truy cập vào các tính năng mạnh mẽ của nền tảng web, bao gồm cả quyền kiểm soát của người dùng, tính minh bạch và công thái học.

Theo cùng một mẫu 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 đều có ràng buộc theo nguồn gốc, nghĩa là một nguồn gốc chỉ có thể truy cập vào dữ liệu tự tạo. Điều này cũng bị giới hạn ở ngữ cảnh bảo mật.

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 hành vi lạm dụng. Trước tiên, bạn cần yêu cầu cấp bộ nhớ muốn chiếm dụng. Giống như các API lưu trữ khác, người dùng có thể xoá dung lượng do Storage Foundation API chiếm dụng thông qua trình duyệt của họ.

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 do Pete LePageJoe Medley đánh giá.

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