【问题标题】:Fast file hashing of large files大文件的快速文件散列
【发布时间】:2014-01-04 06:19:36
【问题描述】:

我正在使用sjcl 在客户端对文件进行哈希处理,以便在开始完整上传之前检查它们是否存在于服务器上。

但是,它似乎有点慢。散列一个 8 MB 的文件大约需要 15 秒。我不确定这是因为库速度慢,JavaScript 速度慢,还是算法本身很慢。它使用的是sha256,这对于我需要的东西可能有点矫枉过正。速度是关键——加密安全和冲突并不是特别重要。

有更快的方法吗?

$(document).on('drop', function(dropEvent) {
    dropEvent.preventDefault();
    _.each(dropEvent.originalEvent.dataTransfer.files, function(file) {
        var reader = new FileReader();
        var pos = 0;
        var startTime = +new Date();

        var hashObj = new sjcl.hash.sha256();

        reader.onprogress = function(progress) {
            var chunk = new Uint8Array(reader.result).subarray(pos, progress.loaded);
            hashObj.update(chunk);
            pos = progress.loaded;

            if(progress.lengthComputable) {
                console.log((progress.loaded/progress.total*100).toFixed(1)+'%');
            }
        };

        reader.onload = function() {
            var endTime = +new Date();
            console.log('hashed',file.name,'in',endTime-startTime,'ms');
            var chunk = new Uint8Array(reader.result, pos);
            if(chunk.length > 0) hashObj.update(chunk);
            console.log(sjcl.codec.hex.fromBits(hashObj.finalize()));
        };

        reader.readAsArrayBuffer(file);
    });
});

编辑:刚刚根据this answer 发现SparkMD5。对于同一个 8 MB 文件,初始测试可以在不到一秒的时间内运行,但它仍然比我想要的慢。

【问题讨论】:

  • xxHash 宣传的速度相当惊人。
  • @JasonLeBrun:我现在正在尝试xxHash。它不会将ArrayBuffer 作为输入,这可能有问题。

标签: javascript performance hash


【解决方案1】:

xxHash 提供 32 位散列。它似乎比 SparkMD5 快 30% 左右。但是,它似乎不适用于 HTML5 的 ArrayBuffer,因此必须将文件作为文本读取。

var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
var chunkSize = 1024 * 1024 * 2;

$(document).on('drop', function (dropEvent) {
    dropEvent.preventDefault();

    _.each(dropEvent.originalEvent.dataTransfer.files, function (file) {
        var startTime = +new Date(), elapsed;
        var chunks = Math.ceil(file.size / chunkSize);
        var currentChunk = 0;
        var xxh = XXH();
        var fileReader = new FileReader();

        var readNextChunk = function() {
            var start = currentChunk * chunkSize;
            var end = Math.min(start + chunkSize, file.size);

            fileReader.readAsText(blobSlice.call(file, start, end));
        };

        fileReader.onload = function (e) {
            console.log("read chunk nr", currentChunk + 1, "of", chunks);
            xxh.update(e.target.result);
            ++currentChunk;

            if (currentChunk < chunks) {
                readNextChunk();
            } else {
                elapsed = +new Date() - startTime;
                console.info("computed hash", xxh.digest().toString(16), 'for file', file.name, 'in', elapsed, 'ms');
            }
        };

        fileReader.onerror = function () {
            console.warn("oops, something went wrong.");
        };

        readNextChunk();
    });
});

我认为blobSlice 会复制该文件,我不是很喜欢。我也不特别喜欢将二进制数据视为文本。我通过挖掘xxHash 的源代码创建了这个与ArrayBuffer API 一起使用的替代版本——结果发现只有一种方法可以让HTML5's Uint8Array 像Node.js Buffer 一样工作。

/**
 * Hack to make Uint8Array work like a Node.js Buffer
 *
 * @param {Buffer} targetBuffer Buffer to copy into
 * @param {Number} targetStart Optional, Default: 0
 * @param {Number} sourceStart Optional, Default: 0
 * @param {Number} sourceEnd Optional, Default: source length
 * @see http://nodejs.org/api/buffer.html#buffer_buf_copy_targetbuffer_targetstart_sourcestart_sourceend
 * @see https://developer.mozilla.org/en-US/docs/Web/API/Uint32Array
 */
Uint8Array.prototype.copy = function(targetBuffer, targetStart, sourceStart, sourceEnd) {
    targetStart = targetStart || 0;
    sourceStart = sourceStart || 0;
    sourceEnd = sourceEnd || this.length;
    for(var i=sourceStart; i<sourceEnd; ++i) {
        targetBuffer[targetStart+i] = this[i];
    }
};

$(document).on('drop', function(dropEvent) {
    dropEvent.preventDefault();
    _.each(dropEvent.originalEvent.dataTransfer.files, function(file) {
        var reader = new FileReader();
        var pos = 0;
        var startTime = +new Date();
        var xxh = XXH();

        reader.onprogress = function(progress) {
            var length = progress.loaded - pos;
            var arr = new Uint8Array(reader.result, pos, length);
            pos += length;

            xxh.update(arr);

            if(progress.lengthComputable) {
                console.log((progress.loaded/progress.total*100).toFixed(1)+'%');
            }
        };

        reader.onload = function() {
            var arr = new Uint8Array(reader.result, pos);
            xxh.update(arr);

            var elapsed = +new Date() - startTime;
            console.info("computed hash", xxh.digest().toString(16), 'for file', file.name, 'in', elapsed, 'ms');
        };

        reader.readAsArrayBuffer(file);
    });
});

不幸的是,它们在速度方面几乎相同,并且仍在复制。但是,它在原始 8 MB 文件上运行大约 270 毫秒,这比 15 秒要好得多。

【讨论】:

    【解决方案2】:

    SparkMD5 比较快:

    var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
    var chunkSize = 1024 * 1024 * 2;
    
    $(document).on('drop', function (dropEvent) {
        dropEvent.preventDefault();
    
        _.each(dropEvent.originalEvent.dataTransfer.files, function (file) {
            var startTime = +new Date(), elapsed;
            var chunks = Math.ceil(file.size / chunkSize);
            var currentChunk = 0;
            var spark = new SparkMD5.ArrayBuffer();
            var fileReader = new FileReader();
    
            var readNextChunk = function() {
                var start = currentChunk * chunkSize;
                var end = Math.min(start + chunkSize, file.size);
    
                fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
            };
    
            fileReader.onload = function (e) {
                console.log("read chunk nr", currentChunk + 1, "of", chunks);
                spark.append(e.target.result);                 // append array buffer
                ++currentChunk;
    
                if (currentChunk < chunks) {
                    readNextChunk();
                } else {
                    elapsed = +new Date() - startTime;
                    console.info("computed hash", spark.end(), 'for file', file.name, 'in', elapsed, 'ms'); // compute hash
                }
            };
    
            fileReader.onerror = function () {
                console.warn("oops, something went wrong.");
            };
    
            readNextChunk();
        });
    });
    

    【讨论】:

      猜你喜欢
      • 2015-09-04
      • 1970-01-01
      • 2010-11-28
      • 2023-03-17
      • 2011-02-25
      • 1970-01-01
      • 2015-05-03
      • 1970-01-01
      • 2011-07-28
      相关资源
      最近更新 更多