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

相关推荐
twins352041 分钟前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky1 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常1 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n02 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。2 小时前
案例-任务清单
前端·javascript·css
zqx_73 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己3 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称4 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色4 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript