如何检查两个文件是否具有相同的内容?

37

我正在使用mocha/supertest/should.js来测试REST服务。

GET /files/<hash>以流的形式返回文件。

如何在should.js中断言文件内容是相同的?

it('should return file as stream', function (done) {
    var writeStream = fs.createWriteStream('test/fixtures/tmp.json');
    
    var req = api.get('/files/676dfg1430af3595');
    req.on('end', function(){
       var tmpBuf = fs.readFileSync('test/fixtures/tmp.json');
       var testBuf = fs.readFileSync('test/fixtures/test.json');
    
       // How to assert with should.js file contents are the same (tmpBuf == testBuf )
       // ...
    
       done();
    });
});
6个回答

54

令人惊讶的是,没有人建议使用Buffer.equals。它似乎是最快和最简单的方法,并且自v0.11以来一直存在。

因此,您的代码将变为tmpBuf.equals(testBuf)


最好的解决方案也是最简单的。 - hellboy
但是如果文件大小超过了RAM大小呢? - Nikolay Makhonin

4
您有3种解决方案: 第一种:
比较结果字符串。
tmpBuf.toString() === testBuf.toString();

第二步:

使用循环逐字节读取缓冲区

var index = 0,
    length = tmpBuf.length,
    match = true;

while (index < length) {
    if (tmpBuf[index] === testBuf[index]) {
        index++;
    } else {
        match = false;
        break;
    }
}

match; // true -> contents are the same, false -> otherwise

第三步:

使用类似buffertools这样的第三方模块,并使用buffertools.compare(buffer, buffer|string)方法。


4
should.js中,您可以使用.eql来比较缓冲区实例:
> var buf1 = new Buffer('abc');
undefined
> var buf2 = new Buffer('abc');
undefined
> var buf3 = new Buffer('dsfg');
undefined
> buf1.should.be.eql(buf1)
...
> buf1.should.be.eql(buf2)
...
> buf1.should.be.eql(buf3)
AssertionError: expected <Buffer 61 62 63> to equal <Buffer 64 73 66 67>
    ...
> 

3
使用file-comparenode-temp来解决问题的方案:
it('should return test2.json as a stream', function (done) {
    var writeStream = temp.createWriteStream();
    temp.track();

    var req = api.get('/files/7386afde8992');

    req.on('end', function() {
        comparator.compare(writeStream.path, TEST2_JSON_FILE, function(result, err) {
            if (err) {
                return done(err);
            }

            result.should.true;
            done();
        });
    });

    req.pipe(writeStream);
});

3

当我们需要比较大型文件(例如图片)时,在进行文件上传的断言时,直接使用should.eql比较缓冲区或字符串的速度非常慢。我建议使用crypto模块来断言缓冲区哈希值:

const buf1Hash = crypto.createHash('sha256').update(buf1).digest();
const buf2Hash = crypto.createHash('sha256').update(buf2).digest();
buf1Hash.should.eql(buf2Hash);

一个更简单的方法是这样断言缓冲区长度:
buf1.length.should.eql(buf2.length)

如果你不想使用shouldjs作为断言模块,你当然可以使用其他工具。


4
请不要使用哈希函数。由于鸽笼原理,哈希值通常但并非总是唯一的。此外,生成哈希值所需的计算成本远高于简单比较原始内容的成本。请注意不改变原文意思并使翻译更加易懂。 - Jack G
6
伙计,sha256是一种具有密码学安全性的哈希函数。具有相同哈希值的2个不同文件的概率非常小,以至于提到它就显得荒谬了。 - zarak
3
加密库必须遍历所有字节以计算哈希值。如果您已经将两个缓冲区完全保存在内存中,则计算它们的哈希值没有意义。直接比较它们的内容不仅更易于阅读,而且更快(因为您可以跳过哈希计算)。只有当文件太大无法将其保存在内存中以运行直接比较时,哈希才有用。 - Lucio Paiva

1
我认为在JavaScript中使用非阻塞调用可以获得更好的性能,至少可以防止阻塞其他操作:
阻塞是指在Node.js进程中执行额外的JavaScript必须等待非JavaScript操作完成。这是因为当发生阻塞操作时,事件循环无法继续运行JavaScript。
在Node.js中,由于CPU密集而不是等待非JavaScript操作(如I/O)而表现出较差性能的JavaScript通常不被称为阻塞。使用libuv的同步方法是Node.js标准库中最常用的阻塞操作。本地模块也可能有阻塞方法。
因此,我会将Sync调用更改为以下代码。此外,我会使用Max建议的equals方法来比较两个文件:
const fs = require('fs')

fs.readFile('file1', (err, data1) => {
    if (err) throw err;
    fs.readFile('file2', (err, data2) => {
        if (err) throw err;
        if (data1.equals(data2)) {
            console.log('EQUAL')
        } else {
            console.log('NON EQUAL')
        }

    });
});

虽然对于一个小型的单一脚本来说,结果几乎相同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接