Node.js 查找具有相同内容异步的文件

Node.js find a file with the same content async

目标:给定输入文件路径和文件路径数组,我需要检查数组中是否存在与输入文件相同的文件(即使名称不同)。我需要异步读取文件并在找到相同文件时停止(在这种情况下我不想读取数组中的所有文件)。

我想只使用 ecma6 功能而不使用额外的库。 我正在使用 node.js,因此比较文件足以使用 compare buffers function:

const fs = require("fs");

let buffer1 = fs.readFileSync(filePath1);
let buffer2 = fs.readFileSync(filePath2);
if (buffer1.compare(buffer2) === 0) {
    console.log("Files are equal");  
} else {
    console.log("Files are different");  
}

基本上,对于数组中的每个文件路径,我想使用如下函数顺序检查相等性(但使用异步读取):

function isFileEqual (fileInputBuffer, path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, (err, data) => {
            if (err) {
                resolve(false);
            }

            if (data.compare(fileInputBuffer) === 0) {                  
                resolve(true);
            } else {
                resolve(false);
            }   
        });     
    });
}

我想出了一个解决方案,利用了 Promises.all 的 "reject fast" 属性,这会导致立即拒绝,以防任何输入承诺在不等待其他输入的情况下被拒绝承诺完成:

const fs = require("fs");

let paths = ["file2", "file3", "file1_copy", "file4", "file5"];


checkIfFileAlreadyExistsAsync("file1", paths).then(path => {
    console.log("\n", "File equal found at: ", path, "\n");
}).catch(err => {
    console.log(err);
});



function checkIfFileAlreadyExistsAsync(filePath, paths) {

    return new Promise( (rootResolve, rootReject) => {

        fs.readFile(filePath, (err, inputBuffer) => {

            if (err) {
                rootReject(err);
                return;
            }


            function isFileEqual(path) {
                return new Promise((resolve, reject) => {
                    fs.readFile(path, (err, data) => {

                        console.log("[isFileEqual]", path);

                        if (err) {
                            resolve();
                        }                                       
                        else if (data.compare(inputBuffer) === 0) {                     
                            reject(path);   // file equal found, reject fast!
                        } else {
                            resolve();
                        }
                    });
                });     
            }



            let promises = [];

            // fill promises array
            paths.forEach(path => {
                promises.push(isFileEqual(path));
            })



            Promise.all(promises).then(values => {

                rootReject(false);

            })
            .catch((path) => {

                // use reject fast to resolve without wait the other promises to complete   
                rootResolve(path);

            });

        });

    });
}

以上脚本的输出:

[isFileEqual] file2
[isFileEqual] file1_copy

 File equal found at:  file1_copy

[isFileEqual] file4
[isFileEqual] file3
[isFileEqual] file5

上述解决方案有效,但如您所见,存在一个问题:无论是否已找到相同的文件,始终读取所有文件。

我以前不知道,Promise一创建就执行(我倒是相反,为什么要这样实现?),所以我想使用像这样的promise工厂以下:

function promiseFactory(path) {
    return function () {
        return new Promise((resolve, reject) => {
            fs.readFile(path, (err, data) => {

                console.log("[isFileEqual]", path);

                if (err) {
                    reject();
                }                                       
                else if (data.compare(inputBuffer) === 0) {                     
                    resolve(true);  
                } else {
                    resolve(false);
                }
            });
        });
    };
}

并尝试按顺序 运行 承诺。但是我该怎么做呢?或者有其他方法吗?

Promise.race

Promise.race 是一个有趣的函数——不是等待所有承诺被解决或拒绝,Promise.race 在数组中的任何承诺被解决或拒绝时立即触发:

var req1 = new Promise(function(resolve, reject) { 
    // A mock async action using setTimeout
    setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) { 
    // A mock async action using setTimeout
    setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
    console.log('Then: ', one);
}).catch(function(one, two) {
    console.log('Catch: ', one);
});

// 从控制台: // 那么:第二个!

看看这个,相信对你有帮助

我找到了一个使用递归的解决方案。基本上我创建了一个工厂数组(每个工厂 returns 一个函数 returns 一个 Promise)然后使用该数组作为递归函数的输入。如果找到相同的文件,则递归函数解析主 Promise(由 main 函数返回的那个),否则它递归地调用自己,输入工厂数组和下一个要创建的承诺的索引。

const fs = require("fs");
let paths = ["file2", "file3", "file1_copy", "file4", "file5"];


checkIfFileAlreadyExistsAsync("file1", paths).then(result => {
    console.log("SUCCESS", result);
}).catch(err => {
    console.log("FAIL", err);
});


function checkIfFileAlreadyExistsAsync(filePath, paths) {

    return new Promise((rootResolve, rootReject) => {   

        fs.readFile(filePath, (err, inputBuffer) => {

            if (err) {
                rootReject(err);
            }



            function compareFilePromiseFactory(path) {
                return function() {
                    return new Promise((resolve, reject) => {
                        fs.readFile(path, (err, data) => {

                            console.log("Compare file: ", path, "\n");  

                            if (err) {
                                resolve(false);
                            }
                            else if(data.compare(inputBuffer) === 0) {
                                resolve(true);
                            }
                            else {
                                resolve(false);
                            }
                        });
                    });
                }
            }



            let factories = [];
            paths.forEach(path => {
                factories.push(compareFilePromiseFactory(path));
            });




            function findFile(factories, index) {               

                if (index == factories.length) {
                    rootReject(false);      
                    return;         
                }

                factories[index]().then(result => {                 

                    if (result) {
                        rootResolve(true);                      
                    }
                    else {                      
                        findFile(factories, index + 1);
                    }
                }); 
            }


            findFile(factories, 0);


        }); 
    });     
}

以上脚本给出以下输出:

Compare file:  file2

Compare file:  file3

Compare file:  file1_copy

SUCCESS true

每个 promise 都是按顺序创建的,但都是异步的。一旦找到相同的文件,它就会停止生成承诺。 不知道是否也可以迭代解决,你觉得呢?