你是否曾梦想拥有一个能够实时监控本地文件变化的网页应用?现在,这个梦想即将成为现实!🌟
🌐 网页端的超级能力:File System Observer API
在最新的Web技术革命中,我们迎来了一个令人兴奋的新API------File System Observer API(文件系统观察者API)。这不仅仅是一个简单的文件操作工具,它赋予了网页应用前所未有的能力------实时监控本地文件的一举一动!
🔍 实时监控,无需刷新
想象一下,你正在开发一个Web端相册应用,用户可以随时添加或删除图片。有了File System Observer API,你的应用能够即时响应这些变化,无需用户手动刷新页面。这就像是给网页装上了千里眼和顺风耳,任何文件的新增、修改或删除都逃不过它的监控。
🛠️ 实现监听,简单几步
实现文件监听的步骤简单到令人难以置信。首先,你需要实例化一个FileSystemObserver
对象,并传入一个回调函数。然后,通过showOpenFilePicker
或showDirectoryPicker
选择要监听的文件或文件夹,并使用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应用增添超级能力了!