纯JS实现监控本地文件变化
本文介绍如何使用纯JavaScript调用File System Observer API实时监控本地文件或文件夹变化。详细讲解实例化对象、选择监听路径、处理变更事件及断开监听的方法。同时提供Chrome 129-134版本获取Origin Trial Token的具体步骤,包括控制台验证与Meta标签配置,帮助开发者在Web应用中实现无刷新文件同步功能。
网页端的超级能力:File System Observer API
在最新的Web技术革新中,出现了一个备受关注的新API——File System Observer API(文件系统观察者API)。它超越了传统的文件操作范畴,赋予网页应用实时监控本地文件系统状态的能力。
实时监控,无需刷新
以Web端相册应用为例,用户可随时添加或删除图片。借助File System Observer API,应用能够即时响应这些文件变动,无需用户手动刷新页面。这种机制如同赋予网页敏锐的感知能力,任何文件的新增、修改或删除操作都能被精准捕获。
实现监听,简单几步
实现文件监听的过程十分简洁。首先,实例化一个FileSystemObserver对象,并传入回调函数。随后,通过showOpenFilePicker或showDirectoryPicker指定要监听的文件或文件夹,并调用observe方法启动监听。
监听文件和文件夹
该API支持监听单个文件或整个文件夹。对于文件夹,可以通过设置recursive: true参数来决定是否递归监听所有子级内容。
操作类型全覆盖
无论是文件的新建、删除、修改、移动还是重命名,File System Observer API均能准确捕捉并通知应用。每次操作都会生成一个详细的FileSystemChangeRecord对象,提供具体的变更信息。
试一下我们的 Demo,实时进行修改,就会触发

解除监听,一键搞定
若需停止对特定文件或文件夹的监听,只需调用disconnect方法,即可恢复初始状态。
怎么申请token
首先访问谷歌FSO申请链接并登录账号。
FSO 属于较新的 API,目前 MDN 和 CanIUse 中尚未建立完整词条。但这并不妨碍其用于生产环境,已有开发者将其应用于线上功能。只需进行少量配置,用户即可体验这一新功能。
Chrome 已对 FSO 开启试用,版本范围涵盖 129 到 134。你可以为 Web App 域名注册试用 token,并跟随以下步骤操作。


在 f12 控制台输入FileSystemObserver,若返回结果则表明使用成功。

那怎么调用这套API来实现我们监听文件夹的功能?
<!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 替换进去。需注意配置正确的域名,域名不匹配会导致报错,无法正常使用。
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 应用赋予更强的能力。
到此这篇关于纯JS实现监控本地文件变化的文章就介绍到这了,更多相关JS监控本地文件变化内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
