【问题标题】:Checksum of large files in nodejsnodejs中大文件的校验和
【发布时间】:2018-02-06 14:19:41
【问题描述】:

我正在编写一个脚本来建立我的图片数据库。我有一个可以工作的脚本。它在 9 分 24 秒内浏览了一个包含 5,670 个文件的文件夹,总计 13.08 GB 的数据。然后我在更新、更大的照片上尝试它,执行似乎急剧下降。 20 分钟内只计算了一个文件夹中三个小预览文件的哈希值,共有 431 个文件,总计 7.58 GB。

我做错了什么?

var fs = require('fs')
var crypto = require('crypto')
var util = require('util')
var p = require('path')
var sqlite3 = require('sqlite3').verbose()
var db = new sqlite3.Database('./sqlite.db')
const hash_algorithm = 'sha256'

var fileCount = 0

function getFiles(directory) {
    fs.readdir(directory, function(err, files) {
        for (var i in files) {
            var filepath = directory + '/' + files[i]
            fileStat(filepath)
        }
    })
}

function fileStat(filepath) {
    fs.stat(filepath, function(err, stats) {
        if (stats.isDirectory()) {
            getFiles(filepath)
        } else {
            computeHash(filepath, hash_algorithm, function(err, hash) {
                if (err) {
                    throw err
                }
                insertStat(filepath, hash, stats.size)
            })
        }
    })
}

function computeHash(filepath, algorithm, callback) {
    var hash = crypto.createHash(algorithm)
    var rs = fs.createReadStream(filepath)

    rs.on('open', function() {})

    rs.on('error', function(err) {
        throw err
    })

    rs.on('data', function(chunk) {
        hash.update(chunk)
    })

    rs.on('end', function() {
        hash = hash.digest('hex')
        return callback(null, hash)
    })
}

function getExif(filepath, callback) {

}

function insertStat(filepath, hash, size) {
    var sql = "INSERT INTO files VALUES ($filename, $path, $hash, $size)"
    var filename = filepath.split('/')
    filename = filename[filename.length - 1]
    db.run(sql, {$filename: filename, $path: filepath, $hash: hash, $size: size})
    if (verbose) console.log('%s: %s', ++fileCount, filepath)
}

db.serialize(function() {
    db.run('CREATE TABLE files (filename text, path text, hash text, size integer)')
})

var verbose = true
var path = process.argv[2] || '.'
path = p.resolve(path)

if (verbose) console.log('path: %s', path)
getFiles(path)

【问题讨论】:

    标签: node.js checksum sha256


    【解决方案1】:

    您的所有过程都是异步的。虽然在 javascript 中这是一种很好的做法,但您应该控制自己的内存消耗:

    1. 您开始使用fs.stat 异步打开文件。这意味着您的所有文件。

    2. 然后您使用缓冲区将它们加载到内存中,但在它们完全加载并点击on('end',..) 之前您无法开始处理它们。这意味着您的所有文件都在竞争完全加载到您的 RAM 中。

    明白了吗?你的内存使用率是 100%,你必须希望一个文件被完全加载和处理,以便为另一个文件释放一些内存。那就是你做错了。

    因此,您需要重新控制内存使用。理想情况下,您应该控制一次处理多少个文件。作为一个快速修复,我建议你让它与fs.statSync同步。


    附注

    您的流程还涉及数据库。这是性能的通常嫌疑人。您的代码必须记录任何数据库错误。在这里,我没有看到潜在的死锁或完全扫描。所以不用担心。只需确保在开始插入之前创建表 files

    切勿使用for..in 循环进入数组。请改用array.forEach()

    请在您的代码中使用半列;。是的,大部分时间都可以不用 JavaScript,但它可以避免奇怪的错误并减轻解释器的工作。

    【讨论】:

    • 由于 fs.readdir 和 computeHash 函数都是异步的,使用 fs.statSync 而不是 fs.stat 将如何解决问题?我可以使用 fs.readdirSync 和 fs.statSync。但是,我不明白如何同步实现文件读取和散列部分。
    • 公平点。您还可以将fs.createReadStream 替换为fs.readFileSync。无论如何,在您的情况下读取缓冲区没有意义:在处理之前您需要完整的文件。 fs.readdir 可以保持异步。
    • 谢谢。有了你的输入,我现在有一些工作代码。但是,我不明白您所说的使用读取缓冲区没有意义。当遇到大文件时,我不想在计算哈希之前加载整个文件。我想我需要以某种方式读取流阻塞。 Hash.update() 适用于流。
    • 我宁愿改用for。当然你必须知道你正在使用一个数组。 forEach()much slower 而不是 for (可能是由于方法调用)。
    • @SebastianBarth 如果您喜欢for in,您可以随时使用其较新的替代品for of
    猜你喜欢
    • 2015-04-30
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 2017-08-13
    • 1970-01-01
    相关资源
    最近更新 更多