Сканирование рекурсивно и запись в файл асинхронным способом - PullRequest
0 голосов
/ 02 ноября 2019

Я совершенно новичок в асинхронном коде, поэтому сейчас у меня над головой.

Я занимаюсь рекурсивным сканированием архива в асинхронном режиме, чтобы обнаружить пути к файлам в заданномархив. То, что я пытаюсь сделать, - это записать все пути к файлам по мере их обнаружения в один файл. Однако когда я выполняю код, он не записывает их в файл должным образом. Я предполагаю, что это происходит из-за нескольких записей, совпадающих друг с другом.

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)
  }
}

Любая помощь будет принята с благодарностью.

1 Ответ

1 голос
/ 02 ноября 2019

Я знаю, что один из способов сделать это - подождать, пока последний файл не будет сканирован перед записью, но я не знаю, как эффективно обнаружить это в асинхронной среде?

Вы бы использовали 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)
  }
}

Кстати, вы можете рассмотреть возможность использования basename и extname из модуля path вместо манипуляции со строками, но, учитывая, что это программа только для Windows (работающая с буквами дисков), это, вероятно, не имеет большого значения.

...