硬盘坏了,一气之下写了个康复程序

师傅解救无望

硬盘现已寄过去超过一周了,一问竟然是还没开端弄???

硬盘坏了,一气之下用 js 写了个恢复程序

再过一周,上来就问我分几个区?我要康复哪些数据?我要康复的数据在哪个位置?

硬盘坏了,一气之下用 js 写了个恢复程序

硬盘坏了,一气之下用 js 写了个恢复程序

硬盘坏了,一气之下用 js 写了个恢复程序

那好吧,已然给了钱师傅也都抛弃了,我也没什么好寄托期望的了。何况通过这三个星期的缓解,心境现已平复了许多,就像时光,回不来了便是回不来了。

自救之路

在把硬盘寄过去的时刻里,等待师傅的修正成果的时刻里,我并没有闲着(在摸鱼)。

通过调研,数据康复办法一般有:

  • 硬件损坏,对坏的盘进行修正
  • 误删或逻辑过错等,文件扫描修正
  • git 重置康复

很明显,这些都不适用于我现在的场景。由于师傅能不能修好是不知道的,我仅仅数据盘没了,体系盘还在。由于 vscode 的数据目录空间占比较小,就没有搬迁到数据盘里,这刚好可认为康复代码供给了可能。

这是由于新版 vscode 有一个时刻线功用,这个时刻线数据是默认存储在用户目录下的。

我从 C:/Users/love/AppData/Roaming/Code/User/History 目录中确实找到了许多名为 entries.json 的文件,结构如下:

{
  // 配置版别
  "version": 1,
  // 本来文件所在位置
  "resource": "file:///d%3A/git2/cloudcmd/.madrun.mjs",
  // 文件历史
  "entries": [
    {
      // 历史文件存储的名称
      "id": "YFRn.mjs",
      "source": "作业区修改",
      // 修改的时刻
      "timestamp": 1656583915880
    },
    {
      "id": "Vfen.mjs",
      "timestamp": 1656585664751
    },
  ]
}

通过上面的文件大概能够看到,每一个时刻点的文件都保存在另一个随机命名的文件里。而网上的办法基本都是自己一个个手动到目录里去依据最新的 id 去找对应的文件内容,然后创立文件并把内容复制出来。

这个进程康复一两个文件还好,但我这可是要康复整个 git 作业区,大概有几十个项目上千个文件。

这时候当然是在网上找找有没有什么 vscode 数据康复 相关的东西,很惋惜找了大半天都没有找到。

气死我了,一气之下就自己写个!

康复程序开发步骤

毕竟只要数据在磁盘上,无非便是一个文件读取操作的问题,还要拿在这水文章,见谅见谅。

首先考虑需求:

  • 我要完成一个主动扫描 vscode 数据目录
  • 然后以原始的目录结构复原出来,不需求我自己去创立文件夹和文件
  • 假如复原的文件最新的那份不是我想要的,我还能依据时刻线进行比照和挑选
  • 扫描出来有N个项目时,我能够指定只复原某此项目
  • 我能够搜索文件、目录名或文件内容进行复原
  • 为了便利,我还要一个看起来不太丑的操作界面

大概就上面这些吧。

然后考虑完成:

我要完成一个主动扫描 vscode 数据目录

要的便是我自己连数据目录和康复地址也不需求填写,就能主动康复的那种。那么就让程序来主动查找数据目录。通过调研,各版别的 vscode 的数据目录一般保存在这些当地:

参考: stackoverflow.com/a/72610691

  - win -- C:\Users\Mark\AppData\Roaming\Code\User\History
  - win -- C:\Users\Mark\AppData\Roaming\Code - Insiders\User\History
  - /home/USER/.config/VSCodium/User/History/
  - C:\Users\USER\AppData\Roaming\VSCodium\User\History

大概有上面这些途径,当然不排除运用者故意把默认位置修改掉这种边际情况,或许运用者就只想扫描某个数据目录的情况,所以我也要支撑手动输入目录:

  let { historyPath, toDir } = req.body
  const homeDir = os.userInfo().homedir
  const pathList = [
    historyPath,
    `${homeDir}/AppData/Roaming/Code/User/History/`,
    `${homeDir}/AppData/Roaming/Code - Insiders/User/History/`,
    `${homeDir}/AppData/Roaming/VSCodium/User/History`,
    `${homeDir}/.config/VSCodium/User/History/`,
  ]
  historyPath = (() => {
    return pathList.find((path) => path && fs.existsSync(path))
  })()
  toDir = toDir || normalize(`${process.cwd()}/re-store/`)

然后以原始的目录结构复原出来……

这就需求解析扫描到的时刻线文件 entries.json 了。咱们先把解析成果放到一个 list 中,以下是一个完好的解析办法。

然后再把列表转换为树型,与硬盘上的状态对应起来,这样便于调试数据和可视化。

function scan({ historyPath, toDir } = {}) {
  const gitRoot = `${historyPath}/**/entries.json`
  fs.existsSync(toDir) === false && fs.mkdirSync(toDir, { recursive: true })
  const globbyList = globby.sync([gitRoot], {})
  let fileList = globbyList.map((file) => {
    const data = require(file)
    const dir = path.parse(file).dir
    // entries.json 地址
    data.from = file
    data.fromDir = dir
    // 原文件地址
    data.resource = decodeURIComponent(data.resource).replace(
      /.*?\/\/\/(.*$)/,
      `$1`
    )
    // 原文件存储目录
    data.resourceDir = path.parse(data.resource).dir
    // 康复后的完好地址
    data.rresource = `${toDir}/${data.resource.replace(/:\//g, `/`)}`
    // 康复后的目录
    data.rresourceDir = `${toDir}/${path
      .parse(data.resource)
      .dir.replace(/:\//g, `/`)}`
    const newItem = [...data.entries].pop()
    // 创立文件所在目录
    fs.mkdirSync(data.rresourceDir, { recursive: true })
    const binary = fs.readFileSync(`${dir}/${newItem.id}`, {
      encoding: `binary`,
    })
    fs.writeFileSync(data.rresource, binary, { encoding: `binary` })
    return data
  })
  const tree = pathToTree(fileList, { key: `resource` })
  return tree
}

为了便利,我还要一个看起来不太丑的操作界面

咱们要把文件树的形式展示出来,还要便利切换。后边决定运用 macos 的文件管理器风格,大概如下。

硬盘坏了,一气之下用 js 写了个恢复程序

假如复原的文件最新的那份不是我想要的,我还能依据时刻线进行比照和挑选

理论上这里应该要做一个像 vscode 比照文件那样,有代码高亮功用,并且把有差异的字符高亮出来。

实际上,这个需求得加钱。

硬盘坏了,一气之下用 js 写了个恢复程序

由于界面是在浏览器里的,需求主动打开,浏览器与体系交互需求一个接口,所以咱们运用 opener 来主动打开浏览器。

运用 get-port 来主动生成接口服务的端口,防止运用时呈现占用。

  const opener = require(`opener`)
  const { portNumbers, default: getPort } = await import(`get-port`)
  const port = await getPort({ port: portNumbers(3000, 3100) })
  const server = express()
  server.listen(port, `0.0.0.0`, () => {
    const link = `http://127.0.0.1:${port}`
    opener(link)
  })

封装成东西,我为人人

理论上我底子不需求什么 UI 界面,也不需求配置,由于我的文件都康复出来了我还花时刻去搞毛线?

实际上,如果别人也有这个康复文件的需求呢?那么他只要运行下面这条指令代码就能立刻康复到当时目录啦!

npx vscode-file-recovery

这便是康复后的文件在硬盘里的姿态啦:

硬盘坏了,一气之下用 js 写了个恢复程序

一切代码位于:

  • github github.com/wll8/vscode…

建议保藏,以备不时之需。/手动狗头