如何使用javascript计算文件的md5哈希

IT技术 javascript md5
2021-02-01 07:40:42

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

6个回答

虽然有MD5 算法的JS 实现,但较旧的浏览器通常无法从本地文件系统读取文件

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

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

如何?

请参阅下面使用CryptoJSMD5 函数的Benny Neugebauer回答

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

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

请注意,您需要 HTML5 FileAPI,所以一定要检查它。test 文件夹中有一个完整的示例。

https://github.com/satazor/SparkMD5

@cameck,图书馆很好。不过我今天试了一下,好像.end()方法有问题如果再次调用此方法,则下次会给出错误的结果。因为内部.end()调用.reset()这是一场编码灾难,不利于图书馆写作。
2021-03-13 07:40:42
2021-03-14 07:40:42
嘿,这很好用!我尝试了 CryptoJS,但由于某种原因从来没有从中获得准确的 MD5,这就像一个魅力!有 sha256 的计划吗?@satazor
2021-03-27 07:40:42
感谢图书馆!将最小代码放在一起:dev.to/micmo/compute-md5-checksum-for-a-file-in-typescript-59a4
2021-04-03 07:40:42

使用CryptoJSMD5 函数HTML5 FileReader API计算 MD5 哈希值非常容易以下代码片段显示了如何读取二进制数据并从已拖入浏览器的图像中计算 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 中测试了示例。

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

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>

以下代码段显示了一个示例,该示例可以在读取和散列文件时以 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>