用纯JS监控本地文件,让网页拥有千里眼!

你是否曾梦想拥有一个能够实时监控本地文件变化的网页应用?现在,这个梦想即将成为现实!🌟

🌐 网页端的超级能力:File System Observer API

在最新的Web技术革命中,我们迎来了一个令人兴奋的新API------File System Observer API(文件系统观察者API)。这不仅仅是一个简单的文件操作工具,它赋予了网页应用前所未有的能力------实时监控本地文件的一举一动!

🔍 实时监控,无需刷新

想象一下,你正在开发一个Web端相册应用,用户可以随时添加或删除图片。有了File System Observer API,你的应用能够即时响应这些变化,无需用户手动刷新页面。这就像是给网页装上了千里眼和顺风耳,任何文件的新增、修改或删除都逃不过它的监控。

🛠️ 实现监听,简单几步

实现文件监听的步骤简单到令人难以置信。首先,你需要实例化一个FileSystemObserver对象,并传入一个回调函数。然后,通过showOpenFilePickershowDirectoryPicker选择要监听的文件或文件夹,并使用observe方法开始监听。

📁 监听文件和文件夹

无论是单个文件还是整个文件夹,File System Observer API都能轻松应对。你可以选择监听文件夹的一级内容,或者通过设置recursive: true来监听所有子级内容。

🔄 操作类型全覆盖

文件的新建、删除、修改、移动,甚至是重命名,File System Observer API都能准确捕捉并通知你的应用。每个操作都会生成一个详细的FileSystemChangeRecord对象,让你能够精确地了解发生了什么。

试一下我们的 Demo,实时进行修改,就会触发

🛑 解除监听,一键搞定

当你想要停止监听某个文件或文件夹时,只需调用disconnect方法,一切都会恢复原状。

怎么申请token?

首先我们访问 谷歌FSO申请链接 并登录账号。

FSO 还是一套崭新的 API,有多新呢?MDN 和 CanIUse 中还没有建立关于它的词条。但这并不意味着我们完全无法用于生产环境,我已经用到线上功能中了。只要做一点配置工作,你和你的用户就能成为全球第一批享受到 FSO 的人 😎。

Chrome 已经对 FSO 开启了试用,版本范围是 129 到 134,你可以为你的 Web App 域名注册一个试用 token,你可以跟着我一步一步操作

f12打开控制台输入FileSystemObserver,如果有返回既是使用成功

那怎么调用这套API来实现我们监听文件夹的功能?

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>谷歌FSO API</title>
  <style>
    .row-box {
      display: flex;
    }
    .row-box > div {
      width: 50%;
      height: 70vh;
      overflow: auto;
    }
    #log-box {
      background-color: #f0f0f0;
      padding: 10px;
      border: 1px solid #ccc;
      border-radius: 5px;
      display: none;
    }
    .alert-box {
      color: red;
      font-size: 12px;
      line-height: 1;
      margin-bottom: 12px;
      padding: 8px;
      border-radius: 12px;
    }
    .alert-title,.alert-content{
      font-size: 20px;
    }
    #dir-name{
      color: red;
    }
  </style>
</head>
<body>
  <h1>File Observer API 监听文件夹变化</h1>
  <div class="alert-box">
    <div class="alert-title">⚠️ 注意!!!</div>
    <div class="alert-content">
      <p>本程序不会上传任何文件到服务器,切勿使用重要文件进行调试,以免造成数据丢失</p>
    </div>
  </div>
  <button id="dir-btn">请选择一个文件夹进行监听</button>
  <p>
    <span id="dir-name"></span>
  </p>
  <div class="row-box">
    <div id="dir-info"></div>
    <div id="log-box"></div>
  </div>
  <script type="importmap">
  {
    "imports": {
      "@rejax/fsot": "https://unpkg.com/@rejax/fsot/index.js"
    }
  }
  </script>
  <script type="module">
    import { setFSObserverToken } from './index.js'
    setFSObserverToken()
  </script>
  <script type="module">
    import { FSObserver } from '@rejax/fsot'

    const dirBtn = document.getElementById('dir-btn')
    const dirInfo = document.getElementById('dir-info')
    const logBox = document.getElementById('log-box')
    const dirName = document.getElementById('dir-name')

    let dirEntries = null
    const fob = new FSObserver(callback)

    const kindMap = {
      'directory': '文件夹',
      'file': '文件',
    }
    const operationMap = {
      'appeared': '新增',
      'disappeared': '删除',
      'modified': '修改',
      'moved': '移动',
      'renamed': '重命名',
    }
    
    dirBtn.addEventListener('click', handleClick)
    
    async function handleClick() {
      const dirHandle = await window.showDirectoryPicker()
      dirEntries = dirHandle.values()

      const options = {
        recursive: true,
      }
      
      console.log('fob', fob);
      
      await fob.observe(dirHandle, options)
      const localDirName = fob.rootHandle.name
      dirName.innerText=`当前监听的本地目录: ${localDirName}`

      dirBtn.disabled = true
      logBox.style.display = 'block'

      for await (const entry of dirEntries) {
        const { kind, name } = entry
        showChild(kind, name)
      }
    }
    
    async function callback(log, records, observer) {
      console.log('log', log);
      const logEle = document.createElement('p')
      logEle.innerText = log.description
      logBox.appendChild(logEle)

      const { operation, handle, to, from } = log
      switch (operation) {
        case 'create':
          add(handle, to)
          break
        case 'remove':
          remove(handle, to)
          break
        case 'modify':
          modifyFile(handle, to)
          break
        case 'move':
          move(handle, to, from)
          break
        case 'rename':
          rename(handle, to, from)
          break
      }
    }
    function showChild(kind, name) {
      const entryId = `${kind}-${name}`
      const entry = document.createElement('p')
      entry.id = entryId
      entry.innerHTML = `${kindMap[kind]} ${name}`
      dirInfo.appendChild(entry)
    }
    
    async function add(handle, path) {
      const { kind } = handle
      if (!path.includes('/')) {
        // 子文件中增加实体时,不显示
        showChild(kind, path)
      }
    }

    function remove(handle, path) {
      const { kind, name } = handle
      const childName = path || name
      const entryId = `${kind}-${childName}`
      const entry = document.getElementById(entryId)
      if (entry) {
        entry.remove()
      }
    }

    async function modifyFile(handle, path) {
      
    }

    async function rename(handle, path, oldPath) {
      const { kind, name } = handle
      const childName = path || name
      const entryId = `${kind}-${oldPath}`
      let entry = document.getElementById(entryId)
      if (entry) {
        entry.innerText = `${kindMap[kind]} ${path}`
        entry.id = `${kind}-${path}`
      }
    }

    async function move(handle, path, oldPath) {
      const { kind, name } = handle
      const childName = path || name
      const entryId = `${kind}-${oldPath}`
      let entry = document.getElementById(entryId)
      const pathArr = path.split('/')
      const oldPathArr = oldPath.split('/')
      
      // 文件层级下沉
      if (pathArr.length > oldPathArr.length) {
        entry?.remove()
      }

      // 文件层级上升
      if (pathArr.length < oldPathArr.length && pathArr.length === 1) {
        showChild(kind, name)
      }
    }
  </script>
</body>
</html>

这里是setFSObserverToken,把前面申请到的token进行替换即可,这里要配置好域名,域名不一样会报错,无法使用

ini 复制代码
export function setFSObserverToken () {

  let token = 'AvBwEvROC6H+jSr2r1nwgj0G0T8tOs2MnXT9GSFcHVXV2un4GQ/+9Sa2TfWbJGhUbZe5lyF+APSpjovP+NtNEQwAAABWeyJvcmlnaW4iOiJodHRwOi8vMTI3LjAuMC4xOjUzMTA1IiwiZmVhdHVyZSI6IkZpbGVTeXN0ZW1PYnNlcnZlciIsImV4cGlyeSI6MTc0NzE4MDc5OX'

  const origin = window.location.origin

  if (origin.includes('https://rejax.fun')) {

    token = 'AvBwEvROC6H+jSr2r1nwgj0G0T8tOs2MnXT9GSFcHVXV2un4GQ/+9Sa2TfWbJGhUbZe5lyF+APSpjovP+NtNEQwAAABWeyJvcmlnaW4iOiJodHRwOi8vMTI3LjAuMC4xOjUzMTA1IiwiZmVhdHVyZSI6IkZpbGVTeXN0ZW1PYnNlcnZlciIsImV4cGlyeSI6MTc0NzE4MDc5OX'

  }

  const meta = document.createElement('meta')

  meta.httpEquiv = 'origin-trial'

  meta.content = token

  document.head.appendChild(meta)

}

总结

File System Observer API的引入,不仅仅是对Web开发者的一次解放,更是对整个Web生态的巨大推动。它让Web应用更加强大,用户体验更加流畅。现在,是时候开始探索这个全新的API,为你的Web应用增添超级能力了!

相关推荐
恋猫de小郭22 分钟前
Android Studio Cloud 正式上线,不只是 Android,随时随地改 bug
android·前端·flutter
清岚_lxn5 小时前
原生SSE实现AI智能问答+Vue3前端打字机流效果
前端·javascript·人工智能·vue·ai问答
ZoeLandia5 小时前
Element UI 设置 el-table-column 宽度 width 为百分比无效
前端·ui·element-ui
橘子味的冰淇淋~6 小时前
解决 vite.config.ts 引入scss 预处理报错
前端·vue·scss
小小小小宇8 小时前
V8 引擎垃圾回收机制详解
前端
lauo8 小时前
智体知识库:ai-docs对分布式智体编程语言Poplang和javascript的语法的比较(知识库问答)
开发语言·前端·javascript·分布式·机器人·开源
拉不动的猪8 小时前
设计模式之------单例模式
前端·javascript·面试
一袋米扛几楼988 小时前
【React框架】什么是 Vite?如何使用vite自动生成react的目录?
前端·react.js·前端框架
Alt.98 小时前
SpringMVC基础二(RestFul、接收数据、视图跳转)
java·开发语言·前端·mvc
进取星辰9 小时前
1、从零搭建魔法工坊:React 19 新手村生存指南
前端·react.js·前端框架