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)查询

相关推荐
ai小鬼头13 分钟前
百度秒搭发布:无代码编程如何让普通人轻松打造AI应用?
前端·后端·github
漂流瓶jz13 分钟前
清除浮动/避开margin折叠:前端CSS中BFC的特点与限制
前端·css·面试
前端 贾公子15 分钟前
在移动端使用 Tailwind CSS (uniapp)
前端·uni-app
散步去海边15 分钟前
Cursor 进阶使用教程
前端·ai编程·cursor
清幽竹客16 分钟前
vue-30(理解 Nuxt.js 目录结构)
前端·javascript·vue.js
weiweiweb88818 分钟前
cesium加载Draco几何压缩数据
前端·javascript·vue.js
幼儿园技术家19 分钟前
微信小店与微信小程序简单集成指南
前端
我不吃饼干9 天前
鸽了六年的某大厂面试题:你会手写一个模板引擎吗?
前端·javascript·面试
涵信9 天前
第一节 布局与盒模型-Flex与Grid布局对比
前端·css
我不吃饼干9 天前
鸽了六年的某大厂面试题:手写 Vue 模板编译(解析篇)
前端·javascript·面试