递归爬取并以异步方式写入文件
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 中的 basename
和 extname
而不是字符串操作,但鉴于这是一个仅 windows 的程序(工作带驱动器号)可能并不重要。
我对异步代码完全陌生,所以我现在不知所措。
我正在做的是异步递归抓取档案,以便检测给定档案中的文件路径。我要做的是将检测到的所有文件路径写入单个文件。但是,当我执行代码时,它没有将它们正确地写入文件。我假设这是由于多次写入相互重合造成的。
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 中的 basename
和 extname
而不是字符串操作,但鉴于这是一个仅 windows 的程序(工作带驱动器号)可能并不重要。