• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

    纯JS实现监控本地文件变化

    发布者: 山止川行 | 发布时间: 2025-6-16 07:40| 查看数: 93| 评论数: 0|帖子模式

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

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

    实时监控,无需刷新

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

    实现监听,简单几步

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

    监听文件和文件夹

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

    操作类型全覆盖

    文件的新建、删除、修改、移动,甚至是重命名,File System Observer API都能准确捕捉并通知你的应用。每个操作都会生成一个详细的
    1. FileSystemChangeRecord
    复制代码
    对象,让你能够精确地了解发生了什么。
    试一下我们的 Demo,实时进行修改,就会触发


    解除监听,一键搞定

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

    怎么申请token

    首先我们访问 谷歌FSO申请链接 并登录账号。
    FSO 还是一套崭新的 API,有多新呢?MDN 和 CanIUse 中还没有建立关于它的词条。但这并不意味着我们完全无法用于生产环境,我已经用到线上功能中了。只要做一点配置工作,你和你的用户就能成为全球第一批享受到 FSO 的人 。
    Chrome 已经对 FSO 开启了试用,版本范围是 129 到 134,你可以为你的 Web App 域名注册一个试用 token,你可以跟着我一步一步操作


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

    那怎么调用这套API来实现我们监听文件夹的功能?
    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4.   <meta charset="UTF-8">
    5.   <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6.   <title>谷歌FSO API</title>
    7.   <style>
    8.     .row-box {
    9.       display: flex;
    10.     }
    11.     .row-box > div {
    12.       width: 50%;
    13.       height: 70vh;
    14.       overflow: auto;
    15.     }
    16.     #log-box {
    17.       background-color: #f0f0f0;
    18.       padding: 10px;
    19.       border: 1px solid #ccc;
    20.       border-radius: 5px;
    21.       display: none;
    22.     }
    23.     .alert-box {
    24.       color: red;
    25.       font-size: 12px;
    26.       line-height: 1;
    27.       margin-bottom: 12px;
    28.       padding: 8px;
    29.       border-radius: 12px;
    30.     }
    31.     .alert-title,.alert-content{
    32.       font-size: 20px;
    33.     }
    34.     #dir-name{
    35.       color: red;
    36.     }
    37.   </style>
    38. </head>
    39. <body>
    40.   <h1>File Observer API 监听文件夹变化</h1>
    41.   <div class="alert-box">
    42.     <div class="alert-title">⚠️ 注意!!!</div>
    43.     <div class="alert-content">
    44.       <p>本程序不会上传任何文件到服务器,切勿使用重要文件进行调试,以免造成数据丢失</p>
    45.     </div>
    46.   </div>
    47.   <button id="dir-btn">请选择一个文件夹进行监听</button>
    48.   <p>
    49.     <span id="dir-name"></span>
    50.   </p>
    51.   <div class="row-box">
    52.     <div id="dir-info"></div>
    53.     <div id="log-box"></div>
    54.   </div>
    55.   <script type="importmap">
    56.   {
    57.     "imports": {
    58.       "@rejax/fsot": "https://unpkg.com/@rejax/fsot/index.js"
    59.     }
    60.   }
    61.   </script>
    62.   <script type="module">
    63.     import { setFSObserverToken } from './index.js'
    64.     setFSObserverToken()
    65.   </script>
    66.   <script type="module">
    67.     import { FSObserver } from '@rejax/fsot'

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

    72.     let dirEntries = null
    73.     const fob = new FSObserver(callback)

    74.     const kindMap = {
    75.       'directory': '文件夹',
    76.       'file': '文件',
    77.     }
    78.     const operationMap = {
    79.       'appeared': '新增',
    80.       'disappeared': '删除',
    81.       'modified': '修改',
    82.       'moved': '移动',
    83.       'renamed': '重命名',
    84.     }
    85.    
    86.     dirBtn.addEventListener('click', handleClick)
    87.    
    88.     async function handleClick() {
    89.       const dirHandle = await window.showDirectoryPicker()
    90.       dirEntries = dirHandle.values()

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

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

    102.       for await (const entry of dirEntries) {
    103.         const { kind, name } = entry
    104.         showChild(kind, name)
    105.       }
    106.     }
    107.    
    108.     async function callback(log, records, observer) {
    109.       console.log('log', log);
    110.       const logEle = document.createElement('p')
    111.       logEle.innerText = log.description
    112.       logBox.appendChild(logEle)

    113.       const { operation, handle, to, from } = log
    114.       switch (operation) {
    115.         case 'create':
    116.           add(handle, to)
    117.           break
    118.         case 'remove':
    119.           remove(handle, to)
    120.           break
    121.         case 'modify':
    122.           modifyFile(handle, to)
    123.           break
    124.         case 'move':
    125.           move(handle, to, from)
    126.           break
    127.         case 'rename':
    128.           rename(handle, to, from)
    129.           break
    130.       }
    131.     }
    132.     function showChild(kind, name) {
    133.       const entryId = `${kind}-${name}`
    134.       const entry = document.createElement('p')
    135.       entry.id = entryId
    136.       entry.innerHTML = `${kindMap[kind]} ${name}`
    137.       dirInfo.appendChild(entry)
    138.     }
    139.    
    140.     async function add(handle, path) {
    141.       const { kind } = handle
    142.       if (!path.includes('/')) {
    143.         // 子文件中增加实体时,不显示
    144.         showChild(kind, path)
    145.       }
    146.     }

    147.     function remove(handle, path) {
    148.       const { kind, name } = handle
    149.       const childName = path || name
    150.       const entryId = `${kind}-${childName}`
    151.       const entry = document.getElementById(entryId)
    152.       if (entry) {
    153.         entry.remove()
    154.       }
    155.     }

    156.     async function modifyFile(handle, path) {
    157.       
    158.     }

    159.     async function rename(handle, path, oldPath) {
    160.       const { kind, name } = handle
    161.       const childName = path || name
    162.       const entryId = `${kind}-${oldPath}`
    163.       let entry = document.getElementById(entryId)
    164.       if (entry) {
    165.         entry.innerText = `${kindMap[kind]} ${path}`
    166.         entry.id = `${kind}-${path}`
    167.       }
    168.     }

    169.     async function move(handle, path, oldPath) {
    170.       const { kind, name } = handle
    171.       const childName = path || name
    172.       const entryId = `${kind}-${oldPath}`
    173.       let entry = document.getElementById(entryId)
    174.       const pathArr = path.split('/')
    175.       const oldPathArr = oldPath.split('/')
    176.       
    177.       // 文件层级下沉
    178.       if (pathArr.length > oldPathArr.length) {
    179.         entry?.remove()
    180.       }

    181.       // 文件层级上升
    182.       if (pathArr.length < oldPathArr.length && pathArr.length === 1) {
    183.         showChild(kind, name)
    184.       }
    185.     }
    186.   </script>
    187. </body>
    188. </html>
    复制代码
    这里是setFSObserverToken,把前面申请到的token进行替换即可,这里要配置好域名,域名不一样会报错,无法使用
    1. export function setFSObserverToken () {
    2.   let token = 'AvBwEvROC6H+jSr2r1nwgj0G0T8tOs2MnXT9GSFcHVXV2un4GQ/+9Sa2TfWbJGhUbZe5lyF+APSpjovP+NtNEQwAAABWeyJvcmlnaW4iOiJodHRwOi8vMTI3LjAuMC4xOjUzMTA1IiwiZmVhdHVyZSI6IkZpbGVTeXN0ZW1PYnNlcnZlciIsImV4cGlyeSI6MTc0NzE4MDc5OX'
    3.   const origin = window.location.origin
    4.   if (origin.includes('https://rejax.fun')) {
    5.     token = 'AvBwEvROC6H+jSr2r1nwgj0G0T8tOs2MnXT9GSFcHVXV2un4GQ/+9Sa2TfWbJGhUbZe5lyF+APSpjovP+NtNEQwAAABWeyJvcmlnaW4iOiJodHRwOi8vMTI3LjAuMC4xOjUzMTA1IiwiZmVhdHVyZSI6IkZpbGVTeXN0ZW1PYnNlcnZlciIsImV4cGlyeSI6MTc0NzE4MDc5OX'
    6.   }
    7.   const meta = document.createElement('meta')
    8.   meta.httpEquiv = 'origin-trial'
    9.   meta.content = token
    10.   document.head.appendChild(meta)
    11. }
    复制代码
    总结

    File System Observer API的引入,不仅仅是对Web开发者的一次解放,更是对整个Web生态的巨大推动。它让Web应用更加强大,用户体验更加流畅。现在,是时候开始探索这个全新的API,为你的Web应用增添超级能力了!
    到此这篇关于纯JS实现监控本地文件变化的文章就介绍到这了,更多相关JS监控本地文件变化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    来源:https://www.jb51.net/javascript/339458mgr.htm
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有账号?立即注册

    ×

    最新评论

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表