md5 递增地哈希一个大文件?

md5 hash a large file incrementally?

在浏览器中,我使用 JS FileReader().readAsBinaryString() 读取文件。使用 CryptoJS 库,我可以对数据进行 MD5 哈希处理。

这很好用,但我不知道如何处理大文件。例如。仅仅读取一个 2GiB 的文件就会使浏览器崩溃 window。我可以从文件数据中切出 blob 并在进行时对其进行哈希处理,但这不会阻止其他人在不遵循与我相同的步骤的情况下验证相同的哈希值吗?

有没有办法在这种情况下获取大文件的 md5 哈希值?例如,您将如何计算 1TB 文件的 md5 哈希值?我需要以流的形式读取文件吗?

第一次尝试这个,我不知道该怎么做。

这位于 angular 指令中,因此是范围。

var reader = new FileReader();
                reader.onload = function (loadEvent) {
                    scope.$apply(function () {
                        scope.files = changeEvent.target.files;
                        scope.fileread = loadEvent.target.result;
                        scope.md5Data = CryptoJS.MD5(scope.fileread).toString();
                    });
                }
                // First ten megs of the file
                reader.readAsBinaryString((changeEvent.target.files[0]).slice(0, 10 * 1024 * 1024));

I can slice blobs from the file data and hash that as I go but wouldn't this prevent anyone else from verifying the same hash without following the same steps as me?

是的,因此这正是 MD5 算法在其合约中提供的内容:

  1. 你有一个文件
  2. 通过添加单个“1”和多个“0”来填充文件,因此文件可以被 512 整除。
  3. 每一轮计算文件的一片 512 字节的 md5 哈希值,并将其与之前的结果组合。

因此您无需重复这些步骤并确保其他用户也这样做。

由于 MD5 是按块计算的,因此可以进行流式传输,您可以在此处阅读(尽管使用 nodejs 的 crypt 模块完成,它是 googlecode 项目 crypto-js 的模块化端口。):

http://www.hacksparrow.com/how-to-generate-md5-sha1-sha512-sha256-checksum-hashes-in-node-js.html

您可能需要查看 CryptoJS 网站上的段落 progressive hashing

例子:

var sha256 = CryptoJS.algo.SHA256.create();
sha256.update("Message Part 1");
sha256.update("Message Part 2");
sha256.update("Message Part 3");
var hash = sha256.finalize();

SHA256替换为MD5并很快(也重命名变量,我会让你选择一个好名字)。

使用SparkMD5 https://github.com/satazor/SparkMD5

var spark = new SparkMD5(); 
spark.append('Hi');
spark.append('there');
var hexHash = spark.end();

它有一个文件切片示例

使用spark-md5 and Q

由于 none 的其他答案提供了完整的代码段,下面是计算大文件 MD5 哈希值 的方法

function calculateMD5Hash(file, bufferSize) {
  var def = Q.defer();

  var fileReader = new FileReader();
  var fileSlicer = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
  var hashAlgorithm = new SparkMD5();
  var totalParts = Math.ceil(file.size / bufferSize);
  var currentPart = 0;
  var startTime = new Date().getTime();

  fileReader.onload = function(e) {
    currentPart += 1;

    def.notify({
      currentPart: currentPart,
      totalParts: totalParts
    });

    var buffer = e.target.result;
    hashAlgorithm.appendBinary(buffer);

    if (currentPart < totalParts) {
      processNextPart();
      return;
    }

    def.resolve({
      hashResult: hashAlgorithm.end(),
      duration: new Date().getTime() - startTime
    });
  };

  fileReader.onerror = function(e) {
    def.reject(e);
  };

  function processNextPart() {
    var start = currentPart * bufferSize;
    var end = Math.min(start + bufferSize, file.size);
    fileReader.readAsBinaryString(fileSlicer.call(file, start, end));
  }

  processNextPart();
  return def.promise;
}

function calculate() {

  var input = document.getElementById('file');
  if (!input.files.length) {
    return;
  }

  var file = input.files[0];
  var bufferSize = Math.pow(1024, 2) * 10; // 10MB

  calculateMD5Hash(file, bufferSize).then(
    function(result) {
      // Success
      console.log(result);
    },
    function(err) {
      // There was an error,
    },
    function(progress) {
      // We get notified of the progress as it is executed
      console.log(progress.currentPart, 'of', progress.totalParts, 'Total bytes:', progress.currentPart * bufferSize, 'of', progress.totalParts * bufferSize);
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/q.js/1.4.1/q.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/2.0.2/spark-md5.min.js"></script>


<div>
  <input type="file" id="file"/>
  <input type="button" onclick="calculate();" value="Calculate" class="btn primary" />
</div>

用法:

const md5 = await incrementalMD5(file)

incrementalMD5 来源:

import SparkMD5 from 'spark-md5'

export const incrementalMD5 = file =>
  new Promise((resolve, reject) => {
    const fileReader = new FileReader()
    const spark = new SparkMD5.ArrayBuffer()
    const chunkSize = 2097152 // Read in chunks of 2MB
    const chunks = Math.ceil(file.size / chunkSize)
    let currentChunk = 0

    fileReader.onload = event => {
      spark.append(event.target.result) // Append array buffer
      ++currentChunk
      currentChunk < chunks ? loadNext() : resolve(spark.end()) // Compute hash
    }

    fileReader.onerror = () => reject(fileReader.error)

    const loadNext = () => {
      const start = currentChunk * chunkSize
      const end = start + chunkSize >= file.size ? file.size : start + chunkSize
      fileReader.readAsArrayBuffer(File.prototype.slice.call(file, start, end))
    }

    loadNext()
  })