استاندارد سیستم فایل، یک سیستم فایل خصوصی مبدا (OPFS) را به عنوان یک نقطه پایانی ذخیرهسازی که برای مبدا صفحه خصوصی است و برای کاربر قابل مشاهده نیست، معرفی میکند که دسترسی اختیاری به نوع خاصی از فایل را فراهم میکند که برای عملکرد بسیار بهینه شده است.
پشتیبانی مرورگر
سیستم فایل خصوصی مبدا توسط مرورگرهای مدرن پشتیبانی میشود و توسط گروه کاری فناوری کاربرد فرامتن وب ( WHATWG ) در استاندارد زندگی سیستم فایل استانداردسازی شده است.
انگیزه
وقتی به فایلهای روی رایانهتان فکر میکنید، احتمالاً سلسله مراتب فایلها به ذهنتان خطور میکند: فایلهایی که در پوشههایی سازماندهی شدهاند که میتوانید با استفاده از فایل اکسپلورر سیستم عامل خود آنها را بررسی کنید. به عنوان مثال، در ویندوز، برای کاربری به نام تام، لیست کارهای او ممکن است در C:\Users\Tom\Documents\ToDo.txt قرار داشته باشد. در این مثال، ToDo.txt نام فایل و Users ، Tom و Documents نام پوشهها هستند. `C:` در ویندوز نشان دهنده دایرکتوری ریشه درایو است.
روش معمول کار با فایلها در وب
برای ویرایش لیست کارها در یک برنامه وب، روند معمول به این صورت است:
- کاربر فایل را روی سرور آپلود میکند یا آن را با استفاده از
<input type="file">روی کلاینت باز میکند . - کاربر تغییرات مورد نظر خود را اعمال میکند و سپس فایل حاصل را با یک
<a download="ToDo.txt>download="ToDo.txt" تزریقشده که شما بهطور برنامهنویسیشده با جاوااسکریپتclick()، دانلود میکند . - برای باز کردن پوشهها، از یک ویژگی خاص در
<input type="file" webkitdirectory>استفاده میکنید که علیرغم نام اختصاصیاش، عملاً توسط مرورگرهای جهانی پشتیبانی میشود.
روش مدرن کار با فایلها در وب
این جریان، نمایانگر نحوهی تفکر کاربران در مورد ویرایش فایلها نیست و به این معنی است که کاربران در نهایت کپیهای دانلود شده از فایلهای ورودی خود را دریافت میکنند. بنابراین، رابط برنامهنویسی کاربردی دسترسی به سیستم فایل (File System Access API) سه روش انتخابگر (picker method) را معرفی کرد:
آنها یک جریان را به شرح زیر فعال میکنند:
-
ToDo.txtبا استفاده ازshowOpenFilePicker()باز کنید و یک شیءFileSystemFileHandleدریافت کنید. - از شیء
FileSystemFileHandle، با فراخوانی متدgetFile()از دسته فایل، یکFileدریافت کنید. - فایل را تغییر دهید، سپس
requestPermission({mode: 'readwrite'})را روی هندل فراخوانی کنید. - اگر کاربر درخواست مجوز را پذیرفت، تغییرات را دوباره در فایل اصلی ذخیره کنید.
- روش دیگر، فراخوانی
showSaveFilePicker()و اجازه دادن به کاربر برای انتخاب یک فایل جدید است. (اگر کاربر فایلی را که قبلاً باز شده انتخاب کند، محتوای آن بازنویسی میشود.) برای ذخیرههای مکرر، میتوانید دستگیره فایل را نگه دارید، بنابراین لازم نیست دوباره کادر ذخیره فایل را نمایش دهید.
محدودیتهای کار با فایلها در وب
فایلها و پوشههایی که با این روشها قابل دسترسی هستند، در چیزی قرار دارند که میتوان آن را سیستم فایل قابل مشاهده توسط کاربر نامید. فایلهای ذخیره شده از وب، و به طور خاص فایلهای اجرایی، با علامت وب مشخص میشوند، بنابراین سیستم عامل میتواند قبل از اجرای یک فایل بالقوه خطرناک، یک هشدار اضافی نشان دهد. به عنوان یک ویژگی امنیتی اضافی، فایلهای به دست آمده از وب نیز توسط مرور ایمن محافظت میشوند، که برای سادگی و در چارچوب این سند، میتوانید آن را به عنوان یک اسکن ویروس مبتنی بر ابر در نظر بگیرید. وقتی با استفاده از API دسترسی به سیستم فایل، دادهها را در یک فایل مینویسید، نوشتهها در جای خود نیستند، بلکه از یک فایل موقت استفاده میکنند. خود فایل تغییر نمیکند مگر اینکه از همه این بررسیهای امنیتی عبور کند.
این کار، عملیات فایل را نسبتاً کند میکند، با وجود اینکه در صورت امکان، مثلاً در macOS ، بهبودهایی اعمال شده است. هنوز هم هر فراخوانی write() مستقل است، بنابراین در پشت صحنه فایل را باز میکند، به دنبال آفست داده شده میگردد و در نهایت دادهها را مینویسد.
فایلها به عنوان پایه و اساس پردازش
در عین حال، فایلها راهی عالی برای ثبت دادهها هستند. به عنوان مثال، SQLite کل پایگاههای داده را در یک فایل واحد ذخیره میکند. مثال دیگر mipmapهایی هستند که در پردازش تصویر استفاده میشوند. Mipmapها توالیهای از پیش محاسبه شده و بهینه شدهای از تصاویر هستند که هر کدام نمایشی با وضوح به تدریج پایینتر از قبلی هستند که باعث میشود بسیاری از عملیات مانند بزرگنمایی سریعتر انجام شوند. بنابراین چگونه برنامههای وب میتوانند از مزایای فایلها بهرهمند شوند، اما بدون هزینههای عملکرد پردازش فایل مبتنی بر وب؟ پاسخ، سیستم فایل خصوصی مبدا است.
سیستم فایل قابل مشاهده توسط کاربر در مقابل سیستم فایل خصوصی مبدا
برخلاف سیستم فایل قابل مشاهده توسط کاربر که با استفاده از فایل اکسپلورر سیستم عامل مرور میشود و فایلها و پوشههایی دارد که میتوانید آنها را بخوانید، بنویسید، جابجا کنید و تغییر نام دهید، سیستم فایل خصوصی origin برای مشاهده توسط کاربران در نظر گرفته نشده است. فایلها و پوشههای موجود در سیستم فایل خصوصی origin، همانطور که از نامش پیداست، خصوصی هستند و به طور دقیقتر، برای مبدا یک سایت خصوصی هستند. با تایپ کردن location.origin در کنسول DevTools، مبدا یک صفحه را کشف کنید. به عنوان مثال، مبدا صفحه https://developer.chrome.com/articles/ ، https://developer.chrome.com است. برای اطلاعات بیشتر در مورد نظریه مبدا، به درک "same-site" و "same-origin" مراجعه کنید.
تمام صفحاتی که از یک مبدا مشترک هستند میتوانند دادههای سیستم فایل خصوصی مبدا یکسانی را ببینند، بنابراین https://developer.chrome.com/docs/extensions/mv3/getstarted/extensions-101/ میتوانند همان جزئیات مثال قبلی را ببینند. هر مبدا، سیستم فایل خصوصی مبدا مستقل خود را دارد، به این معنی که سیستم فایل خصوصی مبدا https://developer.chrome.com کاملاً متفاوت از مثلاً https://web.dev است. در ویندوز، دایرکتوری ریشه سیستم فایل قابل مشاهده توسط کاربر C:\\ است.
معادل آن برای سیستم فایل خصوصی مبدا، یک دایرکتوری ریشه خالی اولیه به ازای هر مبدا است که با فراخوانی متد غیرهمزمان navigator.storage.getDirectory() قابل دسترسی است.
برای مقایسه سیستم فایل قابل مشاهده توسط کاربر و سیستم فایل خصوصی مبدا، به نمودار زیر مراجعه کنید. نمودار نشان میدهد که به غیر از دایرکتوری ریشه، همه چیز دیگر از نظر مفهومی یکسان است، با سلسله مراتبی از فایلها و پوشهها برای سازماندهی و مرتبسازی در صورت نیاز برای دادهها و نیازهای ذخیرهسازی شما.

navigator.storage.getDirectory است. مشخصات سیستم فایل خصوصی مبدا
درست مانند سایر مکانیسمهای ذخیرهسازی در مرورگر (به عنوان مثال، localStorage یا IndexedDB )، سیستم فایل خصوصی origin تابع محدودیتهای سهمیه مرورگر است. هنگامی که یک کاربر تمام دادههای مرور یا تمام دادههای سایت را پاک میکند، سیستم فایل خصوصی origin نیز حذف خواهد شد.
تابع navigator.storage.estimate() را فراخوانی کنید و در شیء پاسخ حاصل، ورودی usage را مشاهده کنید تا ببینید برنامه شما در حال حاضر چقدر فضای ذخیرهسازی مصرف میکند، که بر اساس مکانیزم ذخیرهسازی در شیء usageDetails ، جایی که میخواهید به طور خاص ورودی fileSystem را بررسی کنید، تفکیک شده است. از آنجایی که سیستم فایل خصوصی origin برای کاربر قابل مشاهده نیست، هیچ درخواست مجوز و بررسی مرور ایمن وجود ندارد.
دسترسی به دایرکتوری ریشه
برای دسترسی به دایرکتوری ریشه، دستور زیر را اجرا کنید. در نهایت یک دایرکتوری خالی، یا به طور خاصتر، یک FileSystemDirectoryHandle ، خواهید داشت.
const opfsRoot = await navigator.storage.getDirectory();
// A FileSystemDirectoryHandle whose type is "directory"
// and whose name is "".
console.log(opfsRoot);
نخ اصلی یا وب ورکر
دو روش برای استفاده از سیستم فایل خصوصی مبدا وجود دارد: در نخ اصلی یا در یک Web Worker . Web Workerها نمیتوانند نخ اصلی را مسدود کنند، به این معنی که در این زمینه APIها میتوانند همزمان باشند، الگویی که معمولاً در نخ اصلی مجاز نیست. APIهای همزمان میتوانند سریعتر باشند زیرا از سروکار داشتن با promiseها اجتناب میکنند و عملیات فایل معمولاً در زبانهایی مانند C که میتوانند به WebAssembly کامپایل شوند، همزمان هستند.
// This is synchronous C code.
FILE *f;
f = fopen("example.txt", "w+");
fputs("Some text\n", f);
fclose(f);
اگر به سریعترین عملیات ممکن روی فایلها نیاز دارید، یا با WebAssembly سر و کار دارید، به بخش «استفاده از سیستم فایل خصوصی مبدا در یک Web Worker» بروید.
از سیستم فایل خصوصی origin در نخ اصلی استفاده کنید
ایجاد فایلها و پوشههای جدید
وقتی یک پوشه ریشه ایجاد کردید، به ترتیب با استفاده از متدهای getFileHandle() و getDirectoryHandle() فایلها و پوشهها را ایجاد کنید. با ارسال {create: true} ، اگر فایل یا پوشه وجود نداشته باشد، ایجاد میشود. با فراخوانی این توابع و با استفاده از یک دایرکتوری تازه ایجاد شده به عنوان نقطه شروع، سلسله مراتبی از فایلها ایجاد کنید.
const fileHandle = await opfsRoot
.getFileHandle('my first file', {create: true});
const directoryHandle = await opfsRoot
.getDirectoryHandle('my first folder', {create: true});
const nestedFileHandle = await directoryHandle
.getFileHandle('my first nested file', {create: true});
const nestedDirectoryHandle = await directoryHandle
.getDirectoryHandle('my first nested folder', {create: true});

دسترسی به فایلها و پوشههای موجود
اگر نام فایلها و پوشههای ایجاد شده قبلی را میدانید، با فراخوانی متدهای getFileHandle() یا getDirectoryHandle() و ارسال نام فایل یا پوشه، به آنها دسترسی پیدا کنید.
const existingFileHandle = await opfsRoot.getFileHandle('my first file');
const existingDirectoryHandle = await opfsRoot
.getDirectoryHandle('my first folder');
دریافت فایل مرتبط با یک شناسه فایل برای خواندن
یک FileSystemFileHandle نشان دهنده یک فایل در سیستم فایل است. برای به دست آوردن File مرتبط، از متد getFile() استفاده کنید. یک شیء File نوع خاصی از Blob است و میتواند در هر زمینهای که یک Blob میتواند استفاده شود، مورد استفاده قرار گیرد.
به طور خاص، FileReader ، URL.createObjectURL() ، createImageBitmap() و XMLHttpRequest.send() هر دو نوع Blobs و Files میپذیرند. دریافت یک File از FileSystemFileHandle دادهها را "آزاد" میکند، بنابراین میتوانید به آن دسترسی داشته باشید و آن را در دسترس سیستم فایل قابل مشاهده توسط کاربر قرار دهید.
const file = await fileHandle.getFile();
console.log(await file.text());
نوشتن در فایل با استفاده از استریمینگ
دادهها را با فراخوانی تابع createWritable() به یک فایل استریم کنید. این تابع یک FileSystemWritableFileStream ایجاد میکند که سپس میتوانید محتویات آن را در آن write() و در نهایت، باید استریم را close() .
const contents = 'Some text';
// Get a writable stream.
const writable = await fileHandle.createWritable();
// Write the contents of the file to the stream.
await writable.write(contents);
// Close the stream, which persists the contents.
await writable.close();
حذف فایلها و پوشهها
فایلها و پوشهها را با فراخوانی متد remove() مربوط به فایل یا دایرکتوری مربوطه حذف کنید. برای حذف یک پوشه به همراه تمام زیرپوشههای آن، گزینه {recursive: true} را به آن ارسال کنید.
await fileHandle.remove();
await directoryHandle.remove({recursive: true});
به عنوان یک جایگزین، اگر نام فایل یا پوشهای که قرار است حذف شود را در یک دایرکتوری میدانید، از متد removeEntry() استفاده کنید.
directoryHandle.removeEntry('my first nested file');
جابجایی و تغییر نام فایلها و پوشهها
تغییر نام و جابجایی فایلها و پوشهها با استفاده از متد move() . جابجایی و تغییر نام میتوانند با هم یا به صورت جداگانه انجام شوند.
// Rename a file.
await fileHandle.move('my first renamed file');
// Move a file to another directory.
await fileHandle.move(nestedDirectoryHandle);
// Move a file to another directory and rename it.
await fileHandle
.move(nestedDirectoryHandle, 'my first renamed and now nested file');
مسیر یک فایل یا پوشه را پیدا کنید
برای فهمیدن اینکه یک فایل یا پوشهی مشخص نسبت به یک دایرکتوری مرجع در کجا قرار دارد، از متد resolve() استفاده کنید و یک FileSystemHandle به عنوان آرگومان به آن ارسال کنید. برای به دست آوردن مسیر کامل یک فایل یا پوشه در سیستم فایل خصوصی مبدا، از دایرکتوری ریشه به عنوان دایرکتوری مرجع که با استفاده از navigator.storage.getDirectory() به دست میآید، استفاده کنید.
const relativePath = await opfsRoot.resolve(nestedDirectoryHandle);
// `relativePath` is `['my first folder', 'my first nested folder']`.
بررسی کنید که آیا دو دسته فایل یا پوشه به یک فایل یا پوشه اشاره میکنند یا خیر
گاهی اوقات شما دو هندل دارید و نمیدانید که آیا آنها به یک فایل یا پوشه اشاره میکنند یا خیر. برای بررسی این موضوع، از متد isSameEntry() استفاده کنید.
fileHandle.isSameEntry(nestedFileHandle);
// Returns `false`.
فهرست کردن محتویات یک پوشه
FileSystemDirectoryHandle یک تکرارکنندهی ناهمزمان است که شما با استفاده از یک حلقهی for await... of روی آن تکرار میکنید. به عنوان یک تکرارکنندهی ناهمزمان، از متدهای entries() ، values() و keys() نیز پشتیبانی میکند که میتوانید بسته به اطلاعات مورد نیاز خود، از بین آنها انتخاب کنید:
for await (let [name, handle] of directoryHandle) {}
for await (let [name, handle] of directoryHandle.entries()) {}
for await (let handle of directoryHandle.values()) {}
for await (let name of directoryHandle.keys()) {}
به صورت بازگشتی محتویات یک پوشه و تمام زیرپوشهها را لیست میکند
سر و کار داشتن با حلقهها و توابع ناهمزمان که با بازگشت جفت میشوند، به راحتی میتواند اشتباه باشد. تابع زیر میتواند به عنوان نقطه شروعی برای فهرست کردن محتویات یک پوشه و تمام زیرپوشههای آن، از جمله تمام فایلها و اندازههای آنها، عمل کند. اگر به اندازه فایلها نیازی ندارید، میتوانید تابع را با این روش ساده کنید، جایی که میگوید directoryEntryPromises.push ، به جای اینکه promise مربوط به handle.getFile() را وارد کند، مستقیماً handle را وارد میکند.
const getDirectoryEntriesRecursive = async (
directoryHandle,
relativePath = '.',
) => {
const fileHandles = [];
const directoryHandles = [];
const entries = {};
// Get an iterator of the files and folders in the directory.
const directoryIterator = directoryHandle.values();
const directoryEntryPromises = [];
for await (const handle of directoryIterator) {
const nestedPath = `${relativePath}/${handle.name}`;
if (handle.kind === 'file') {
fileHandles.push({ handle, nestedPath });
directoryEntryPromises.push(
handle.getFile().then((file) => {
return {
name: handle.name,
kind: handle.kind,
size: file.size,
type: file.type,
lastModified: file.lastModified,
relativePath: nestedPath,
handle
};
}),
);
} else if (handle.kind === 'directory') {
directoryHandles.push({ handle, nestedPath });
directoryEntryPromises.push(
(async () => {
return {
name: handle.name,
kind: handle.kind,
relativePath: nestedPath,
entries:
await getDirectoryEntriesRecursive(handle, nestedPath),
handle,
};
})(),
);
}
}
const directoryEntries = await Promise.all(directoryEntryPromises);
directoryEntries.forEach((directoryEntry) => {
entries[directoryEntry.name] = directoryEntry;
});
return entries;
};
استفاده از سیستم فایل خصوصی origin در یک Web Worker
همانطور که قبلاً اشاره شد، Web Workerها نمیتوانند thread اصلی را مسدود کنند، به همین دلیل است که در این زمینه، متدهای همگام مجاز هستند.
دریافت یک دستگیره دسترسی همزمان
نقطه ورود به سریعترین عملیات ممکن روی فایل، یک FileSystemSyncAccessHandle است که با فراخوانی تابع createSyncAccessHandle() از یک FileSystemFileHandle معمولی به دست میآید.
const fileHandle = await opfsRoot
.getFileHandle('my highspeed file.txt', {create: true});
const syncAccessHandle = await fileHandle.createSyncAccessHandle();
متدهای فایل همزمان درجا
زمانی که یک دسته دسترسی همگام داشته باشید، به متدهای فایل سریع و درجا که همگی همگام هستند، دسترسی پیدا میکنید.
-
getSize(): اندازه فایل را بر حسب بایت برمیگرداند. -
write(): محتوای یک بافر را در فایل، به صورت اختیاری با یک آفست مشخص، مینویسد و تعداد بایتهای نوشته شده را برمیگرداند. بررسی تعداد بایتهای نوشته شدهی برگردانده شده به فراخوانیکنندگان این امکان را میدهد که خطاها و نوشتنهای جزئی را شناسایی و مدیریت کنند. -
read(): محتویات فایل را در یک بافر، به صورت اختیاری با یک آفست مشخص، میخواند. -
truncate(): اندازه فایل را به اندازه داده شده تغییر میدهد. -
flush(): تضمین میکند که محتویات فایل شامل تمام تغییراتی باشد که از طریقwrite() انجام شدهاند. -
close()، دستگیره دسترسی را میبندد.
در اینجا مثالی آورده شده است که از هر روش استفاده میکند.
const opfsRoot = await navigator.storage.getDirectory();
const fileHandle = await opfsRoot.getFileHandle('fast', {create: true});
const accessHandle = await fileHandle.createSyncAccessHandle();
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
// Initialize this variable for the size of the file.
let size;
// The current size of the file, initially `0`.
size = accessHandle.getSize();
// Encode content to write to the file.
const content = textEncoder.encode('Some text');
// Write the content at the beginning of the file.
accessHandle.write(content, {at: size});
// Flush the changes.
accessHandle.flush();
// The current size of the file, now `9` (the length of "Some text").
size = accessHandle.getSize();
// Encode more content to write to the file.
const moreContent = textEncoder.encode('More content');
// Write the content at the end of the file.
accessHandle.write(moreContent, {at: size});
// Flush the changes.
accessHandle.flush();
// The current size of the file, now `21` (the length of
// "Some textMore content").
size = accessHandle.getSize();
// Prepare a data view of the length of the file.
const dataView = new DataView(new ArrayBuffer(size));
// Read the entire file into the data view.
accessHandle.read(dataView);
// Logs `"Some textMore content"`.
console.log(textDecoder.decode(dataView));
// Read starting at offset 9 into the data view.
accessHandle.read(dataView, {at: 9});
// Logs `"More content"`.
console.log(textDecoder.decode(dataView));
// Truncate the file after 4 bytes.
accessHandle.truncate(4);
کپی کردن یک فایل از سیستم فایل خصوصی مبدا به سیستم فایل قابل مشاهده توسط کاربر
همانطور که در بالا ذکر شد، انتقال فایلها از سیستم فایل خصوصی مبدا به سیستم فایل قابل مشاهده توسط کاربر امکانپذیر نیست، اما میتوانید فایلها را کپی کنید. از آنجایی که showSaveFilePicker() فقط در thread اصلی نمایش داده میشود، اما در thread Worker نمایش داده نمیشود، حتماً کد را در آنجا اجرا کنید.
// On the main thread, not in the Worker. This assumes
// `fileHandle` is the `FileSystemFileHandle` you obtained
// the `FileSystemSyncAccessHandle` from in the Worker
// thread. Be sure to close the file in the Worker thread first.
const fileHandle = await opfsRoot.getFileHandle('fast');
try {
// Obtain a file handle to a new file in the user-visible file system
// with the same name as the file in the origin private file system.
const saveHandle = await showSaveFilePicker({
suggestedName: fileHandle.name || ''
});
const writable = await saveHandle.createWritable();
await writable.write(await fileHandle.getFile());
await writable.close();
} catch (err) {
console.error(err.name, err.message);
}
اشکالزدایی سیستم فایل خصوصی مبدا
تا زمانی که پشتیبانی از DevTools داخلی اضافه شود (به crbug/1284595 مراجعه کنید)، از افزونه OPFS Explorer کروم برای اشکالزدایی سیستم فایل خصوصی origin استفاده کنید. ضمناً، تصویر صفحه از بخش ایجاد فایلها و پوشههای جدید مستقیماً از این افزونه گرفته شده است.

پس از نصب افزونه، Chrome DevTools را باز کنید، تب OPFS Explorer را انتخاب کنید، و سپس آماده بررسی سلسله مراتب فایلها هستید. با کلیک روی نام فایل، فایلها را از سیستم فایل خصوصی مبدا به سیستم فایل قابل مشاهده توسط کاربر ذخیره کنید و با کلیک روی نماد سطل زباله، فایلها و پوشهها را حذف کنید.
نسخه آزمایشی
سیستم فایل خصوصی origin را در عمل (اگر افزونه OPFS Explorer را نصب کنید) در یک نسخه آزمایشی که از آن به عنوان یک backend برای یک پایگاه داده SQLite کامپایل شده در WebAssembly استفاده میکند، مشاهده کنید. حتماً کد منبع را در GitHub بررسی کنید. توجه داشته باشید که نسخه تعبیه شده از backend سیستم فایل خصوصی origin استفاده نمیکند (زیرا iframe کراس-اوریجین است)، اما وقتی نسخه آزمایشی را در یک برگه جداگانه باز میکنید، این کار را انجام میدهد.
نتیجهگیری
سیستم فایل خصوصی origin، همانطور که توسط WHATWG مشخص شده است، نحوه استفاده و تعامل ما با فایلها در وب را شکل داده است. این سیستم موارد استفاده جدیدی را فراهم کرده است که با سیستم فایل قابل مشاهده توسط کاربر غیرممکن بود. همه فروشندگان اصلی مرورگر - اپل، موزیلا و گوگل - با این سیستم موافق هستند و دیدگاه مشترکی دارند. توسعه سیستم فایل خصوصی origin یک تلاش مشترک است و بازخورد توسعهدهندگان و کاربران برای پیشرفت آن ضروری است.
همچنان که ما به اصلاح و بهبود استاندارد ادامه میدهیم، از بازخورد در مورد مخزن whatwg/fs به شکل مشکلات یا درخواستهای دریافت (Pull Requests) استقبال میشود.
لینکهای مرتبط
- مشخصات استاندارد سیستم فایل
- مخزن استاندارد سیستم فایل
- پست API سیستم فایل با Origin Private File System WebKit
- افزونهی اکسپلورر OPFS
تقدیرنامهها
این سند توسط آستین سالی ، اتین نوئل و راشل اندرو بررسی شده است.