Y-WebSocket服务端如何获取YDoc文本并写入磁盘?
如何在y-websocket服务端将Yjs文档刷入磁盘?
问题描述
我需要在y-websocket服务端把Yjs文档写入磁盘,编写了如下代码:
const handleFileSync = async (docName, ydoc) => { try { let fileContent = await getFileJsonData(docName) if (!fileContent) { console.error(`获取文件信息失败,文件信息: ${fileContent},docName:${docName}`) return } let projectId = fileContent.result.project_id let fileName = fileContent.result.name let text = ydoc.getText(docName) console.info('获取文件文本:', text) fs.writeFile(`/opt/data/project/${projectId}/${fileName}`, text, (err) => { if (err) { console.error('写入文件失败:', err) } }) } catch (err) { console.error('同步文件到磁盘失败', err) } }
运行时触发错误:
Failed to sync file to disk TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of YText at Object.writeFile (node:fs:2155:5) at handleFileSync (/home/node/app/bin/rd_file.js:15:8) at processTicksAndRejections (node:internal/process/task_queues:96:5) { code: 'ERR_INVALID_ARG_TYPE' }
从日志可见ydoc.getText(docName)返回的是YText实例而非字符串,导致写入失败。我查阅了YText相关文档后,尝试用Y.decodeSnapshot(persistedYdoc.snapshot)获取内容,完整代码如下:
if (typeof persistenceDir === 'string') { console.info('将文档持久化到 "' + persistenceDir + '"') // @ts-ignore const LeveldbPersistence = require('y-leveldb').LeveldbPersistence const ldb = new LeveldbPersistence(persistenceDir) persistence = { provider: ldb, bindState: async (docName, ydoc) => { const persistedYdoc = await ldb.getYDoc(docName) const newUpdates = Y.encodeStateAsUpdate(ydoc) ldb.storeUpdate(docName, newUpdates) Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc)) ydoc.on('update', update => { ldb.storeUpdate(docName, update) const content = Y.decodeSnapshot(persistedYdoc.snapshot) handleFileSync(docName, ydoc, content) }) }, writeState: async (docName, ydoc) => { } } }
但又出现新错误:
Caught error while handling a Yjs update TypeError: Cannot read properties of undefined (reading 'length') at Object.readVarUint (/home/node/app/node_modules/lib0/dist/buffer-9b566988.cjs:1135:27) at readDeleteSet (/home/node/app/node_modules/yjs/dist/yjs.cjs:309:42) at decodeSnapshotV2 (/home/node/app/node_modules/yjs/dist/yjs.cjs:2614:23) at Object.decodeSnapshot (/home/node/app/node_modules/yjs/dist/yjs.cjs:2621:31) at /home/node/app/bin/utils.js:44:27 at /home/node/app/node_modules/lib0/dist/observable.cjs:77:90 at Array.forEach (<anonymous>) at WSSharedDoc.emit (/home/node/app/node_modules/lib0/dist/observable.cjs:77:77) at cleanupTransactions (/home/node/app/node_modules/yjs/dist/yjs.cjs:3259:15) at transact (/home/node/app/node_modules/yjs/dist/yjs.cjs:3335:9)
请问如何正确获取YDoc的内容并写入磁盘?
解决方案
1. 正确获取YText的文本内容
ydoc.getText(docName)返回的是YText类型实例,并非原生字符串。要拿到可直接写入文件的文本,只需调用YText的toString()方法:
// 替换原文本获取逻辑 let text = ydoc.getText(docName).toString();
2. 修复Y.decodeSnapshot的错误用法
persistedYdoc.snapshot本身已是解码后的文档状态结构,不需要再用Y.decodeSnapshot处理。Y.decodeSnapshot仅用于解码Y.encodeSnapshot生成的二进制快照数据,直接访问snapshot即可获取状态信息。
优化后的完整代码
调整后的handleFileSync函数:
const handleFileSync = async (docName, ydoc) => { try { let fileContent = await getFileJsonData(docName) if (!fileContent) { console.error(`获取文件信息失败,文件信息: ${fileContent},docName:${docName}`) return } let projectId = fileContent.result.project_id let fileName = fileContent.result.name // 正确获取文本内容 let text = ydoc.getText(docName).toString(); console.info('获取文件文本:', text) // 可选:用异步写法替代回调,避免嵌套 await fs.promises.writeFile(`/opt/data/project/${projectId}/${fileName}`, text); } catch (err) { console.error('同步文件到磁盘失败', err) } }
同时调整bindState中的更新监听逻辑,无需多余的快照解码操作:
ydoc.on('update', update => { ldb.storeUpdate(docName, update) // 直接传入当前ydoc获取最新内容 handleFileSync(docName, ydoc) })
额外建议
- 写入文件前确保目标目录存在,可提前创建:
const dirPath = `/opt/data/project/${projectId}`; await fs.promises.mkdir(dirPath, { recursive: true });
- 使用
fs.promises的异步API,配合async/await让代码逻辑更清晰。
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

