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 ให้เรียกหนึ่งใน วิธีการ, (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;
});

เขียนไฟล์ไปยังระบบไฟล์ในเครื่อง

ในเครื่องมือแก้ไขข้อความ คุณจะบันทึกไฟล์ได้ 2 วิธี ได้แก่ บันทึกและบันทึกเป็น บันทึก เขียนการเปลี่ยนแปลงกลับไปยังไฟล์ต้นฉบับโดยใช้แฮนเดิลไฟล์ที่ดึงมาก่อนหน้านี้ แต่บันทึก "เมื่อ" สร้างไฟล์ใหม่ ดังนั้นจึงต้องมีแฮนเดิลไฟล์ใหม่

สร้างไฟล์ใหม่

หากต้องการบันทึกไฟล์ ให้เรียกใช้ 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 รายการในเมธอด 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
});

การระบุวัตถุประสงค์ของเครื่องมือเลือกไฟล์ต่างๆ

บางครั้งแอปพลิเคชันอาจมีเครื่องมือเลือกที่แตกต่างกันสำหรับวัตถุประสงค์ต่างๆ เช่น Rich Text อาจอนุญาตให้ผู้ใช้เปิดไฟล์ข้อความได้และนําเข้ารูปภาพได้ด้วย โดยค่าเริ่มต้น แต่ละไฟล์ ตำแหน่งที่จะจดจำล่าสุด คุณหลีกเลี่ยงปัญหานี้ได้ด้วยการจัดเก็บค่า id สำหรับเครื่องมือเลือกแต่ละประเภท หากระบุ id ไว้ การใช้งานเครื่องมือเลือกไฟล์จะจดจำ แยกไดเรกทอรีที่ใช้ล่าสุดสำหรับ id ดังกล่าว

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

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

การจัดเก็บแฮนเดิลไฟล์หรือแฮนเดิลไดเรกทอรีใน IndexedDB

แฮนเดิลไฟล์และแฮนเดิลไดเรกทอรีเป็นแบบทำให้ซีเรียลได้ ซึ่งหมายความว่าคุณสามารถบันทึกไฟล์หรือ แฮนเดิลไดเรกทอรีไปยัง IndexedDB หรือเรียกใช้ postMessage() เพื่อส่งระหว่างระดับบนเดียวกัน

การบันทึกแฮนเดิลของไฟล์หรือไดเรกทอรีลงใน IndexedDB หมายความว่าคุณสามารถจัดเก็บสถานะหรือจดจำสถานะ ไฟล์หรือไดเรกทอรีที่ผู้ใช้กำลังใช้งานอยู่ ทำให้สามารถเก็บรายการที่เพิ่งเปิดล่าสุด หรือไฟล์ที่แก้ไขแล้ว เสนอให้เปิดไฟล์ล่าสุดอีกครั้งเมื่อเปิดแอป คืนค่าการทำงานก่อนหน้า ไดเรกทอรี และอื่นๆ ในเครื่องมือแก้ไขข้อความ ฉันจะจัดเก็บรายการไฟล์ 5 รายการล่าสุดที่ผู้ใช้มี เปิดอยู่ ทำให้เข้าถึงไฟล์เหล่านั้นได้อีกครั้ง

ตัวอย่างโค้ดต่อไปนี้แสดงการจัดเก็บและเรียกข้อมูลแฮนเดิลไฟล์และแฮนเดิลไดเรกทอรี คุณสามารถ ดูการทำงานจริงใน 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;
}

การขอสิทธิ์เขียนในคำขออ่านทำให้ฉันลดจำนวนข้อความแจ้งสิทธิ์ ผู้ใช้เห็นข้อความแจ้ง 1 รายการเมื่อเปิดไฟล์ และให้สิทธิ์ทั้งการอ่านและการเขียนในไฟล์

การเปิดไดเรกทอรีและแจกแจงเนื้อหา

หากต้องการแจกแจงไฟล์ทั้งหมดในไดเรกทอรี ให้เรียกใช้ showDirectoryPicker() ผู้ใช้ เลือกไดเรกทอรีในเครื่องมือเลือก ซึ่งหลังจากนั้น FileSystemDirectoryHandle ผลลัพธ์จะช่วยให้คุณสามารถแจกแจงและเข้าถึงไฟล์ของไดเรกทอรีได้ โดยค่าเริ่มต้น คุณจะได้อ่าน เข้าถึงไฟล์ในไดเรกทอรี แต่ถ้าต้องการสิทธิ์การเขียน คุณสามารถส่งผ่าน { mode: 'readwrite' } เป็นเมธอด

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

การแก้ไขเส้นทางของรายการในไดเรกทอรี

เมื่อทำงานกับไฟล์หรือโฟลเดอร์ในไดเรกทอรี การแก้ไขเส้นทางของรายการอาจมีประโยชน์ ที่เป็นปัญหา ซึ่งทำได้ด้วยเมธอด 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() มีพารามิเตอร์ 1 หรือ 2 รายการ ตัวเลือกแรกสามารถ เป็นสตริงที่มีชื่อใหม่หรือ 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() จะแสดงผลการสัญญาพร้อมออบเจ็กต์ 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 นี้ ไม่ต้องการพบไฟล์ที่สร้างขึ้นตรงกับแบบ 1 ต่อ 1 ในฮาร์ดดิสก์ คุณดำเนินการได้ตามปกติใน ระบบไฟล์ส่วนตัวต้นทางเมื่อคุณมีสิทธิ์เข้าถึงรูท 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 Worker

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

การเคลือบพลาสติก

ไม่สามารถทำ Polyfill เมธอด File System Access API ได้โดยสมบูรณ์

  • เมธอด 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 Library ในกรณีนี้ เบราว์เซอร์จะแสดงข้อความแจ้งและขอให้ผู้ใช้เลือก โฟลเดอร์

การแก้ไขไฟล์หรือไดเรกทอรีที่มีอยู่

เว็บแอปจะแก้ไขไฟล์ในดิสก์ไม่ได้หากไม่ได้รับสิทธิ์อย่างชัดแจ้งจากผู้ใช้

ข้อความแจ้งเกี่ยวกับสิทธิ์

หากผู้ใช้ต้องการบันทึกการเปลี่ยนแปลงลงในไฟล์ที่ตนได้ให้สิทธิ์อ่านไว้ก่อนหน้านี้ แสดงข้อความแจ้งสิทธิ์ ซึ่งขอสิทธิ์ให้เว็บไซต์เขียนการเปลี่ยนแปลงลงในดิสก์ คำขอสิทธิ์สามารถทริกเกอร์ได้ด้วยท่าทางสัมผัสของผู้ใช้เท่านั้น เช่น โดยการคลิกไอคอน "บันทึก"

วันที่ ข้อความแจ้งสิทธิ์แสดงขึ้นก่อนบันทึกไฟล์
ข้อความแจ้งที่แสดงต่อผู้ใช้ก่อนให้สิทธิ์เขียนในเบราว์เซอร์ ในไฟล์ที่มีอยู่

นอกจากนี้ เว็บแอปที่แก้ไขไฟล์หลายไฟล์ เช่น 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