JavaScriptを使用してファイルのMD5ハッシュを計算する方法


104

JavaScriptを使用してサーバーにアップロードする前にファイルのMD5ハッシュを計算する方法はありますか?


1
強く関連:[RAMをオーバーフローせずに非常に大きなファイルのチェックサムを生成してJavascriptで64ビットに変換する方法?](stackoverflow.com/q/51987434/514235
iammilind 2018

回答:


92

ありますが、JSの実装 MD5アルゴリズムのは、古いブラウザでは、一般的にローカルのファイルシステムからファイルを読み取ることができません

2009年にそれを書きました。では、新しいブラウザはどうですか?

FileAPIをサポートするブラウザを使用すると、ファイルの内容を*読み取ることができます* -ユーザーは<input>要素またはドラッグアンドドロップでファイルを選択する必要があります。2013年1月の時点で、主要なブラウザーは次のようになっています。


30
JSでファイルシステムアクセスを取得することは不可能であることを除けば、クライアントが生成したチェックサムにはまったく信頼を置きません。そのため、サーバーでのチェックサムの生成は、いずれの場合でも必須です。
Tomalak 2009

4
@Tomalakすでに持っているものと異なる場合にのみアップロードしたい場合は、クライアントで実行することも必須です。
ジョン

2
@ジョンまあ、私の声明はこれを除外していません。クライアント側のチェックは厳密にユーザーの便宜のためです(したがって、どの程度便利にするかによって、多かれ少なかれオプションになります)。一方、サーバー側のチェックは必須です。
Tomalak 2016

pajhome.org.uk/crypt/md5の md5関数は、入力としてバイナリをサポートしていませんか?ブラウザでアップロードした画像のバイナリストリームを計算する必要があると思います。ありがとうございました。
jiajianrong 2018

可能であれば、サンプルコードを回答に追加してください。それは大いに役立ちます。
cbdeveloper

30

大きなファイルを効率的にハッシュするために、増分md5を実装するライブラリを作成しました。基本的には、ファイルを(メモリを低く保つために)チャンクで読み取り、増分的にハッシュします。Readmeで基本的な使用法と例を入手しました。

HTML5 FileAPIが必要なので、必ず確認してください。testフォルダーに完全な例があります。

https://github.com/satazor/SparkMD5


@Biswaここが私の実装です。gist.github.com/marlocorridor/3e6484ae5a646bd7c625
marlo

1
こんにちは!私はCryptoJSを試しましたが、何らかの理由で正確なMD5を取得できませんでした。これは魅力のように機能します。sha256の計画は?@satazor
cameck 2017

@cameck、ライブラリはいいです。しかし、今日試してみたところ、.end()メソッドに問題があるようです。このメソッドを再度呼び出すと、次回は誤った結果が返されます。内部で.end()呼び出すため.reset()。これはコーディングの問題であり、ライブラリの作成には適していません。
iammilind 2018

ライブラリをありがとう!最小限のコードをまとめます:dev.to/micmo/compute-md5-checksum-for-a-file-in-typescript-59a4
Qortex

27

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;
}

ドラッグアンドドロップ機能の詳細については、こちらをご覧ください:ファイルAPIおよびFileReader

Google Chromeバージョン32でサンプルをテストしました。


2
問題は、readAsBinaryString()標準化されておらず、Internet Explorerでサポートされていないことです。Edgeではテストしていませんが、IE11でもサポートされていません。
StanE 2016

@ user25163 Internet Explorer(およびOpera Mini)は、サポートされていない最新のブラウザーであるようですreadAsBinaryString()caniuse.com/#feat=filereader — Microsoft Edgeがサポートしています。
Benny Neugebauer、2016

MS Edgeに関する情報をありがとう!私は会社で働いています。そして、ご存知のように、顧客は古いソフトウェアを使用することが多く、ソフトウェアを更新するように説得するのがどれほど難しいかを知っています。readAsBinaryString()古いブラウザではサポートされていないため、使用には注意が必要です。私が見つけた代替案はSparkMD5です。FileReader APIも使用しますが、readAsArrayBufferIEでサポートされているメソッドです。そして、それはチャンクでそれらを読むことによって巨大なファイルを扱うことができます。
StanE 2016

2
CryptoJSがArrayBufferからBinary / WordArrayへの変換をサポートするようになりました:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

@WarrenParad次に、上記のコードをArrayBufferで動作するように変更するにはどうすればよいでしょうか。ああ、ここでそれを見つけた:stackoverflow.com/questions/28437181/...
TheStoryCoder

9

HTML5 + spark-md5およびQ

(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>


8

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
Aleksandar Totic '20

1
私はこれをうまく機能させることができ、同じmd5ハッシュがテキストファイルに対して(php:md5_file(...))を生成していますが、画像によって異なる結果が得られますか?これはバイナリデータまたはそのアップロード方法と関係がありますか?

onloadはコールバックであるため、このコードは複数のファイルで動作しないと確信していますreader。onload関数が実行されるまでに、変数は最後のファイルになります。
デイブ

CryptoJSがArrayBufferからBinary / WordArrayへの変換をサポートするようになりました:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

4

JSでファイルシステムアクセスを取得することは不可能であることを除けば、クライアントが生成したチェックサムにはまったく信頼を置きません。そのため、サーバーでのチェックサムの生成は、いずれの場合でも必須です。– Tomalak 2009年4月20日14:05

これはほとんどの場合役に立たない。MD5をクライアント側で計算して、サーバー側で再計算されたコードと比較して、異なる場合はアップロードが失敗したと結論付けることができます。科学データの大きなファイルを扱うアプリケーションでは、破損していないファイルを受信することが重要であったため、これを行う必要がありました。私のケースは単純でした。ユーザーがデータ分析ツールからMD5を既に計算していたため、テキストフィールドでそれを尋ねるだけで済みました。


3

ファイルのハッシュを取得するには、多くのオプションがあります。通常、問題は大きなファイルのハッシュを取得するのが本当に遅いということです。

私はファイルのハッシュを取得する小さなライブラリを作成しました。ファイルの先頭64kbと末尾64kbです。

ライブの例:http : //marcu87.github.com/hashme/およびライブラリ:https : //github.com/marcu87/hashme


2

MD5ハッシュを作成するためのスクリプトがインターネット上にいくつかあります。

webtoolkitからのものは良いです、http: //www.webtoolkit.info/javascript-md5.html

ただし、アクセスが制限されているため、ローカルファイルシステムにアクセスできるとは思いません。


1

あなたが今までに良い解決策を見つけたことを願っています。そうでない場合、以下のソリューションは、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();
  });

1

次のスニペットは、ファイルの読み取りとハッシュ中に400 MB /秒のスループットをアーカイブできる例を示しています。

これは、WebAssemblyに基づいており、jsのみのライブラリよりも高速にハッシュを計算するhash -wasmというライブラリを使用しています。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>



-1

JavaScriptにファイルアップロードのコンテンツにアクセスする方法があるとは思いません。したがって、ファイルの内容を見てMD5サムを生成することはできません。

ただし、サーバーにファイルを送信することはできます。サーバーはMD5サムを送信したり、ファイルの内容を送信したりできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.