File System API 实现 Web级系统文件读写

在Web环境中,理论上是无法轻易实现直接对系统级文件进行访问和修改的,因为浏览器本身就是个隔离容器,这个容器和系统层级的交互,只能通过官方API去打通,因此官方推出了File System API,这套API较新存在兼容性问题,在使用时需做好兼容处理。

概念

File System API 使用了面向对象的概念,把系统文件映射成了js对象,在我们对系统文件的日常操作上,一般都是文件夹内,存放文件。而在系统底层,文件夹本身也是文件,就是一种特殊文件类型的文件而已,因此File System API 也对文件夹和文件的概念,提供了2种文件对象,分别对应FileSystemDirectoryHandleFileSystemFileHandle

FileSystemFileHandle 对象(读取系统文件)

FileSystemFileHandle 是一个系统文件信息的描述对象,官方称这个对象为文件句柄对象,说人话就是( 这个对象是一个当前选择文件对象和系统之间的映射对象,当你使用 FileSystemFileHandle实例.getFile()后就可以得到当前句柄对象指向的文件所属的File对象 )File对象到手,怎么玩就考验基本功了😀

html 复制代码
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<button onclick="selectFile()">选择文件</button>
<body>
<script>
  const selectFile = async ()=>{
    //window.showOpenFilePicker 会打开一个文件选择对话框,用户选择文件后,将返回一个 FileSystemFileHandle数组
    const FileSystemFileHandleInstanceArray = await window.showOpenFilePicker()
    //这里只取第一个
    const FileSystemFileHandleInstance = FileSystemFileHandleInstanceArray[0]
    //通过FileSystemFileHandle的getFile方法获取File对象
    const FileInstance = await  FileSystemFileHandleInstance.getFile()
    console.log(FileInstance)
    //File {name: 'test_image.png', lastModified: 1711421687929, lastModifiedDate: Tue Mar 26 2024 10:54:47 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 42763, ...}
  }
</script>
</body>
</html>

FileSystemFileHandle 对象(写入系统文件)

前面说了FileSystemFileHandle 读取,但是怎么样把修改后的文件数据写回硬盘呢?那就要再谈到句柄对象 的概念了,既然可以通过这个对象拿到系统中的文件对象,那么这个对象,对文件对应的硬盘所处位置的映射关系肯定也是知道的,所以,不出意料的这个对象提供一个方法createWritable 这个方法会返回一个Promise,这个Promise成功的兑换结果就是一个写入器对象,这个对象提供一个write 方法,这个方法的参数就是你要写入回硬盘的内容,支持 ArrayBufferBlobstring,当然这个方法的调用,本质上不是直接写入硬盘,而且是先写入内存,然后再通过调用close方法来完成写入

html 复制代码
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<button onclick="selectFile()">选择文件</button>
<body>
<script>
  const selectFile = async ()=>{
    //window.showOpenFilePicker 会打开一个文件选择对话框,用户选择文件后,将返回一个 FileSystemFileHandle数组
    const FileSystemFileHandleInstanceArray = await window.showOpenFilePicker()
    //这里只取第一个
    const FileSystemFileHandleInstance = FileSystemFileHandleInstanceArray[0]
    //通过FileSystemFileHandle的getFile方法获取File对象
    const FileInstance = await  FileSystemFileHandleInstance.getFile()
    //通过File的text方法读取文件内容
    let FileInstanceContent = await FileInstance.text()
    //对文件内容进行修改
    FileInstanceContent += 'new Content'
    //通过FileSystemFileHandle的createWritable方法创建一个可写的文件
    const writable = await FileSystemFileHandleInstance.createWritable()
    //通过可写文件的write方法写入文件内容
    await writable.write(FileInstanceContent)
    //通过可写文件的close方法关闭文件
    await writable.close()
  }
</script>
</body>
</html>

FileSystemDirectoryHandle 对象

FileSystemDirectoryHandle 对象映射的就是文件夹对象,像一个盒子,可以通过调用FileSystemDirectoryHandle.entries()来获取一个异步迭代器,这个异步迭代器里面存放的就是他的下属文件的名称和FileSystemFileHandle 对象,如果下属文件存在文件夹嵌套,则存放的是文件名和FileSystemDirectoryHandle对象。

比如以这样的目录结构为事例

html 复制代码
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<button onclick="selectFile()">选择文件</button>
<body>
<script>
  const selectFile = async ()=>{
    //window.showDirectoryPicker 会打开一个对话框,用户选择文件夹后,将返回一个 FileSystemDirectoryHandle 对象
    const FileSystemDirectoryHandle = await window.showDirectoryPicker()
    const FileSystemDirectoryHandleAsyncIterator =  FileSystemDirectoryHandle.entries()
    //使用异步迭代器去读取
    for await (const [fileName,FileSystemFileHandleOrFileSystemDirectoryHandle ] of FileSystemDirectoryHandleAsyncIterator) {
      console.log(FileSystemFileHandleOrFileSystemDirectoryHandle)
      //FileSystemFileHandle{kind: 'file', name: 'test_image.png'}
      //FileSystemDirectoryHandle{kind: 'directory', name: '子测试文件夹'}
    }
  }
</script>
</body>
</html>

可以发现不论是FileSystemFileHandle 还是FileSystemDirectoryHandle 都会有一个属性kind用来标记文件类型,就和开篇说的一样,文件夹本质就是一种特殊的文件,所以可以结合这一规律,自行通过递归,来实现一个目录树的生成。

html 复制代码
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<button onclick="selectFile()">选择文件</button>
<body>
<script>
  const selectFile = async ()=>{
    //window.showDirectoryPicker 会打开一个对话框,用户选择文件夹后,将返回一个 FileSystemDirectoryHandle 对象
    const FileSystemDirectoryHandle = await window.showDirectoryPicker()
    const fileTree = await createDirectoryTree(FileSystemDirectoryHandle)
    console.log(fileTree)
  }
  /**
   * @Description:通过 FileSystemDirectoryHandle创建目录树的功能函数
   * @author 莫若省
   * @createTime 2024/3/26
  */

  const createDirectoryTree = async (FileSystemDirectoryHandle)=>{
    FileSystemDirectoryHandle.children=[]
    const FileSystemDirectoryHandleAsyncIterator =  FileSystemDirectoryHandle.entries()
    for await (const [fileName,FileSystemFileHandleOrFileSystemDirectoryHandle ] of FileSystemDirectoryHandleAsyncIterator) {
      if(FileSystemFileHandleOrFileSystemDirectoryHandle.kind === 'directory'){
        FileSystemDirectoryHandle.children.push(await createDirectoryTree(FileSystemFileHandleOrFileSystemDirectoryHandle))
      }else{
        FileSystemDirectoryHandle.children.push(FileSystemFileHandleOrFileSystemDirectoryHandle)
      }
    }
    return FileSystemDirectoryHandle
  }
</script>
</body>
</html>
js 复制代码
 FileSystemDirectoryHandle {children: Array(3), kind: 'directory', name: '测试文件夹'}
   children: Array(3)
        FileSystemFileHandle {kind: 'file', name: '.DS_Store'}
        FileSystemFileHandle {kind: 'file', name: 'test_image.png'}
        FileSystemDirectoryHandle
        kind: "directory"
        name: "子测试文件夹"
        children: Array(1)
             FileSystemFileHandle {kind: 'file', name: '未命名.txt'}
             length: 1
             [[Prototype]]: Array(0)
             [[Prototype]]: FileSystemDirectoryHandle
        length: 3
        [[Prototype]]: Array(0)
    kind: "directory"
    name: "测试文件夹"
    [[Prototype]]: FileSystemDirectoryHandle

FileSystemDirectoryHandleFileSystemFileHandle对象的获取

官方描述如下

FileSystemDirectoryHandle 接口提供指向一个文件系统目录的句柄。 这个接口可以通过 window.showDirectoryPicker()StorageManager.getDirectory() (en-US)DataTransferItem.getAsFileSystemHandle() (en-US)FileSystemDirectoryHandle.getDirectoryHandle() 这些方法访问获得。

FileSystemFileHandle 接口表示一个指向文件系统条目的句柄。可通过 window.showOpenFilePicker() 方法来访问此接口。

可以通过上述方法调用,获取文件句柄对象得到文件对象后实现业务功能。 此外File System API 还提供了其他文件操作相关API具体可以通过文件系统 API - Web API 接口参考 | MDN (mozilla.org)查询

相关推荐
Bigger几秒前
Tauri (25)——消除搜索列表默认选中的 UI 闪动
前端·react.js·weui
李少兄11 分钟前
简单讲讲 SVG:前端开发中的矢量图形
前端·svg
前端小万13 分钟前
告别 CJS 库加载兼容坑
前端·前端工程化
恋猫de小郭13 分钟前
Flutter 3.38.1 之后,因为某些框架低级错误导致提交 Store 被拒
android·前端·flutter
JarvanMo17 分钟前
Flutter 需要 Hooks 吗?
前端
光影少年27 分钟前
前端如何虚拟列表优化?
前端·react native·react.js
Moment29 分钟前
一杯茶时间带你基于 Yjs 和 reactflow 构建协同流程图编辑器 😍😍😍
前端·后端·面试
invicinble1 小时前
对于前端数据的生命周期的认识
前端
PieroPc1 小时前
用FastAPI 后端 和 HTML/CSS/JavaScript 前端写一个博客系统 例
前端·html·fastapi
hunter14501 小时前
2026.1.4 html简单制作
java·前端·笔记·html