递归爬取并以异步方式写入文件

Crawl recursively and write to file in asynchronous manner

我对异步代码完全陌生,所以我现在不知所措。

我正在做的是异步递归抓取档案,以便检测给定档案中的文件路径。我要做的是将检测到的所有文件路径写入单个文件。但是,当我执行代码时,它没有将它们正确地写入文件。我假设这是由于多次写入相互重合造成的。

data.json之前

{
  "K": {
    "files": []
  }
}

data.json 在

之后
{
  "K": {
    "files": [
      {
        "name": "Testing.txt",
        "bytes": 1648,
        "path": "K:\Texts\Testing.txt"
      }
    ]
  }
}      }
    ]
  }
}
}.txt"
      }
    ]
  }
}    }
    ]
  }
}

我显然可以同步编写代码,但为了效率,我更愿意异步完成所有这些工作。尽管如此,我真的不确定解决这个问题的最佳方法。我知道执行此操作的一种方法是等到最后一个文件被抓取并推送(然后将新的 属性 写入文件),但我不知道如何在异步环境中有效地检测到它?我可以经常检查,但我认为这是一种愚蠢的方法。

下面是导致问题的异步代码。

// Scan directories looking for target file types.
async function scanDirs(){
  const
    config = await fsp.readFile('./config.json', 'utf8'),
    archives = JSON.parse(config).archives,
    { join } = require('path'),
    traverse = async (path) => {
      try {
        const stats = await fsp.stat(path)
        if (stats.isDirectory()){
          const childPaths = await fsp.readdir(path)
          for (const childPath of childPaths){
            const
              fullPath = join(path, childPath)
            traverse(fullPath)
          }
        } else if (stats.isFile()) {
          const
            fileTypes = config.fileTypes,
            fileExt = path.substring(path.lastIndexOf('.')+1)
          if (fileTypes.includes(fileExt)){
            const
              data = await fsp.readFile('./data.json', 'utf8'),
              json = JSON.parse(data),
              drive = path.substring(0,1),
              files = json[drive].files,
              stat = await fsp.stat(path),
              newFile = {
                "path": path,
                "name": path.substring(path.lastIndexOf('\')+1),
                "bytes": stat.size
              }
            files.push(newFile)
            fsp.writeFile('./data.json', JSON.stringify(json, null, 2))
          }
        }
      }
      catch (error){
        console.error(error)
      }
    }

  for (const path of archives){
    traverse(path)
  }
}

如有任何帮助,我们将不胜感激。

I know one way to do this would be to wait until the last file is crawled before writing, but I have no idea how to efficiently detect that in an asynchronous environment?

您将使用 Promise.all 等待多个承诺:

const { join } = require('path');
async function searchFiles(path, fileTypes) {
  try {
    const stats = await fsp.stat(path)
    if (stats.isDirectory()){
      const childPaths = await fsp.readdir(path)
      const promises = childPaths.map(childPath =>
        searchFiles(join(path, childPath), fileTypes)
      );
      const results = await Promise.all(promises);
      return [].concat(...results);
    } else if (stats.isFile()) {
      const fileExt = path.substring(path.lastIndexOf('.')+1)
      if (fileTypes.includes(fileExt)) {
        return [{
          "path": path,
          "name": path.substring(path.lastIndexOf('\')+1),
          "bytes": stats.size
        }];
      }
    }
  } catch(e) {
    // ignore. Log?
  }
  return [];
}
async function readJson(path) {
  return JSON.parse(await fsp.readFile(path, 'utf8'));
}

// Scan directories looking for target file types.
async function scanDirs() {
  try {
    const [config, data] = await Promise.all([readJson('./config.json'), readJson('./data.json')]);
    const results = await Promise.all(config.archives.map(path => searchFiles(path, config.fileTypes)));
    for (const newFile of [].concat(...results)) {
      const drive = newFile.path.substring(0,1);
      data[drive].files.push(newFile);
    }
    fsp.writeFile('./data.json', JSON.stringify(data, null, 2));
  } catch (error){
    console.error(error)
  }
}

顺便说一句,您可能想考虑使用 path module 中的 basenameextname 而不是字符串操作,但鉴于这是一个仅 windows 的程序(工作带驱动器号)可能并不重要。