【问题标题】:Calculate a file hash and save the file计算文件哈希并保存文件
【发布时间】:2015-06-15 08:43:23
【问题描述】:

用户将文件上传到我的快速应用程序中。我需要计算上传文件的哈希值,然后使用计算的哈希值作为文件名将文件写入磁盘。我尝试使用以下代码:

function storeFileStream(file, next) {
    createFileHash(file, function(err, hash) {
        if (err) {
            return next(err);
        }

        var fileName = path.join(config.storagePath, hash),
            stream = fs.createWriteStream(fileName);

        stream.on('error', function(err) {
            return next(err);
        });
        stream.on('finish', function() {
            return next();
        });

        file.pipe(stream);
    });
}

function createFileHash(file, next) {
    var hash = crypto.createHash('sha1');
    hash.setEncoding('hex');

    file.on('error', function(err) {
        return next(err);
    });
    file.on('end', function(data) {
        hash.end();
        return next(null, hash.read());
    });

    file.pipe(hash);
}

问题是我计算文件哈希后写入的文件大小为0。解决此任务的最佳方法是什么?

更新 根据@poke 的建议,我尝试复制我的流。现在我的代码是:

function storeFileStream(file, next) {
    var s1 = new pass;
    var s2 = new pass;
    file.pipe(s1);
    file.pipe(s2);        

    createFileHash(s1, function(err, hash) {
        if (err) {
            return next(err);
        }

        var fileName = path.join(config.storagePath, hash),
            stream = fs.createWriteStream(fileName);

        stream.on('error', function(err) {
            return next(err);
        });
        stream.on('finish', function() {
            return next();
        });

        s2.pipe(stream);
    });
}

function createFileHash(file, next) {
    var hash = crypto.createHash('sha1');
    hash.setEncoding('hex');

    file.on('error', function(err) {
        return next(err);
    });
    file.on('end', function(data) {
        hash.end();
        return next(null, hash.read());
    });

    file.pipe(hash);
}

这段代码的问题是没有发出事件endfinish。如果我评论 file.pipe(s2); 会发出事件,但我又遇到了起源问题。

【问题讨论】:

  • 由于您将文件 stream 通过管道传输到散列函数中,因此当您拥有散列并且想要写入文件时,您已经完全耗尽了流。您可以先将文件写入磁盘,然后再次读取以计算哈希,或者您需要预先复制您的流;见this question
  • @poke 谢谢你的评论。第一个选项对我不利,因为我需要先计算哈希。我尝试使用 PassThrough 流复制流并使用 PassThrough 实例而不是 file 参数。但是,现在不会发出“完成”和“结束”事件。

标签: node.js stream


【解决方案1】:

这段代码解决了问题:

var s1 = new passThrough,
    s2 = new passThrough;

file.on('data', function(data) {
    s1.write(data);
    s2.write(data);
});
file.on('end', function() {
    s1.end();
    s2.end();
});

【讨论】:

    【解决方案2】:

    正确简单的方法应该如下:

    我们应该恢复直通的流

    function storeFileStream(file, directory, version, reject, resolve) {
      const fileHashSource = new PassThrough();
      const writeSource = new PassThrough();
      file.pipe(fileHashSource);
      file.pipe(writeSource);
    
      // this is the key point, see https://nodejs.org/api/stream.html#stream_three_states
      fileHashSource.resume();
      writeSource.resume();
    
      createFileHash(fileHashSource, function(err, hash) {
        if (err) {
          return reject(err);
        }
    
        const fileName = path.join(directory, version + '_' + hash.slice(0, 8) + '.zip');
        const writeStream = fs.createWriteStream(fileName);
    
        writeStream.on('error', function(err) {
          return reject(err);
        });
        writeStream.on('finish', function() {
          return resolve();
        });
    
        writeSource.pipe(writeStream);
      });
    }
    
    function createFileHash(readStream, next) {
      const hash = crypto.createHash('sha1');
      hash.setEncoding('hex');
    
      hash.on('error', function(err) {
        return next(err);
      });
      hash.on('finish', function(data) {
        return next(null, hash.read());
      });
    
      readStream.pipe(hash);
    }
    

    【讨论】:

    【解决方案3】:

    您可以使用async 模块(未经测试但应该可以使用):

    async.waterfall([
        function(done) {
            var hash = crypto.createHash('sha1');
            hash.setEncoding('hex');
    
            file.on('error', function(err) {
                done(err);
            });
            file.on('end', function(data) {
                done(null, hash.read);
            });
    
            file.pipe(hash);
    
        },
        function(hash, done) {
            var fileName = path.join(config.storagePath, hash),
                stream = fs.createWriteStream(fileName);
    
            stream.on('error', function(err) {
                done(err);
            });
            stream.on('finish', function() {
                done(null);
            });
    
            file.pipe(stream);
        }
    ], function (err) {
        console.log("Everything is done!");
    });
    

    【讨论】:

    • 我尝试了你的代码,它有同样的问题:保存的文件是空的。实际上,我不知道为什么它可能对我有帮助。从已经管道化的流中读取的问题在这里仍然表现出来,
    猜你喜欢
    • 2011-10-24
    • 2011-11-05
    • 2015-12-18
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    • 2011-12-12
    • 1970-01-01
    • 2013-11-13
    相关资源
    最近更新 更多