【问题标题】:How to calculate md5 hash of a file using javascript如何使用javascript计算文件的md5哈希
【发布时间】:2010-10-20 13:49:35
【问题描述】:

有没有办法在使用 Javascript 上传到服务器之前计算文件的 MD5 哈希?

【问题讨论】:

标签: javascript md5


【解决方案1】:

虽然有JS implementations的MD5算法,旧版浏览器一般无法从本地文件系统读取文件

我是在 2009 年写的。那么新的浏览器呢?

使用支持FileAPI 的浏览器,您可以读取文件的内容 - 用户必须选择它,或者使用<input>元素或拖放。截至 2013 年 1 月,主要浏览器的排名如下:

怎么做?

查看使用MD5 function of CryptoJSanswer below by Benny Neugebauer

【讨论】:

  • 除了无法在 JS 中访问文件系统之外,我根本不会信任客户端生成的校验和。所以在任何情况下都必须在服务器上生成校验和。
  • @Tomalak 如果您只想在与您已有的不同的情况下上传,也必须在客户端执行此操作。
  • @John 好吧,我的声明并不排除这一点。客户端检查严格是为了用户方便(因此或多或少是可选的,取决于您想要实现的方便程度)。另一方面,服务器端检查是强制性的。
  • pajhome.org.uk/crypt/md5 中的 md5 函数不支持二进制作为输入?我认为有必要在浏览器中计算上传图像的二进制流。谢谢。
  • 如果可以,请在您的答案中添加一些示例代码。这会很有帮助。
【解决方案2】:

我制作了一个实现增量 md5 的库,以便有效地散列大文件。 基本上,您以块的形式读取文件(以保持低内存)并逐步散列它。 您在自述文件中有基本用法和示例。

请注意,您需要 HTML5 FileAPI,因此请务必检查一下。 测试文件夹中有完整的示例。

https://github.com/satazor/SparkMD5

【讨论】:

  • 嘿,这很好用!我尝试了 CryptoJS,但由于某种原因永远无法从中获得准确的 MD5,这就像一个魅力! sha256有什么计划吗? @satazor
  • @cameck,图书馆很好。但是我今天试了一下,似乎.end() 方法有问题。如果您再次调用此方法,那么下次它会给出错误的结果。因为.end() 在内部调用.reset()。这是一场编码灾难,不利于图书馆写作。
  • 感谢图书馆!编写一个最小代码:dev.to/micmo/compute-md5-checksum-for-a-file-in-typescript-59a4
【解决方案3】:

使用MD5 function of CryptoJSHTML5 FileReader API 计算MD5 哈希非常容易。以下代码 sn-p 显示了如何读取二进制数据并从拖入浏览器的图像中计算 MD5 哈希:

var holder = document.getElementById('holder');

holder.ondragover = function() {
  return false;
};

holder.ondragend = function() {
  return false;
};

holder.ondrop = function(event) {
  event.preventDefault();

  var file = event.dataTransfer.files[0];
  var reader = new FileReader();

  reader.onload = function(event) {
    var binary = event.target.result;
    var md5 = CryptoJS.MD5(binary).toString();
    console.log(md5);
  };

  reader.readAsBinaryString(file);
};

我建议添加一些 CSS 以查看拖放区域:

#holder {
  border: 10px dashed #ccc;
  width: 300px;
  height: 300px;
}

#holder.hover {
  border: 10px dashed #333;
}

更多关于拖放功能的信息可以在这里找到:File API & FileReader

我在 Google Chrome 版本 32 中测试了该示例。

【讨论】:

  • 问题是,readAsBinaryString() 还没有被标准化,并且不受 Internet Explorer 的支持。我没有在Edge中测试过,但是连IE11都不支持。
  • @user25163 Internet Explorer(和 Opera Mini)似乎是唯一不支持 readAsBinaryString():caniuse.com/#feat=filereader 的现代浏览器——Microsoft Edge 支持它。
  • 感谢您提供有关 MS Edge 的信息!我为一家公司工作。你知道,客户经常使用旧软件,说服他们更新软件是多么困难。我只是想指出,使用readAsBinaryString() 时必须小心,因为旧版浏览器不支持它。我发现的另一种选择是 SparkMD5。它也使用 FileReader API,但 IE 支持的方法 readAsArrayBuffer。它可以通过分块读取来处理大文件。
  • CryptoJS 现在支持通过以下方式从 ArrayBuffer 转换为 Binary/WordArray:CryptoJS.lib.WordArray.create(arrayBuffer);
  • @WarrenParad 然后如何修改上述代码以使用 ArrayBuffer?啊,在这里找到它:stackoverflow.com/questions/28437181/…
【解决方案4】:

HTML5 + spark-md5Q

假设您使用的是现代浏览器(支持 HTML5 文件 API),以下是计算大文件的 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>

【讨论】:

【解决方案5】:

以下 sn-p 显示了一个示例,它可以在读取和散列文件时归档 400 MB/s 的吞吐量。

它使用了一个名为 hash-wasm 的库,该库基于 WebAssembly,计算哈希的速度比纯 js 库快。截至 2020 年,所有现代浏览器都支持 WebAssembly。

const chunkSize = 64 * 1024 * 1024;
const fileReader = new FileReader();
let hasher = null;

function hashChunk(chunk) {
  return new Promise((resolve, reject) => {
    fileReader.onload = async(e) => {
      const view = new Uint8Array(e.target.result);
      hasher.update(view);
      resolve();
    };

    fileReader.readAsArrayBuffer(chunk);
  });
}

const readFile = async(file) => {
  if (hasher) {
    hasher.init();
  } else {
    hasher = await hashwasm.createMD5();
  }

  const chunkNumber = Math.floor(file.size / chunkSize);

  for (let i = 0; i <= chunkNumber; i++) {
    const chunk = file.slice(
      chunkSize * i,
      Math.min(chunkSize * (i + 1), file.size)
    );
    await hashChunk(chunk);
  }

  const hash = hasher.digest();
  return Promise.resolve(hash);
};

const fileSelector = document.getElementById("file-input");
const resultElement = document.getElementById("result");

fileSelector.addEventListener("change", async(event) => {
  const file = event.target.files[0];

  resultElement.innerHTML = "Loading...";
  const start = Date.now();
  const hash = await readFile(file);
  const end = Date.now();
  const duration = end - start;
  const fileSizeMB = file.size / 1024 / 1024;
  const throughput = fileSizeMB / (duration / 1000);
  resultElement.innerHTML = `
    Hash: ${hash}<br>
    Duration: ${duration} ms<br>
    Throughput: ${throughput.toFixed(2)} MB/s
  `;
});
<script src="https://cdn.jsdelivr.net/npm/hash-wasm"></script>
<!-- defines the global `hashwasm` variable -->

<input type="file" id="file-input">
<div id="result"></div>

【讨论】:

  • 这应该是 2022 年公认的答案。
【解决方案6】:

您需要使用 FileAPI。它在最新的 FF 和 Chrome 中可用,但在 IE9 中不可用。 获取上面建议的任何 md5 JS 实现。我已经尝试过了并放弃了它,因为 JS 太慢了(大图像文件需要几分钟)。如果有人使用类型化数组重写 MD5,可能会重新访问它。

代码看起来像这样:

HTML:     
<input type="file" id="file-dialog" multiple="true" accept="image/*">

JS (w JQuery)

$("#file-dialog").change(function() {
  handleFiles(this.files);
});

function handleFiles(files) {
    for (var i=0; i<files.length; i++) {
        var reader = new FileReader();
        reader.onload = function() {
        var md5 = binl_md5(reader.result, reader.result.length);
            console.log("MD5 is " + md5);
        };
        reader.onerror = function() {
            console.error("Could not read the file");
        };
        reader.readAsBinaryString(files.item(i));
     }
 }

【讨论】:

  • Bendewey 指出的 Webtoolkit MD5 性能要好得多,多 MB 文件需要 16 秒:webtoolkit.info/javascript-md5.html
  • 我已经设法让它工作并且为文本文件生成相同的 md5 哈希 (php: md5_file(...)) 但图像给了我不同的结果?这与二进制数据或上传方式有关吗?
  • 我很确定这段代码不适用于多个文件,因为 onload 是一个回调,reader 变量将是运行 onload 函数时的最后一个文件。跨度>
  • CryptoJS 现在支持通过:CryptoJS.lib.WordArray.create(arrayBuffer); 从 ArrayBuffer 转换为 Binary/WordArray
【解决方案7】:

除了无法获得 JS中的文件系统访问,我不会 完全信任 客户端生成的校验和。所以 在服务器上生成校验和 在任何情况下都是强制性的。 – 托马拉克 2009 年 4 月 20 日 14:05

这在大多数情况下是无用的。您希望在客户端计算 MD5,以便您可以将其与在服务器端重新计算的代码进行比较,如果它们不同,则得出上传错误的结论。我需要在处理大型科学数据文件的应用程序中执行此操作,其中接收未损坏的文件是关键。我的案例很简单,因为用户已经从他们的数据分析工具中计算了 MD5,所以我只需要通过文本字段向他们询问。

【讨论】:

    【解决方案8】:

    要获取文件的哈希值,有很多选项。通常问题是获取大文件的哈希真的很慢。

    我创建了一个小库来获取文件的哈希值,文件开头为 64kb,结尾为 64kb。

    实时示例:http://marcu87.github.com/hashme/ 和库:https://github.com/marcu87/hashme

    【讨论】:

      【解决方案9】:

      互联网上有几个脚本可以创建 MD5 哈希。

      webtoolkit 的那个不错,http://www.webtoolkit.info/javascript-md5.html

      虽然,我不相信它会访问本地文件系统,因为访问是有限的。

      【讨论】:

        【解决方案10】:

        希望您现在已经找到了一个好的解决方案。如果不是,下面的解决方案是基于js-spark-md5的ES6 promise实现

        import SparkMD5 from 'spark-md5';
        
        // Read in chunks of 2MB
        const CHUCK_SIZE = 2097152;
        
        /**
         * Incrementally calculate checksum of a given file based on MD5 algorithm
         */
        export const checksum = (file) =>
          new Promise((resolve, reject) => {
            let currentChunk = 0;
            const chunks = Math.ceil(file.size / CHUCK_SIZE);
            const blobSlice =
              File.prototype.slice ||
              File.prototype.mozSlice ||
              File.prototype.webkitSlice;
            const spark = new SparkMD5.ArrayBuffer();
            const fileReader = new FileReader();
        
            const loadNext = () => {
              const start = currentChunk * CHUCK_SIZE;
              const end =
                start + CHUCK_SIZE >= file.size ? file.size : start + CHUCK_SIZE;
        
              // Selectively read the file and only store part of it in memory.
              // This allows client-side applications to process huge files without the need for huge memory
              fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
            };
        
            fileReader.onload = e => {
              spark.append(e.target.result);
              currentChunk++;
        
              if (currentChunk < chunks) loadNext();
              else resolve(spark.end());
            };
        
            fileReader.onerror = () => {
              return reject('Calculating file checksum failed');
            };
        
            loadNext();
          });
        

        【讨论】:

          【解决方案11】:

          使用当前的 HTML5 应该可以计算二进制文件的 md5 哈希,但我认为之前的步骤是将二进制数据 BlobBuilder 转换为字符串,我正在尝试执行此步骤:但还没有成功了。

          这是我试过的代码:Converting a BlobBuilder to string, in HTML5 Javascript

          【讨论】:

            【解决方案12】:

            我不相信 javascript 有办法访问文件上传的内容。因此,您无法查看文件内容来生成 MD5 和。

            但是,您可以将文件发送到服务器,然后服务器可以将 MD5 和或发送回文件内容.. 但这是很多工作,可能不值得您的目的。

            【讨论】:

              猜你喜欢
              • 2016-12-30
              • 2015-12-18
              • 2012-04-14
              • 1970-01-01
              • 2021-06-19
              • 1970-01-01
              • 1970-01-01
              • 2011-12-08
              • 2012-05-06
              相关资源
              最近更新 更多