API Truy cập hệ thống tệp cho phép các ứng dụng web đọc hoặc lưu trực tiếp các thay đổi đối với tệp và thư mục trên thiết bị của người dùng.
API Truy cập hệ thống tệp là gì?
File System Access API cho phép nhà phát triển xây dựng các ứng dụng web mạnh mẽ tương tác với trên thiết bị cục bộ của người dùng, chẳng hạn như IDE, trình chỉnh sửa ảnh và video, trình chỉnh sửa văn bản, v.v. Sau người dùng cấp quyền truy cập vào ứng dụng web, API này cho phép họ đọc hoặc lưu trực tiếp các thay đổi vào tệp và các thư mục trên thiết bị của người dùng. Ngoài việc đọc và ghi tệp, API Truy cập hệ thống tệp còn cung cấp khả năng mở một thư mục và liệt kê nội dung của thư mục đó.
Nếu bạn đã từng đọc và ghi tệp trước đây, phần lớn những gì tôi sắp chia sẻ sẽ là quen thuộc với bạn. Bạn nên đọc hướng dẫn này vì không phải mọi hệ thống đều giống nhau.
API Truy cập hệ thống tệp được hỗ trợ trên hầu hết các trình duyệt Chromium trên Windows, macOS, ChromeOS và Linux. Một ngoại lệ đáng chú ý là Brave hiện chỉ hoạt động sau một cờ. Chúng tôi đang nỗ lực hỗ trợ Android theo ngữ cảnh crbug.com/1011535.
Sử dụng API Truy cập hệ thống tệp
Để thể hiện sức mạnh và tính hữu ích của API Truy cập hệ thống tệp, tôi đã viết một tệp văn bản trình chỉnh sửa. Cửa sổ này cho phép bạn mở, chỉnh sửa tệp văn bản, lưu thay đổi trở lại đĩa hoặc bắt đầu tệp mới và lưu thay đổi vào đĩa. Không có gì cầu kỳ nhưng cung cấp đủ để giúp bạn hiểu các khái niệm.
Hỗ trợ trình duyệt
Phát hiện tính năng
Để tìm hiểu xem API Truy cập hệ thống tệp có được hỗ trợ hay không, hãy kiểm tra xem phương thức bộ chọn có được hỗ trợ hay không mà bạn quan tâm đã tồn tại.
if ('showOpenFilePicker' in self) {
// The `showOpenFilePicker()` method of the File System Access API is supported.
}
Dùng thử
Xem cách API Truy cập hệ thống tệp hoạt động trong bản minh hoạ về trình chỉnh sửa văn bản.
Đọc một tệp từ hệ thống tệp cục bộ
Trường hợp sử dụng đầu tiên mà tôi muốn xử lý là yêu cầu người dùng chọn một tệp, sau đó mở và đọc tệp đó khỏi đĩa.
Yêu cầu người dùng chọn một tệp để đọc
Điểm truy cập vào API Truy cập hệ thống tệp là
window.showOpenFilePicker()
. Khi được gọi, ứng dụng này sẽ hiện hộp thoại chọn tệp,
và nhắc người dùng chọn một tệp. Sau khi người dùng chọn một tệp, API sẽ trả về một mảng tệp
tên người dùng. Tham số options
không bắt buộc cho phép bạn tác động đến hành vi của bộ chọn tệp, đối với
ví dụ: bằng cách cho phép người dùng chọn nhiều tệp hoặc thư mục hoặc nhiều loại tệp.
Nếu không chỉ định bất kỳ tuỳ chọn nào, bộ chọn tệp sẽ cho phép người dùng chọn một tệp. Đây là
hoàn hảo cho một trình soạn thảo văn bản.
Giống như nhiều API mạnh mẽ khác, việc gọi showOpenFilePicker()
phải được thực hiện trong một bảo mật
ngữ cảnh và phải được gọi từ trong một cử chỉ của người dùng.
let fileHandle;
butOpenFile.addEventListener('click', async () => {
// Destructure the one-element array.
[fileHandle] = await window.showOpenFilePicker();
// Do something with the file handle.
});
Sau khi người dùng chọn một tệp, showOpenFilePicker()
sẽ trả về một mảng tên người dùng, trong trường hợp này là
mảng một phần tử với một FileSystemFileHandle
chứa các thuộc tính và
cần thiết để tương tác với tệp đó.
Bạn nên giữ lại một tham chiếu đến tên người dùng tệp để có thể sử dụng sau này. Sẽ là để lưu những thay đổi đối với tệp hoặc để thực hiện bất kỳ thao tác nào khác đối với tệp.
Đọc một tệp từ hệ thống tệp
Giờ đây, khi đã có tên người dùng cho một tệp, bạn có thể lấy các thuộc tính của tệp đó hoặc truy cập vào chính tệp đó.
Bây giờ, tôi sẽ đọc nội dung của email. Việc gọi handle.getFile()
sẽ trả về File
chứa một blob. Để lấy dữ liệu từ blob, hãy gọi một trong các
phương thức, (slice()
,
stream()
,
text()
hoặc
arrayBuffer()
).
const file = await fileHandle.getFile();
const contents = await file.text();
Đối tượng File
do FileSystemFileHandle.getFile()
trả về chỉ có thể đọc được, miễn là
tệp cơ bản trên đĩa không thay đổi. Nếu tệp trên đĩa được sửa đổi, đối tượng File
sẽ trở thành
không thể đọc được và bạn phải gọi lại getFile()
để lấy đối tượng File
mới nhằm đọc nội dung đã thay đổi
.
Kết hợp kiến thức đã học
Khi người dùng nhấp vào nút Open (Mở), trình duyệt sẽ hiển thị một bộ chọn tệp. Sau khi chọn một tệp,
ứng dụng đọc nội dung và đặt chúng vào <textarea>
.
let fileHandle;
butOpenFile.addEventListener('click', async () => {
[fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const contents = await file.text();
textArea.value = contents;
});
Ghi tệp vào hệ thống tệp cục bộ
Trong trình chỉnh sửa văn bản, có hai cách để lưu tệp: Lưu và Lưu dưới dạng. Lưu ghi các thay đổi trở lại tệp gốc bằng cách sử dụng tên người dùng tệp được truy xuất trước đó. Nhưng Lưu Khi tạo một tệp mới nên yêu cầu một tên người dùng mới cho tệp.
Tạo tệp mới
Để lưu một tệp, hãy gọi hàm showSaveFilePicker()
để cho thấy bộ chọn tệp
trong "lưu" chế độ này, cho phép người dùng chọn tệp mới họ muốn sử dụng để lưu. Đối với văn bản
trình chỉnh sửa của mình, tôi cũng muốn tiện ích này tự động thêm một tiện ích .txt
, vì vậy, tôi đã cung cấp thêm
tham số.
async function getNewFileHandle() {
const options = {
types: [
{
description: 'Text Files',
accept: {
'text/plain': ['.txt'],
},
},
],
};
const handle = await window.showSaveFilePicker(options);
return handle;
}
Lưu thay đổi vào đĩa
Bạn có thể tìm thấy tất cả mã để lưu thay đổi cho tệp trong bản minh hoạ trình chỉnh sửa văn bản của tôi trên
GitHub. Các hoạt động tương tác chính với hệ thống tệp nằm trong
fs-helpers.js
. Cách đơn giản nhất là quá trình này có dạng như mã sau.
Tôi sẽ giải thích từng bước và giải thích từng bước.
// fileHandle is an instance of FileSystemFileHandle..
async function writeFile(fileHandle, contents) {
// Create a FileSystemWritableFileStream to write to.
const writable = await fileHandle.createWritable();
// Write the contents of the file to the stream.
await writable.write(contents);
// Close the file and write the contents to disk.
await writable.close();
}
Việc ghi dữ liệu vào ổ đĩa bằng đối tượng FileSystemWritableFileStream
là một lớp con
trong tổng số WritableStream
. Tạo luồng bằng cách gọi createWritable()
trên tệp
xử lý. Khi createWritable()
được gọi, trước tiên, trình duyệt sẽ kiểm tra xem người dùng đã cấp quyền hay chưa
quyền ghi vào tệp. Nếu bạn chưa cấp quyền ghi, trình duyệt sẽ nhắc
để người dùng cấp quyền. Nếu chưa được cấp quyền, createWritable()
sẽ gửi ra một
DOMException
và ứng dụng sẽ không thể ghi vào tệp. Trong trình chỉnh sửa văn bản,
Các đối tượng DOMException
được xử lý trong phương thức saveFile()
.
Phương thức write()
nhận một chuỗi. Đây là chuỗi cần thiết cho một trình chỉnh sửa văn bản. Tuy nhiên, quá trình này cũng có thể mất nhiều thời gian
BufferSource hoặc Blob. Ví dụ: bạn có thể chuyển luồng trực tiếp đến
nó:
async function writeURLToFile(fileHandle, url) {
// Create a FileSystemWritableFileStream to write to.
const writable = await fileHandle.createWritable();
// Make an HTTP request for the contents.
const response = await fetch(url);
// Stream the response into the file.
await response.body.pipeTo(writable);
// pipeTo() closes the destination pipe by default, no need to close it.
}
Bạn cũng có thể seek()
hoặc truncate()
trong luồng để cập nhật
tại một vị trí cụ thể hoặc đổi kích thước tệp.
Chỉ định tên tệp được đề xuất và thư mục bắt đầu
Trong nhiều trường hợp, bạn có thể muốn ứng dụng của mình đề xuất tên tệp hoặc vị trí mặc định. Ví dụ: văn bản
trình chỉnh sửa nên đề xuất tên tệp mặc định là Untitled Text.txt
thay vì Untitled
. Bạn
bạn có thể thực hiện điều này bằng cách truyền thuộc tính suggestedName
như một phần của các tuỳ chọn showSaveFilePicker
.
const fileHandle = await self.showSaveFilePicker({
suggestedName: 'Untitled Text.txt',
types: [{
description: 'Text documents',
accept: {
'text/plain': ['.txt'],
},
}],
});
Tương tự như vậy đối với thư mục khởi động mặc định. Nếu đang xây dựng một trình chỉnh sửa văn bản, có thể bạn cần
bắt đầu hộp thoại lưu tệp hoặc mở tệp trong thư mục documents
mặc định, còn đối với hình ảnh
trình chỉnh sửa của bạn, bạn nên bắt đầu trong thư mục pictures
mặc định. Bạn có thể đề xuất thời điểm bắt đầu mặc định
thư mục bằng cách truyền thuộc tính startIn
đến showSaveFilePicker
, showDirectoryPicker()
hoặc
showOpenFilePicker
như vậy.
const fileHandle = await self.showOpenFilePicker({
startIn: 'pictures'
});
Danh sách các thư mục hệ thống phổ biến là:
desktop
: Thư mục máy tính của người dùng, nếu có.documents
: Thư mục lưu trữ các tài liệu do người dùng tạo.downloads
: Thư mục nơi thường lưu trữ các tệp được tải xuống.music
: Thư mục nơi thường lưu trữ các tệp âm thanh.pictures
: Thư mục thường lưu trữ ảnh và các hình ảnh tĩnh khác.videos
: Thư mục nơi video hoặc phim thường được lưu trữ.
Ngoài các thư mục hệ thống phổ biến, bạn cũng có thể truyền một tên người dùng tệp hoặc thư mục hiện có dưới dạng
một giá trị cho startIn
. Sau đó, hộp thoại sẽ mở ra trong cùng thư mục đó.
// Assume `directoryHandle` is a handle to a previously opened directory.
const fileHandle = await self.showOpenFilePicker({
startIn: directoryHandle
});
Chỉ định mục đích của các bộ chọn tệp khác nhau
Đôi khi, các ứng dụng có bộ chọn khác nhau cho những mục đích khác nhau. Ví dụ: văn bản đa dạng thức
có thể cho phép người dùng mở tệp văn bản nhưng cũng có thể nhập hình ảnh. Theo mặc định, mỗi tệp
sẽ mở tại vị trí được nhớ cuối cùng. Bạn có thể tránh né điều này bằng cách lưu trữ các giá trị id
cho mỗi loại bộ chọn. Nếu bạn chỉ định id
, thì quá trình triển khai bộ chọn tệp sẽ ghi nhớ một
riêng thư mục dùng gần đây nhất cho id
đó.
const fileHandle1 = await self.showSaveFilePicker({
id: 'openText',
});
const fileHandle2 = await self.showSaveFilePicker({
id: 'importImage',
});
Lưu trữ các tên người dùng tệp hoặc tên người dùng thư mục trong IndexedDB
Tên người dùng tệp và tên người dùng thư mục có thể chuyển đổi tuần tự, tức là bạn có thể lưu tệp hoặc
xử lý thư mục đến IndexedDB hoặc gọi postMessage()
để gửi chúng giữa cùng một cấp cao nhất
máy chủ gốc.
Lưu tệp hoặc tên người dùng thư mục vào IndexedDB có nghĩa là bạn có thể lưu trữ trạng thái hoặc nhớ trạng thái nào các tệp hoặc thư mục mà người dùng đang thao tác. Thao tác này giúp bạn có thể giữ lại danh sách các sự kiện đã mở gần đây hoặc đã chỉnh sửa, đề nghị mở lại tệp gần đây nhất khi ứng dụng được mở, khôi phục lại trạng thái làm việc trước đó và nhiều thứ khác. Trong trình chỉnh sửa văn bản, tôi lưu trữ danh sách 5 tệp gần đây nhất mà người dùng đã truy cập đã mở để có thể truy cập lại vào những tệp đó.
Ví dụ về mã sau đây cho thấy việc lưu trữ và truy xuất một tên người dùng tệp và một tên người dùng thư mục. Bạn có thể xem điều này trong thực tế trên Glitch. (Tôi sử dụng thư viện idb-keyval cho ngắn gọn.)
import { get, set } from 'https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js';
const pre1 = document.querySelector('pre.file');
const pre2 = document.querySelector('pre.directory');
const button1 = document.querySelector('button.file');
const button2 = document.querySelector('button.directory');
// File handle
button1.addEventListener('click', async () => {
try {
const fileHandleOrUndefined = await get('file');
if (fileHandleOrUndefined) {
pre1.textContent = `Retrieved file handle "${fileHandleOrUndefined.name}" from IndexedDB.`;
return;
}
const [fileHandle] = await window.showOpenFilePicker();
await set('file', fileHandle);
pre1.textContent = `Stored file handle for "${fileHandle.name}" in IndexedDB.`;
} catch (error) {
alert(error.name, error.message);
}
});
// Directory handle
button2.addEventListener('click', async () => {
try {
const directoryHandleOrUndefined = await get('directory');
if (directoryHandleOrUndefined) {
pre2.textContent = `Retrieved directroy handle "${directoryHandleOrUndefined.name}" from IndexedDB.`;
return;
}
const directoryHandle = await window.showDirectoryPicker();
await set('directory', directoryHandle);
pre2.textContent = `Stored directory handle for "${directoryHandle.name}" in IndexedDB.`;
} catch (error) {
alert(error.name, error.message);
}
});
Các quyền và trình xử lý tệp hoặc thư mục được lưu trữ
Vì quyền không phải lúc nào cũng được duy trì giữa các phiên, bạn cần xác minh xem người dùng
đã cấp quyền cho tệp hoặc thư mục bằng queryPermission()
. Nếu họ chưa nhận được, hãy gọi
requestPermission()
để yêu cầu (lại). Cách hoạt động tương tự cũng áp dụng cho tên người dùng tệp và thư mục. Bạn
cần chạy fileOrDirectoryHandle.requestPermission(descriptor)
hoặc
fileOrDirectoryHandle.queryPermission(descriptor)
.
Trong trình chỉnh sửa văn bản, tôi đã tạo một phương thức verifyPermission()
để kiểm tra xem người dùng đã thực hiện
đã cấp quyền và nếu cần, đưa ra yêu cầu.
async function verifyPermission(fileHandle, readWrite) {
const options = {};
if (readWrite) {
options.mode = 'readwrite';
}
// Check if permission was already granted. If so, return true.
if ((await fileHandle.queryPermission(options)) === 'granted') {
return true;
}
// Request permission. If the user grants permission, return true.
if ((await fileHandle.requestPermission(options)) === 'granted') {
return true;
}
// The user didn't grant permission, so return false.
return false;
}
Bằng cách yêu cầu quyền ghi với yêu cầu đọc, tôi đã giảm số lượng lời nhắc cấp quyền; người dùng thấy một lời nhắc khi mở tệp, đồng thời cấp quyền đọc và ghi vào tệp.
Mở một thư mục và liệt kê nội dung trong đó
Để liệt kê tất cả các tệp trong một thư mục, hãy gọi showDirectoryPicker()
. Người dùng
chọn một thư mục trong bộ chọn, sau đó FileSystemDirectoryHandle
là
trả về, cho phép bạn liệt kê và truy cập các tệp của thư mục. Theo mặc định, bạn sẽ đọc
quyền truy cập vào các tệp trong thư mục, nhưng nếu cần quyền ghi, bạn có thể chuyển
{ mode: 'readwrite' }
vào phương thức.
butDir.addEventListener('click', async () => {
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.kind, entry.name);
}
});
Ví dụ: nếu bạn cần truy cập vào từng tệp bằng getFile()
để lấy từng tệp
không sử dụng await
trên từng kết quả theo trình tự, mà hãy xử lý tất cả các tệp trong
song song, ví dụ: sử dụng Promise.all()
.
butDir.addEventListener('click', async () => {
const dirHandle = await window.showDirectoryPicker();
const promises = [];
for await (const entry of dirHandle.values()) {
if (entry.kind !== 'file') {
continue;
}
promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`));
}
console.log(await Promise.all(promises));
});
Tạo hoặc truy cập vào tệp và thư mục trong thư mục
Từ một thư mục, bạn có thể tạo hoặc truy cập vào các tệp và thư mục bằng
getFileHandle()
hoặc tương ứng với getDirectoryHandle()
. Bằng cách truyền vào một đối tượng options
(không bắt buộc) có khoá create
và giá trị boolean là
true
hoặc false
, bạn có thể xác định xem có nên tạo tệp hoặc thư mục mới hay không nếu chưa có.
// In an existing directory, create a new directory named "My Documents".
const newDirectoryHandle = await existingDirectoryHandle.getDirectoryHandle('My Documents', {
create: true,
});
// In this new directory, create a file named "My Notes.txt".
const newFileHandle = await newDirectoryHandle.getFileHandle('My Notes.txt', { create: true });
Giải quyết đường dẫn của một mục trong thư mục
Khi làm việc với các tệp hoặc thư mục trong một thư mục, bạn nên phân giải đường dẫn của mục
liên quan. Bạn có thể thực hiện việc này bằng phương thức resolve()
được đặt tên phù hợp. Để giải quyết vấn đề này,
mục có thể là phần tử con trực tiếp hoặc gián tiếp của thư mục.
// Resolve the path of the previously created file called "My Notes.txt".
const path = await newDirectoryHandle.resolve(newFileHandle);
// `path` is now ["My Documents", "My Notes.txt"]
Xoá tệp và thư mục trong một thư mục
Nếu đã có được quyền truy cập vào một thư mục, bạn có thể xoá các tệp và thư mục được chứa bằng
Phương thức removeEntry()
. Đối với các thư mục, thao tác xoá có thể là đệ quy và bao gồm
tất cả thư mục con và các tệp trong đó.
// Delete a file.
await directoryHandle.removeEntry('Abandoned Projects.txt');
// Recursively delete a folder.
await directoryHandle.removeEntry('Old Stuff', { recursive: true });
Xoá trực tiếp tệp hoặc thư mục
Nếu bạn có quyền truy cập vào một tên người dùng tệp hoặc thư mục, hãy gọi remove()
trên FileSystemFileHandle
hoặc
FileSystemDirectoryHandle
để xoá.
// Delete a file.
await fileHandle.remove();
// Delete a directory.
await directoryHandle.remove();
Đổi tên và di chuyển tệp và thư mục
Bạn có thể đổi tên hoặc di chuyển tệp và thư mục đến một vị trí mới bằng cách gọi move()
trên
Giao diện FileSystemHandle
. FileSystemHandle
có các giao diện con FileSystemFileHandle
và
FileSystemDirectoryHandle
. Phương thức move()
nhận một hoặc hai tham số. Hộp thoại đầu tiên có thể:
là một chuỗi có tên mới hoặc FileSystemDirectoryHandle
vào thư mục đích. Trong
trường hợp sau, tham số thứ hai tùy chọn là một chuỗi có tên mới, vì vậy việc di chuyển và đổi tên có thể
xảy ra trong một bước.
// Rename the file.
await file.move('new_name');
// Move the file to a new directory.
await file.move(directory);
// Move the file to a new directory and rename it.
await file.move(directory, 'newer_name');
Tích hợp tính năng kéo và thả
Chiến lược phát hành đĩa đơn
Giao diện Kéo và thả HTML
cho phép ứng dụng web chấp nhận
tệp được kéo và thả
trên một trang web. Trong thao tác kéo và thả, tệp đã kéo và các mục trong thư mục sẽ được liên kết
với các mục nhập tệp và mục nhập thư mục tương ứng. DataTransferItem.getAsFileSystemHandle()
phương thức trả về một lời hứa có đối tượng FileSystemFileHandle
nếu mục được kéo là một tệp và
hứa hẹn với đối tượng FileSystemDirectoryHandle
nếu mục được kéo là một thư mục. Danh sách sau đây
cho thấy điều này trong thực tế. Lưu ý rằng giao diện Kéo và thả
DataTransferItem.kind
là
"file"
cho cả tệp và thư mục, trong khi FileSystemHandle.kind
của API Truy cập hệ thống tệp là
"file"
cho các tệp và "directory"
cho thư mục.
elem.addEventListener('dragover', (e) => {
// Prevent navigation.
e.preventDefault();
});
elem.addEventListener('drop', async (e) => {
e.preventDefault();
const fileHandlesPromises = [...e.dataTransfer.items]
.filter((item) => item.kind === 'file')
.map((item) => item.getAsFileSystemHandle());
for await (const handle of fileHandlesPromises) {
if (handle.kind === 'directory') {
console.log(`Directory: ${handle.name}`);
} else {
console.log(`File: ${handle.name}`);
}
}
});
Truy cập vào hệ thống tệp riêng tư của máy chủ gốc
Hệ thống tệp riêng tư gốc là một điểm cuối lưu trữ, đúng như tên gọi, là điểm cuối dành riêng cho
nguồn gốc của trang. Mặc dù các trình duyệt thường triển khai chế độ này bằng cách duy trì nội dung của thuộc tính này
hệ thống tệp riêng tư gốc sang ổ đĩa nào đó, thì nội dung không phải là của người dùng
dễ sử dụng. Tương tự, không có kỳ vọng rằng các tệp hoặc thư mục có tên khớp với
tên con của hệ thống tệp riêng tư gốc. Mặc dù trình duyệt có vẻ như
có một số tệp trong nội bộ—vì đây là hệ thống tệp riêng tư gốc — nên trình duyệt có thể lưu trữ
những "tệp" này trong cơ sở dữ liệu hay bất kỳ cấu trúc dữ liệu nào khác. Về cơ bản, nếu bạn sử dụng API này,
không muốn tìm các tệp đã tạo được so khớp từng tệp với một ở đâu đó trên ổ đĩa cứng. Bạn có thể hoạt động như bình thường trên
hệ thống tệp riêng tư gốc sau khi bạn có quyền truy cập vào FileSystemDirectoryHandle
gốc.
const root = await navigator.storage.getDirectory();
// Create a new file handle.
const fileHandle = await root.getFileHandle('Untitled.txt', { create: true });
// Create a new directory handle.
const dirHandle = await root.getDirectoryHandle('New Folder', { create: true });
// Recursively remove a directory.
await root.removeEntry('Old Stuff', { recursive: true });
Truy cập vào các tệp được tối ưu hoá để cải thiện hiệu suất qua hệ thống tệp riêng tư của nguồn gốc
Hệ thống tệp riêng tư gốc cung cấp quyền truy cập tuỳ chọn vào một loại tệp đặc biệt
được tối ưu hoá cho hiệu suất, ví dụ: bằng cách cung cấp quyền ghi tại chỗ và độc quyền đối với các dữ liệu
nội dung. Trong Chromium 102 trở lên, có một phương thức khác được áp dụng cho hệ thống tệp riêng tư gốc dành cho
đơn giản hoá quyền truy cập tệp: createSyncAccessHandle()
(dành cho các thao tác đọc và ghi đồng bộ).
Nội dung này được hiển thị vào FileSystemFileHandle
, nhưng độc quyền ở
Trình chạy web.
// (Read and write operations are synchronous,
// but obtaining the handle is asynchronous.)
// Synchronous access exclusively in Worker contexts.
const accessHandle = await fileHandle.createSyncAccessHandle();
const writtenBytes = accessHandle.write(buffer);
const readBytes = accessHandle.read(buffer, { at: 1 });
Polyfilling
Không thể điền hoàn toàn các phương thức API Truy cập hệ thống tệp.
- Phương thức
showOpenFilePicker()
có thể được ước chừng bằng phần tử<input type="file">
. - Phương thức
showSaveFilePicker()
có thể được mô phỏng bằng phần tử<a download="file_name">
, mặc dù điều này kích hoạt quá trình tải xuống có lập trình và không cho phép ghi đè các tệp hiện có. - Phương thức
showDirectoryPicker()
có thể được mô phỏng một chút bằng phương thức không chuẩn Phần tử<input type="file" webkitdirectory>
.
Chúng tôi đã phát triển một thư viện có tên browser-fs-access sử dụng tệp System Access API (API Truy cập hệ thống) bất cứ khi nào có thể và tương thích với các lựa chọn tốt nhất tiếp theo sau đây trong tất cả các trường hợp.
Tính bảo mật và quyền truy cập
Nhóm Chrome đã thiết kế và triển khai API Truy cập hệ thống tệp theo 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 nền tảng web mạnh mẽ, bao gồm cả người dùng khả năng kiểm soát và tính minh bạch cũng như công thái học người dùng.
Mở tệp hoặc lưu tệp mới
![Bộ chọn tệp để mở tệp và đọc](https://developer.chrome.google.cn/static/docs/capabilities/web-apis/file-system-access/image/file-picker-open-file-8cb5f6783ee89.jpg?authuser=3&hl=vi)
Khi mở tệp, người dùng cấp quyền đọc tệp hoặc thư mục bằng bộ chọn tệp.
Bộ chọn tệp đang mở chỉ có thể hiển thị bằng cử chỉ của người dùng khi được cung cấp từ một chế độ bảo mật
ngữ cảnh. Nếu người dùng đổi ý, họ có thể huỷ lựa chọn trong tệp
và trang web không có quyền truy cập vào bất kỳ nội dung nào. Đây là hành vi tương tự như của
Phần tử <input type="file">
.
![Bộ chọn tệp để lưu tệp vào đĩa.](https://developer.chrome.google.cn/static/docs/capabilities/web-apis/file-system-access/image/file-picker-save-file-bd54ae001799a.jpg?authuser=3&hl=vi)
Tương tự, khi một ứng dụng web muốn lưu một tệp mới, trình duyệt sẽ hiện bộ chọn lưu tệp, cho phép người dùng chỉ định tên và vị trí của tệp mới. Vì họ đang lưu một tệp mới cho thiết bị (so với việc ghi đè một tệp hiện có), bộ chọn tệp sẽ cấp quyền cho ứng dụng ghi vào tệp.
Thư mục bị hạn chế
Để giúp bảo vệ người dùng và dữ liệu của họ, trình duyệt có thể giới hạn khả năng lưu của người dùng vào một số các thư mục, ví dụ: thư mục hệ điều hành cốt lõi như Windows, thư mục Thư viện macOS. Khi điều này xảy ra, trình duyệt sẽ hiển thị lời nhắc và yêu cầu người dùng chọn một .
Sửa đổi tệp hoặc thư mục hiện có
Ứng dụng web không thể sửa đổi tệp trên đĩa khi chưa nhận được sự cho phép rõ ràng của người dùng.
Lời nhắc cấp quyền
Nếu một người muốn lưu các thay đổi vào một tệp mà trước đó họ đã cấp quyền đọc, trình duyệt hiển thị lời nhắc cấp quyền, yêu cầu cấp quyền cho trang web để ghi thay đổi vào đĩa. Yêu cầu cấp quyền chỉ có thể được kích hoạt bằng một cử chỉ của người dùng, ví dụ: bằng cách nhấp vào Lưu .
![Thông báo cấp quyền xuất hiện trước khi lưu tệp.](https://developer.chrome.google.cn/static/docs/capabilities/web-apis/file-system-access/image/permission-prompt-shown-p-823d180f97ad5.png?authuser=3&hl=vi)
Ngoài ra, một ứng dụng web chỉnh sửa nhiều tệp (chẳng hạn như một IDE) cũng có thể yêu cầu quyền lưu thay đổi tại thời điểm mở.
Nếu người dùng chọn Huỷ và không cấp quyền ghi, ứng dụng web sẽ không thể lưu các thay đổi đối với tệp cục bộ. Phải cung cấp cho người dùng một phương thức thay thế để lưu dữ liệu của họ, cho bằng cách cung cấp cách "tải xuống" tệp hoặc lưu dữ liệu lên đám mây.
Sự minh bạch
![Biểu tượng thanh địa chỉ](https://developer.chrome.google.cn/static/docs/capabilities/web-apis/file-system-access/image/omnibox-icon-5797e9a831603.jpg?authuser=3&hl=vi)
Sau khi người dùng cấp quyền cho một ứng dụng web để lưu tệp trên máy, trình duyệt sẽ hiển thị biểu tượng vào thanh địa chỉ. Khi bạn nhấp vào biểu tượng này, một cửa sổ bật lên sẽ mở ra và hiển thị danh sách các tệp mà người dùng đã cung cấp truy cập vào. Người dùng có thể thu hồi quyền truy cập đó bất cứ lúc nào nếu muốn.
Khả năng lưu trữ cố định quyền
Ứng dụng web có thể tiếp tục lưu các thay đổi đối với tệp mà không nhắc cho đến khi tất cả các thẻ cho máy chủ gốc đã đóng. Khi một thẻ bị đóng, trang web sẽ mất tất cả quyền truy cập. Lần tiếp theo khi người dùng sử dụng ứng dụng web, họ sẽ được nhắc lại để truy cập vào các tệp.
Phản hồi
Chúng tôi muốn biết trải nghiệm của bạn với API Truy cập hệ thống tệp.
Cho chúng tôi biết về thiết kế API
Có điều gì về API không hoạt động như bạn mong đợi không? Hoặc có phương thức nào bị thiếu hoặc thuộc tính nào bạn cần để triển khai ý tưởng của mình? Có câu hỏi hoặc nhận xét về bảo mật mẫu?
- Báo cáo vấn đề về thông số kỹ thuật trên kho lưu trữ GitHub về Quyền truy cập hệ thống tệp WICG hoặc chia sẻ ý kiến của bạn cho vấn đề hiện tại.
Bạn gặp vấn đề trong quá trình triển khai?
Bạn có phát hiện lỗi trong quá trình triển khai Chrome không? Hay cách triển khai có khác với thông số kỹ thuật không?
- Báo cáo lỗi tại https://new.crbug.com. Hãy nhớ cung cấp càng nhiều thông tin chi tiết càng tốt,
hướng dẫn tái tạo và đặt Thành phần thành
Blink>Storage>FileSystem
. Glitch rất hữu ích khi chia sẻ các bản trình bày nhanh.
Dự định sử dụng API?
Bạn đang định sử dụng API Truy cập hệ thống tệp trên trang web của mình? Sự hỗ trợ công khai của bạn giúp chúng tôi sắp xếp thứ tự ưu tiên đồng thời cho thấy các nhà cung cấp trình duyệt khác tầm quan trọng của việc hỗ trợ các nhà cung cấp này.
- Chia sẻ cách bạn dự định sử dụng tài khoản này trên chuỗi bài thuyết trình về WICG.
- Gửi một bài đăng đến @ChromiumDev kèm theo hashtag
#FileSystemAccess
và cho chúng tôi biết bạn đang sử dụng ở đâu và như thế nào.
Các đường liên kết hữu ích
- Thông báo giải thích công khai
- Thông số kỹ thuật về Quyền truy cập hệ thống tệp và Quy cách tệp
- Theo dõi lỗi
- Mục ChromeStatus.com
- Định nghĩa TypeScript
- API Truy cập hệ thống tệp – Mô hình bảo mật Chromium
- Thành phần nháy:
Blink>Storage>FileSystem
Xác nhận
Thông số API Truy cập hệ thống tệp được viết bởi Marijn Kruisselbrink.