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 để tạo các ứng dụng hiệu suất cao được tinh chỉnh cho web. Đặc biệ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ã đã được thử nghiệm và kiểm chứng 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ó cùng khả năng và tính linh hoạt khi nói đến bộ nhớ.
Đây là lúc Storage Foundation API phát huy tác dụng. Storage Foundation API là một API lưu trữ mới, nhanh chóng và không thiên vị, mở ra những 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 hiệu suất cao và quản lý các tệp tạm thời lớn một cách hiệu quả. Với giao diện mới này, nhà phát triển có thể "mang bộ nhớ riêng" lên web, giảm khoảng cách về tính năng giữa web và mã dành riêng cho nền tảng.
Storage Foundation API được thiết kế để mô phỏng một hệ thống tệp rất cơ bản, nhờ đó mang lại cho nhà phát triển sự linh hoạt bằng cách cung cấp các thành phần cơ bản chung, đơn giản và hiệu quả mà họ có thể dùng để 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ụ phù hợp nhất cho nhu cầu của mình, tìm ra sự cân bằng phù hợp giữa khả năng sử 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 lưu trữ cho nhà phát triển, mỗi lựa chọn được xây dựng dựa trên các 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 Web Storage API bao gồm cơ chế
sessionStorage
vàlocalStorage
. - Các lựa chọn khác đã không được dùng nữa vì nhiều lý do, chẳng hạn như File and Directory Entries API hoặc WebSQL.
- File System Access API có một giao diện API tương tự, nhưng mục đích sử dụng là để tương tác với hệ thống tệp của máy khách và cung 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. Sự khác biệt này đi kèm với các yếu tố 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 một 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 liên tục dựa trên IndexedDB. Tuy nhiên, vì IndexedDB về cơ bản là một kho khoá-giá trị, nên nó có 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 một tệp còn khó khăn và chậm hơn nữa 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à bất biến.
Storage Foundation API là một nỗ lực nhằm khắc phục mọi thiếu sót của các lựa 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 đượ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ề các trang web có thể sử dụng API này:
- Các ứng dụng năng 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. Các ứng dụng như vậy có thể chuyển các phân đoạn sang ổ đĩa thay vì giữ chúng trong bộ nhớ.
- Các ứng dụng dựa vào một hệ thống tệp liên tục có thể truy cập từ Wasm và cần hiệu suất cao hơn so với những gì IDBFS có thể đảm bảo.
Storage Foundation API là gì?
API có 2 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 các tệp và đường dẫn tệp.
- Xử lý tệp, cung cấp quyền đọc và ghi cho một tệp hiện có.
Cuộc gọi hệ thống tệp
Storage Foundation API giới thiệu 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 có tên đã cho nếu tệp đó tồn tại, nếu không, hãy tạo một tệp mới. Trả về một lời hứa được giải quyết bằng tệp đã mở.
storageFoundation.delete(name)
: Xoá tệp có tên đã cho. Trả về một 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ũ sang tên mới một cách nguyên 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 được giải quyết bằng một mảng gồm tất 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 đã chỉ định khỏi ngữ cảnh thực thi hiện tại và trả về một lời hứa được giải quyết với dung lượng còn lại.storageFoundation.getRemainingCapacity()
: Trả về một lời hứa được giải quyết với dung lượng có sẵn cho ngữ cảnh thực thi hiện tại.
Trình xử lý tệp
Bạn có thể làm việc với các tệp thông qua các hàm sau:
NativeIOFile.close()
: Đóng một tệp và trả về một lời hứa sẽ phân giải khi thao tác hoàn tất.NativeIOFile.flush()
: Đồng bộ hoá (tức là xoá) trạng thái trong bộ nhớ của một 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 được giải quyết bằng độ 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ẽ 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, các byte sẽ bị xoá bắt đầu 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 sẽ bị tách ra. Trả về mộtNativeIOReadResult
có vùng đệm được chuyển và số lượng byte đã đọc thành công.NativeIOReadResult
là một đối tượng bao gồm 2 mục:buffer
: MộtArrayBufferView
, là kết quả của việc chuyển vùng đệm được truyền đếnread()
. Nó có cùng loại và độ dài với vùng đệm nguồn.readBytes
: Số byte đã được đọc thành công vàobuffer
. Giá trị này có thể nhỏ hơn kích thước bộ nhớ đệm, nếu xảy ra lỗi hoặc nếu phạm vi đọc trải dài vượt quá cuối 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 của tệp.
NativeIOFile.write(buffer, offset)
: Ghi nội dung của vùng đệm đã cho vào tệp tại độ lệch đã cho. Vùng đệm được chuyển trước khi bất kỳ dữ liệu nào được ghi và do đó vẫn ở trạng thái tách rời. Trả về mộtNativeIOWriteResult
có 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 của tệp.NativeIOWriteResult
là một đối tượng bao gồm 2 mục:buffer
: MộtArrayBufferView
là kết quả của việc chuyển vùng đệm được truyền đếnwrite()
. Nó có cùng loại và độ dài với vùng đệm nguồn.writtenBytes
: Số byte đã được ghi thành công vàobuffer
. Giá trị này có thể nhỏ hơn kích thước vùng đệm nếu xảy ra lỗi.
Ví dụ đầy đủ
Để làm rõ hơn các khái niệm được giới thiệu ở trên, sau đâ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 các 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ở, 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();
Tính bảo mật và quyền truy cập
Nhóm Chromium đã thiết kế và triển khai Storage Foundation API bằng các nguyên tắc cốt lõi được xác định trong 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 quyền kiểm soát của người dùng, tính minh bạch và tính tiện dụng.
Theo cùng một mẫu như các API bộ nhớ hiện đại khác trên web, quyền truy cập vào Storage Foundation API bị 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 do chính nguồn gốc đó tạo. API này cũng chỉ giới hạn trong các ngữ cảnh bảo mật.
Quyền kiểm soát của người dùng
Hạn mức lưu trữ 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 sai trái. Trước tiên, bạn cần yêu cầu bộ nhớ mà bạn muốn chiếm dụng. Giống như các Storage API khác, người dùng có thể xoá dung lượng mà Storage Foundation API chiếm dụng thông qua trình duyệt của họ.
Đường liên kết hữu ích
- Thông tin giải thích công khai
- Lỗi theo dõi Chromium
- Mục nhập trên ChromeStatus.com
- Thành phần Blink:
Blink>Storage>NativeIO
- Bài đánh giá của TAG
- Ý định tạo nguyên mẫu
- Luồng WebKit
- Luồng Mozilla
Lời cảm ơn
Storage Foundation API được chỉ định và triển khai bởi Emanuel Krivoy và Richard Stotz. Bài viết này được Pete LePage và Joe Medley xem xét.
Hình ảnh chính của Markus Spiske trên Unsplash.