File System Access API: דרך פשוטה יותר לגשת לקבצים מקומיים

File System Access API מאפשר לאפליקציות אינטרנט לקרוא או לשמור שינויים ישירות בקבצים ובתיקיות במכשיר של המשתמש.

מה זה File System Access API?

File System Access API מאפשר למפתחים ליצור אפליקציות אינטרנט מתקדמות שמקיימות אינטראקציה עם קבצים במכשיר המקומי של המשתמש, כמו סביבות פיתוח משולבות (IDE), עורכים של תמונות וסרטונים, כלים לעריכת טקסט ועוד. אחרי משתמש מעניק לאפליקציית אינטרנט גישה, ה-API הזה מאפשר לו לקרוא או לשמור שינויים ישירות בקבצים תיקיות במכשיר של המשתמש. מעבר לקריאה ולכתיבה של קבצים, File System Access API מספק יכולת לפתוח ספרייה ולספר את התוכן שלה.

אם כבר עבדת עם קריאה וכתיבה של קבצים בעבר, חלק גדול מהתוכן שאני עומד לשתף שמוכר לך. אני ממליץ לך לקרוא אותו בכל זאת, כי לא כל המערכות זהות.

File System Access API נתמך ברוב דפדפני Chromium ב- Windows, macOS, ChromeOS ו-Linux. יוצא הדופן היחיד הוא Brave, בכל מקום שבו זמין כרגע רק מאחורי דגל. אנחנו עובדים על תמיכה ב-Android בהקשר של crbug.com/1011535.

שימוש ב-File System Access API

כדי להראות את העוצמה והתועלת של File System Access API, כתבתי טקסט לקובץ יחיד Editor. הוא מאפשר לפתוח קובץ טקסט, לערוך אותו, לשמור את השינויים בחזרה בדיסק או להפעיל קובץ חדש ושומרים את השינויים בדיסק. זה לא משהו מתוחכם, אבל הוא מספק מספיק כדי לעזור לך להבין את המושגים.

תמיכה בדפדפנים

תמיכה בדפדפן

  • Chrome: 86.
  • קצה: 86.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

זיהוי תכונות

כדי לבדוק אם File System Access API נתמך, צריך לבדוק אם שיטת הבורר שמעניינות אתכם.

if ('showOpenFilePicker' in self) {
  // The `showOpenFilePicker()` method of the File System Access API is supported.
}

רוצה לנסות?

אפשר לראות את File System Access API בפעולה ההדגמה של הכלי לעריכת טקסט.

קריאת קובץ ממערכת הקבצים המקומית

התרחיש לדוגמה הראשון שבו אני רוצה להתמודד הוא לבקש מהמשתמש לבחור קובץ ואז לפתוח ולקרוא אותו מהדיסק.

צריך לבקש מהמשתמש לבחור קובץ לקריאה

נקודת הכניסה ל-File System Access API היא window.showOpenFilePicker() כשהקריאה מופעלת, מוצגת תיבת דו-שיח לבחירת קבצים, ומבקש מהמשתמש לבחור קובץ. אחרי שהם בוחרים קובץ, ה-API מחזיר מערך של קבצים לכינויים. פרמטר אופציונלי של options מאפשר להשפיע על ההתנהגות של הכלי לבחירת קבצים, למשל למשל, באמצעות מתן אפשרות למשתמש לבחור מספר קבצים, ספריות או סוגי קבצים שונים. אם לא צוינו אפשרויות כלשהן, הכלי לבחירת קבצים מאפשר למשתמש לבחור קובץ יחיד. הדבר מושלם לעריכת טקסט.

בדומה להרבה ממשקי API חזקים אחרים, הקריאה showOpenFilePicker() צריכה להתבצע מאובטח הקשר, והקריאה צריכה להתבצע מתוך תנועת משתמש.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  // Destructure the one-element array.
  [fileHandle] = await window.showOpenFilePicker();
  // Do something with the file handle.
});

אחרי שהמשתמש בוחר קובץ, הפונקציה showOpenFilePicker() מחזירה מערך של כינויים, במקרה הזה מערך של רכיב אחד עם FileSystemFileHandle אחד שמכיל את המאפיינים וגם את השיטות הנדרשות כדי לבצע פעולות בקובץ.

מומלץ לשמור הפניה לכינוי של הקובץ כדי שתוכלו להשתמש בו מאוחר יותר. הוא יהיה שנדרשים כדי לשמור את השינויים בקובץ או כדי לבצע פעולות אחרות בקובץ.

קריאת קובץ ממערכת הקבצים

עכשיו, כשיש לכם כינוי לקובץ, תוכלו לקבל את המאפיינים של הקובץ או לגשת לקובץ עצמו. בינתיים, אקרא את התוכן שלו. התקשרות אל handle.getFile() מחזירה File שמכיל blob. כדי לקבל את הנתונים מה-blob, צריך להפעיל אחד מהם methods, (slice(), stream(), text(), או arrayBuffer()).

const file = await fileHandle.getFile();
const contents = await file.text();

ניתן לקרוא את האובייקט File שהוחזר על ידי FileSystemFileHandle.getFile() רק כל עוד קובץ הבסיס בדיסק לא השתנה. אם משנים את הקובץ בדיסק, האובייקט File הופך לא ניתן לקרוא, וצריך להפעיל את הפקודה getFile() שוב כדי לקבל אובייקט File חדש לקריאת השינוי .

סיכום של כל המידע

כשמשתמשים ילחצו על הלחצן פתיחה, יוצג בדפדפן כלי לבחירת קבצים. אחרי שהם בוחרים קובץ, האפליקציה קוראת את התוכן ומכניסה אותו ל-<textarea>.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  [fileHandle] = await window.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.text();
  textArea.value = contents;
});

כתיבת הקובץ במערכת הקבצים המקומית

בכלי לעריכת טקסט יש שתי דרכים לשמור קובץ: שמירה ושמירה בשם. שמירה כותב את השינויים בקובץ המקורי באמצעות הכינוי של הקובץ שאוחזר קודם. אבל שמור מכיוון ש-יוצר קובץ חדש, נדרש כינוי חדש לקובץ.

יצירת קובץ חדש

כדי לשמור קובץ, קוראים לפונקציה showSaveFilePicker(), שם מוצג הכלי לבחירת קבצים. ב"שמירה" כך שהמשתמש יכול לבחור קובץ חדש שבו הוא רוצה לשמור. לטקסט עריכה, רציתי גם להוסיף באופן אוטומטי תוסף .txt, לכן סיפקתי עוד כמה תוספים .

async function getNewFileHandle() {
  const options = {
    types: [
      {
        description: 'Text Files',
        accept: {
          'text/plain': ['.txt'],
        },
      },
    ],
  };
  const handle = await window.showSaveFilePicker(options);
  return handle;
}

שמירת שינויים בדיסק

אפשר למצוא את כל הקוד לשמירת שינויים בקובץ בהדגמה של עורך הטקסט: GitHub. האינטראקציות העיקריות של מערכת הקבצים הן fs-helpers.js במקרה הפשוט ביותר, התהליך נראה כמו הקוד הבא. אני אעבור על כל שלב ואסביר אותו.

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

כתיבת נתונים לדיסק משתמשת באובייקט FileSystemWritableFileStream, מחלקה משנית מתוך WritableStream. כדי ליצור את השידור, צריך להפעיל את createWritable() בקובץ טיפול באובייקט. כשמתבצעת קריאה אל createWritable(), הדפדפן בודק קודם אם המשתמש העניק הרשאת כתיבה לקובץ. אם לא ניתנה הרשאת כתיבה, תוצג בדפדפן את המשתמש כדי לקבל הרשאה. אם לא ניתנה הרשאה, createWritable() יקפיץ DOMException, והאפליקציה לא תוכל לכתוב לקובץ. בכלי לעריכת טקסט, DOMException אובייקטים מטופלים באמצעות method saveFile().

השיטה write() מקבלת מחרוזת, והיא נחוצה לכלי לעריכת טקסט. אבל אפשר גם לקחת BufferSource או Blob. לדוגמה, אפשר לנתב זרם ישירות אל זה:

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.
}

אפשר גם seek() או truncate() בתוך השידור כדי לעדכן את בקובץ במיקום מסוים, או לשנות את גודל הקובץ.

ציון שם קובץ מוצע וספריית התחלה

במקרים רבים, כדאי שהאפליקציה תציע שם קובץ או מיקום שמוגדרים כברירת מחדל. לדוגמה, טקסט יכול להיות שהעורך ירצה להציע שם קובץ ברירת מחדל של Untitled Text.txt במקום Untitled. שלך יכול לעשות זאת על ידי העברת מאפיין suggestedName כחלק מהאפשרויות של showSaveFilePicker.

const fileHandle = await self.showSaveFilePicker({
  suggestedName: 'Untitled Text.txt',
  types: [{
    description: 'Text documents',
    accept: {
      'text/plain': ['.txt'],
    },
  }],
});

אותו עיקרון חל על ספריית ההתחלה שמוגדרת כברירת מחדל. אם אתם מפתחים כלי לעריכת טקסט, מומלץ לפתוח את תיבת הדו-שיח לשמירת קובץ או לפתיחת קובץ בתיקיית ברירת המחדל documents, ואילו בשביל תמונה עורך, מומלץ להתחיל בתיקיית ברירת המחדל pictures. אפשר להציע התחלת ברירת מחדל הספרייה על ידי העברת מאפיין startIn אל showSaveFilePicker, showDirectoryPicker(), או showOpenFilePicker שיטות כאלה.

const fileHandle = await self.showOpenFilePicker({
  startIn: 'pictures'
});

רשימה של ספריות המערכת המוכרות:

  • desktop: ספריית שולחן העבודה של המשתמש, אם קיים כזה.
  • documents: ספרייה שבה בדרך כלל מאוחסנים מסמכים שנוצרו על ידי המשתמש.
  • downloads: הספרייה שבה מאוחסנים בדרך כלל הקבצים שהורדתם.
  • music: ספרייה שבה מאוחסנים בדרך כלל קובצי האודיו.
  • pictures: ספרייה שבה יאוחסנו בדרך כלל תמונות ותמונות סטילס אחרות.
  • videos: ספרייה שבה נשמרים בדרך כלל סרטונים או סרטים.

מלבד ספריות מערכת מוכרות, אפשר גם להעביר כינוי קיים של קובץ או ספרייה בתור ערך עבור startIn. תיבת הדו-שיח תיפתח באותה ספרייה.

// Assume `directoryHandle` is a handle to a previously opened directory.
const fileHandle = await self.showOpenFilePicker({
  startIn: directoryHandle
});

ציון המטרה של בוחרי קבצים שונים

לפעמים יש אפליקציות עם כלי בחירה שונים למטרות שונות. לדוגמה, טקסט עשיר עורך עשוי לאפשר למשתמש לפתוח קובצי טקסט, אך גם לייבא תמונות. כברירת מחדל, כל קובץ הבורר ייפתח במיקום האחרון שזוכר. אפשר לעקוף את הבעיה על ידי אחסון ערכים של id לכל סוג של בורר. אם צוין id, ההטמעה של הכלי לבחירת קבצים זוכרת הספרייה האחרונה שהייתה בשימוש עבור id.

const fileHandle1 = await self.showSaveFilePicker({
  id: 'openText',
});

const fileHandle2 = await self.showSaveFilePicker({
  id: 'importImage',
});

אחסון כינויי קבצים או נקודות אחיזה של ספריות ב-IndexedDB

הכינויים של הקבצים ונקודות האחיזה של הספריות ניתנים לסריאליזציה, כך שאפשר לשמור קובץ או מזהה ספרייה ב-IndexedDB, או קוראים לפונקציה postMessage() כדי לשלוח אותם בין אותה נקודה עליונה המקור.

שמירת כינויי קובץ או ספרייה ב-IndexedDB מאפשרת לך לאחסן מצבים, או לזכור אילו מהם קבצים או ספריות שעליהם עבד המשתמש. כך אפשר לשמור רשימה של האירועים שנפתחו לאחרונה או קבצים שנערכו, מציעים לפתוח מחדש את הקובץ האחרון כשהאפליקציה נפתחה, לשחזר את הפעולה הקודמת ועוד. בכלי לעריכת טקסט, אני מאחסנת רשימה של חמשת הקבצים האחרונים של המשתמש כך שאפשר לגשת שוב לקבצים האלה.

בדוגמת הקוד הבאה אפשר לראות אחסון ואחזור של מזהה קובץ ומזהה ספרייה. אפשר אפשר לראות את זה בפעולה ב-Glitch. (אני משתמש/ת ספריית idb-keyval לשם קיצור.)

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

כינויים והרשאות של קבצים או ספריות מאוחסנים

ההרשאות לא תמיד נשמרות בין סשנים, לכן צריך לבדוק אם המשתמש העניק/ה הרשאה לקובץ או לספרייה באמצעות queryPermission(). אם הם לא ענו, התקשרו requestPermission() כדי (לבקש אותה מחדש). הפעולה הזו עובדת באותו אופן עבור כינויים של קבצים וספריות. שלך צריך להריץ את הפקודה fileOrDirectoryHandle.requestPermission(descriptor) או fileOrDirectoryHandle.queryPermission(descriptor) בהתאמה.

בכלי לעריכת טקסט, יצרתי שיטה מסוג verifyPermission() שבודקת אם המשתמש כבר מעניק הרשאה, ובמקרה הצורך, שולח את הבקשה.

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;
}

על ידי בקשת הרשאת כתיבה עם בקשת הקריאה, הקטתי את מספר הבקשות להרשאות; המשתמש רואה הנחיה אחת כשפותחים את הקובץ, ומעניק הרשאה גם לקריאה וגם לכתיבה.

פתיחת ספרייה וספירת התוכן שלה

כדי לספור את כל הקבצים בספרייה, צריך להפעיל את הפונקציה showDirectoryPicker(). המשתמש/ת בוחר ספרייה בכלי בחירה, ואחר כך FileSystemDirectoryHandle מוחזרת, וכך תוכלו לספור את הקבצים של הספרייה ולגשת אליהם. כברירת מחדל, המערכת תקרא גישה לקבצים בספרייה. אבל אם אתם צריכים גישת כתיבה, תוכלו להעביר { mode: 'readwrite' } ל-method.

butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  for await (const entry of dirHandle.values()) {
    console.log(entry.kind, entry.name);
  }
});

אם בנוסף אתם צריכים לגשת לכל קובץ באמצעות getFile(), למשל, צריך להשיג את גודל הקבצים, אין להשתמש ב-await בכל תוצאה ברצף, אלא לעבד את כל הקבצים מקביל, למשל, באמצעות 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));
});

יצירה של קבצים ותיקיות בספרייה או גישה אליהם

מספרייה ניתן ליצור קבצים ותיקיות או לגשת אליהם באמצעות getFileHandle() או בהתאמה getDirectoryHandle() . על ידי העברת אובייקט options אופציונלי עם מפתח של create וערך בוליאני של true או false, יש לך אפשרות לקבוע אם צריך ליצור קובץ או תיקייה חדשים אם הם לא קיימים.

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

פתרון נתיב של פריט בספרייה

כשעובדים עם קבצים או תיקיות בספרייה, כדאי לזהות את נתיב הפריט שבהן מדובר. אפשר לעשות זאת באמצעות ה-method resolve(), עם השם המדויק. כדי לפתור את הבעיה, הפריט יכול להיות צאצא ישיר או עקיף של הספרייה.

// 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"]

מחיקת קבצים ותיקיות בספרייה

אם יש לכם גישה לספרייה, אתם יכולים למחוק את הקבצים והתיקיות שבתיקייה באמצעות removeEntry(). לתיקיות, המחיקה יכולה להיות רקורסיבית ולכלול את כל תיקיות המשנה והקבצים שבתוכה.

// Delete a file.
await directoryHandle.removeEntry('Abandoned Projects.txt');
// Recursively delete a folder.
await directoryHandle.removeEntry('Old Stuff', { recursive: true });

מחיקה ישירה של קובץ או תיקייה

אם יש לך גישה לכינוי של קובץ או ספרייה, יש להתקשר למספר remove() באמצעות FileSystemFileHandle או FileSystemDirectoryHandle כדי להסיר אותו.

// Delete a file.
await fileHandle.remove();
// Delete a directory.
await directoryHandle.remove();

שינוי שם והעברה של קבצים ותיקיות

אפשר לשנות את השם של קבצים ותיקיות או להעביר אותם למיקום חדש על ידי קריאה ל-move() ממשק FileSystemHandle. ל-FileSystemHandle יש את ממשקי הצאצא FileSystemFileHandle וגם FileSystemDirectoryHandle. השיטה move() מקבלת פרמטר אחד או שניים. הראשון יכול להיות מחרוזת עם השם החדש או FileSystemDirectoryHandle לתיקיית היעד. ב במקרה השני, הפרמטר השני האופציונלי הוא מחרוזת עם השם החדש, כך שהעברה ושינוי שם יכולים בשלב אחד.

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

שילוב גרירה ושחרור

ממשקים לגרירה ושחרור ב-HTML אפשר לאפליקציות אינטרנט לקבל גוררים קבצים ושוחררו בדף אינטרנט. במהלך פעולת גרירה ושחרור, מתבצע שיוך של פריטי ספרייה וקובץ שגוררים עם רשומות קבצים ורשומות בספרייה, בהתאמה. DataTransferItem.getAsFileSystemHandle() method מחזירה הבטחה עם אובייקט FileSystemFileHandle אם הפריט שגורר הוא קובץ, ופונקציית להבטיח עם אובייקט FileSystemDirectoryHandle אם הפריט שגורר הוא ספרייה. הדף העסקי הבא רואים את זה בפעולה. שים לב שממשק 'גרירה ושחרור' DataTransferItem.kind הוא "file" גם לקבצים וגם לספריות, ואילו FileSystemHandle.kind של File System Access API "file" לקבצים ו-"directory" לספריות.

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}`);
    }
  }
});

גישה למערכת הקבצים הפרטית המקורית

מערכת הקבצים הפרטית של המקור היא נקודת קצה לאחסון, שכפי שמרמז השם, היא פרטית מקור הדף. אמנם דפדפנים מיישמים את הקוד הזה בדרך כלל על ידי שמירת התוכן מקור של מערכת קבצים פרטית לדיסק במקום כלשהו, לא התכוון שהתוכן יהיה משתמש נגיש. באותו האופן, לא צריך לצפות שקבצים או ספריות עם שמות תואמים שמות של צאצאים של מערכת הקבצים הפרטית המקורית. למרות שהדפדפן עשוי להיראות קיימים קבצים באופן פנימי - כיוון שזוהי מערכת קבצים פרטית ממקור - הדפדפן עשוי לשמור ה"קבצים" האלה במסד נתונים או בכל מבנה נתונים אחר. בעיקרון, אם תשתמשו ב-API הזה, לא מצפה למצוא את הקבצים שנוצרו בהתאמה אחד לאחד במקום כלשהו בדיסק הקשיח. אפשר לפעול כרגיל ב- מערכת הקבצים הפרטיים המקורית, ברגע שיש לכם גישה לרמה הבסיסית (FileSystemDirectoryHandle).

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

תמיכה בדפדפן

  • Chrome: 86.
  • קצה: 86.
  • Firefox: 111.
  • Safari: 15.2.

מקור

גישה לקבצים עם אופטימיזציה לביצועים ממערכת הקבצים הפרטית המקורית

מערכת הקבצים הפרטית של המקור מספקת גישה אופציונלית לסוג קובץ מיוחד לבצע אופטימיזציה לביצועים, לדוגמה, באמצעות הצעת גישה במקום ובלעדית לכתיבה תוכן. ב-Chromium 102 ואילך, יש שיטה נוספת במערכת הקבצים הפרטית המקורית עבור פישוט הגישה לקבצים: createSyncAccessHandle() (לפעולות סינכרוניות של קריאה וכתיבה). היא נחשפת ב-FileSystemFileHandle, אבל רק ב: Web Workers (עובדים באינטרנט).

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

מילוי פוליגונים

לא ניתן לבצע פוליגונים באופן מלא ל-File System Access API.

  • ניתן להעריך את ה-method showOpenFilePicker() באמצעות רכיב <input type="file">.
  • אפשר לדמות את השיטה showSaveFilePicker() עם רכיב <a download="file_name">, גם אם הפעולה הזו מפעילה הורדה פרוגרמטית ולא מאפשרת להחליף קבצים קיימים.
  • ניתן לבצע אמולציה של השיטה showDirectoryPicker() בעזרת רכיב <input type="file" webkitdirectory>.

פיתחנו ספרייה בשם browser-fs-access שמתבססת על System Access API ככל האפשר, שעונה על האפשרויות הטובות ביותר הבאות בכל שאר במקרים שונים.

אבטחה והרשאות

צוות Chrome תכנן והטמיע את File System Access API באמצעות עקרונות הליבה מוגדרת בקטע שליטה בגישה לתכונות מתקדמות של פלטפורמת אינטרנט, כולל משתמשים שליטה, שקיפות וארגונומיה של משתמשים.

פתיחת קובץ או שמירת קובץ חדש

בוחר קבצים לפתיחת קובץ לקריאה
בוחר קבצים שמשמש לפתיחת קובץ קיים לקריאה.

כשפותחים קובץ, המשתמש מספק הרשאה לקרוא קובץ או ספרייה באמצעות בוחר הקבצים. ניתן להציג את בוחר הקבצים הפתוח רק באמצעות תנועת משתמש כאשר הוא מוצג מתוך קובץ מאובטח הקשר מסוים. אם המשתמשים ישנו את דעתם, הם יוכלו לבטל את הבחירה בקובץ ולאתר לא תהיה גישה לשום דבר. זו אותה התנהגות כמו רכיב <input type="file">.

בוחר קבצים לשמירת קובץ בדיסק.
בוחר קבצים שמשמש לשמירת קובץ בדיסק.

באותו האופן, כשאפליקציית אינטרנט מבקשת לשמור קובץ חדש, הדפדפן מציג את בוחר הקבצים לשמירת הקבצים, שמאפשרות למשתמש לציין את השם והמיקום של הקובץ החדש. כי הם שומרים קובץ חדש למכשיר (לעומת החלפה של קובץ קיים), הכלי לבחירת קבצים מעניק לאפליקציה הרשאה לכתוב לקובץ.

תיקיות מוגבלות

כדי להגן על המשתמשים והנתונים שלהם, הדפדפן עשוי להגביל את היכולת של המשתמש לשמור תיקיות, למשל, תיקיות ליבה של מערכת ההפעלה כמו Windows, התיקיות של ספריית macOS. במקרה כזה, הדפדפן מציג הודעה ומבקש מהמשתמש לבחור .

שינוי קובץ או ספרייה קיימים

אפליקציית אינטרנט לא יכולה לבצע שינויים בקובץ בכונן בלי לקבל הרשאה מפורשת מהמשתמש.

בקשת הרשאה

אם משתמש רוצה לשמור שינויים בקובץ שהוא העניק לו בעבר גישת קריאה, הדפדפן מציג בקשה להרשאה שמבקשת הרשאה לאתר לכתוב שינויים בדיסק. אפשר להפעיל את בקשת ההרשאה רק באמצעות תנועת משתמש, לדוגמה, לחיצה על 'שמירה' לחצן.

בקשת הרשאה שמוצגת לפני שמירת קובץ.
בקשה שמוצגת למשתמשים לפני שהדפדפן מקבל הרשאה לכתיבה בקובץ קיים.

לחלופין, אפליקציית אינטרנט שעורכת כמה קבצים, כמו סביבת פיתוח משולבת (IDE), יכולה גם לבקש הרשאה לשמירה משתנה בזמן הפתיחה.

אם המשתמש בוחר ב'ביטול', ולא מעניק גישת כתיבה, אפליקציית האינטרנט לא יכולה לשמור את השינויים באופן מקומי. היא צריכה לספק למשתמשים שיטה חלופית לשמירת הנתונים, לדוגמה, באמצעות מתן אפשרות "להוריד" את הקובץ או לשמור את הנתונים בענן.

שקיפות

סמל של סרגל הכתובות
סמל של סרגל הכתובות שמציין שהמשתמש העניק לאתר הרשאה ל- לשמור בקובץ מקומי.

אחרי שמשתמש נתן לאפליקציית אינטרנט הרשאה לשמור קובץ מקומי, מוצג סמל בדפדפן בסרגל הכתובות. לחיצה על הסמל פותחת חלון קופץ שמציג את רשימת הקבצים שהמשתמש נתן גישה אל. המשתמש תמיד יכול לבטל את הגישה הזו אם הוא רוצה.

שמירת ההרשאות

אפליקציית האינטרנט יכולה להמשיך לשמור שינויים בקובץ בלי להציג בקשה עד שכל הכרטיסיות של הקובץ המקור סגורים. לאחר סגירת כרטיסייה, האתר מאבד את הגישה. בפעם הבאה שהמשתמש ישתמש ברכיב באינטרנט, תישלח אליהם בקשה מחדש לקבלת גישה לקבצים.

משוב

נשמח לשמוע על החוויה שלכם עם File System Access API.

מתארים את עיצוב ה-API

האם יש משהו ב-API שלא פועל כצפוי? או שחסרות שיטות או מאפיינים שאתם צריכים ליישום הרעיון שלכם? יש לכם שאלה או הערה בנושא האבטחה מודל טרנספורמר?

נתקלתם בבעיה בהטמעה?

מצאת באג בהטמעה של Chrome? או שההטמעה שונה מהמפרט?

  • דווחו על באג בכתובת https://new.crbug.com. כדאי לכלול כמה שיותר פרטים, של ההוראות לשחזור, ומגדירים את רכיבים כ-Blink>Storage>FileSystem. Glitch היא אפשרות טובה לשיתוף תגובות מהירות.

אתם מתכננים להשתמש ב-API?

אתם מתכננים להשתמש ב-File System Access API באתר שלכם? התמיכה הציבורית שלך עוזרת לנו לתת עדיפות וגם מראה לספקי דפדפנים אחרים עד כמה זה קריטי לתמוך בהם.

קישורים שימושיים

אישורים

המפרט של File System Access API נכתב על ידי Marijn Kruisselbrink.