You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:27:02