So berechnen Sie den MD5-Hash einer Datei mit Javascript

104

Gibt es eine Möglichkeit, den MD5-Hash einer Datei vor dem Hochladen auf den Server mit Javascript zu berechnen?

LuRsT
quelle
1
Stark verwandt: [Wie wird eine Prüfsumme generiert und in Javascript für sehr große Dateien in 64-Bit konvertiert, ohne dass RAM überfüllt wird? ] ( stackoverflow.com/q/51987434/514235 )
iammilind

Antworten:

92

Während es JS-Implementierungen des MD5-Algorithmus gibt, können ältere Browser im Allgemeinen keine Dateien aus dem lokalen Dateisystem lesen .

Ich habe das 2009 geschrieben. Was ist also mit neuen Browsern?

Mit einem Browser, der die FileAPI unterstützt , können Sie den Inhalt einer Datei * lesen * - der Benutzer muss ihn entweder mit einem <input>Element oder per Drag & Drop ausgewählt haben. Ab Januar 2013 stapeln sich die wichtigsten Browser folgendermaßen:

Paul Dixon
quelle
30
Abgesehen von der Unmöglichkeit, Dateisystemzugriff in JS zu erhalten, würde ich einer vom Client generierten Prüfsumme überhaupt kein Vertrauen schenken. Das Generieren der Prüfsumme auf dem Server ist daher in jedem Fall obligatorisch.
Tomalak
4
@Tomalak Es ist auch obligatorisch, dies auf dem Client zu tun, wenn Sie es nur hochladen möchten, wenn es anders ist als das, was Sie bereits haben.
John
2
@ John Nun, meine Aussage schließt dies nicht aus. Clientseitige Überprüfungen dienen ausschließlich der Benutzerfreundlichkeit (und sind daher mehr oder weniger optional, je nachdem, wie bequem Sie sie durchführen möchten). Serverseitige Überprüfungen sind dagegen obligatorisch.
Tomalak
md5-Funktion in pajhome.org.uk/crypt/md5 unterstützt keine Binärdatei als Eingabe? Ich denke, es ist notwendig, den Binärstrom für ein hochgeladenes Bild im Browser zu berechnen. Danke dir.
Jiajianrong
Wenn Sie können, fügen Sie Ihrer Antwort einen Beispielcode hinzu. Es würde viel helfen.
Entwickler
30

Ich habe eine Bibliothek erstellt, die inkrementelles md5 implementiert, um große Dateien effizient zu hashen. Grundsätzlich lesen Sie eine Datei in Blöcken (um den Speicher niedrig zu halten) und hashen sie schrittweise. Grundlegende Verwendung und Beispiele finden Sie in der Readme-Datei.

Beachten Sie, dass Sie HTML5 FileAPI benötigen. Überprüfen Sie dies daher unbedingt. Im Testordner befindet sich ein vollständiges Beispiel.

https://github.com/satazor/SparkMD5

Satazor
quelle
@ Biswa hier ist meine Implementierung. gist.github.com/marlocorridor/3e6484ae5a646bd7c625
Marlo
1
Hey das funktioniert super! Ich habe CryptoJS ausprobiert und konnte aus irgendeinem Grund nie ein genaues MD5 daraus machen. Das funktioniert wie ein Zauber! Pläne für sha256? @ Satazor
Cameck
@cameck, die Bibliothek ist gut. Allerdings habe ich es heute versucht und es scheint, dass es ein Problem mit der .end()Methode gibt. Wenn Sie diese Methode erneut aufrufen, führt dies beim nächsten Mal zu einem falschen Ergebnis. Da .end()Anrufe .reset()intern. Dies ist eine Codierungskatastrophe und nicht gut für das Schreiben von Bibliotheken.
Iammilind
Danke für die Bibliothek! Stellen
Qortex
27

Es ist ziemlich einfach, den MD5-Hash mit der MD5-Funktion von CryptoJS und der HTML5 FileReader-API zu berechnen . Das folgende Codefragment zeigt, wie Sie die Binärdaten lesen und den MD5-Hash aus einem Bild berechnen können, das in Ihren Browser gezogen wurde:

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

Ich empfehle, CSS hinzuzufügen, um den Drag & Drop-Bereich anzuzeigen:

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

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

Weitere Informationen zur Drag & Drop-Funktionalität finden Sie hier: File API & FileReader

Ich habe das Beispiel in Google Chrome Version 32 getestet.

Benny Neugebauer
quelle
2
Das Problem ist, dass dies readAsBinaryString()nicht standardisiert wurde und von Internet Explorer nicht unterstützt wird. Ich habe es nicht in Edge getestet, aber selbst IE11 unterstützt es nicht.
StanE
@ user25163 Internet Explorer (und Opera Mini) scheinen die einzigen modernen Browser zu sein, die dies nicht unterstützen readAsBinaryString(): caniuse.com/#feat=filereader - Microsoft Edge unterstützt dies.
Benny Neugebauer
Vielen Dank für die Infos zu MS Edge! Ich arbeite für eine Firma. Und Sie wissen, dass Kunden häufig alte Software verwenden und wie schwierig es ist, sie davon zu überzeugen, ihre Software zu aktualisieren. Ich wollte nur darauf hinweisen, dass man vorsichtig sein muss, readAsBinaryString()da es von älteren Browsern nicht unterstützt wird. Eine Alternative, die ich gefunden habe, ist SparkMD5. Es wird auch die FileReader-API verwendet readAsArrayBuffer, jedoch die vom IE unterstützte Methode . Und es kann riesige Dateien verarbeiten, indem es sie in Stücken liest.
StanE
2
CryptoJS unterstützt jetzt die Konvertierung von einem ArrayBuffer in Binary / WordArray über:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad
@WarrenParad Und wie würde der obige Code dann geändert, um mit ArrayBuffer zu arbeiten? Ahh, fand es hier: stackoverflow.com/questions/28437181/…
TheStoryCoder
9

HTML5 + spark-md5undQ

Angenommen, Sie verwenden einen modernen Browser (der die HTML5-Datei-API unterstützt). So berechnen Sie den MD5-Hash einer großen Datei (er berechnet den Hash für variable Chunks).

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>

Jossef Harush
quelle
8

Sie müssen FileAPI verwenden. Es ist in der neuesten Version von FF & Chrome verfügbar, jedoch nicht in IE9. Besorgen Sie sich eine der oben vorgeschlagenen md5 JS-Implementierungen. Ich habe dies versucht und es aufgegeben, weil JS zu langsam war (Minuten bei großen Bilddateien). Könnte es erneut besuchen, wenn jemand MD5 mit typisierten Arrays neu schreibt.

Code würde ungefähr so ​​aussehen:

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));
     }
 }
Aleksandar Totic
quelle
Webtoolkit MD5 von Bendewey zeigte eine viel bessere Leistung, 16s für eine Multi-MB-Datei: webtoolkit.info/javascript-md5.html
Aleksandar Totic
1
Ich habe es geschafft, dass dies funktioniert und der gleiche MD5-Hash generiert (php: md5_file (...)) für Textdateien, aber Bilder liefern mir unterschiedliche Ergebnisse? Hat dies etwas mit den Binärdaten oder der Art und Weise zu tun, wie sie hochgeladen werden?
Schlösser
Ich bin mir ziemlich sicher, dass dieser Code nicht mit mehreren Dateien funktioniert, da Onload ein Rückruf ist und die readerVariable zum Zeitpunkt der Ausführung der Onload-Funktionen die letzte Datei ist.
Dave
CryptoJS unterstützt jetzt die Konvertierung von einem ArrayBuffer in Binary / WordArray über:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad
4

Abgesehen von der Unmöglichkeit, Dateisystemzugriff in JS zu erhalten, würde ich einer vom Client generierten Prüfsumme überhaupt kein Vertrauen schenken. Das Generieren der Prüfsumme auf dem Server ist daher in jedem Fall obligatorisch. - Tomalak 20. April 09 um 14:05 Uhr

Was in den meisten Fällen nutzlos ist. Sie möchten, dass der MD5 auf der Clientseite berechnet wird, damit Sie ihn mit dem auf der Serverseite neu berechneten Code vergleichen und feststellen können, dass der Upload fehlgeschlagen ist, wenn sie sich unterscheiden. Ich musste dies in Anwendungen tun, die mit großen Dateien wissenschaftlicher Daten arbeiten, bei denen der Empfang unbeschädigter Dateien der Schlüssel war. Meine Fälle waren einfach, da Benutzer den MD5 bereits aus ihren Datenanalysetools berechnet hatten, sodass ich ihn nur mit einem Textfeld fragen musste.

Marco
quelle
3

Um den Hash von Dateien zu erhalten, gibt es viele Optionen. Normalerweise ist das Problem, dass es sehr langsam ist, den Hash großer Dateien zu erhalten.

Ich habe eine kleine Bibliothek erstellt, die den Hash von Dateien abruft, mit den 64 KB am Anfang der Datei und den 64 KB am Ende der Datei.

Live-Beispiel: http://marcu87.github.com/hashme/ und Bibliothek: https://github.com/marcu87/hashme

Marco Antonio
quelle
2

Es gibt ein paar Skripte im Internet, um einen MD5-Hash zu erstellen.

Das von webtoolkit ist gut, http://www.webtoolkit.info/javascript-md5.html

Ich glaube jedoch nicht, dass es Zugriff auf das lokale Dateisystem haben wird, da dieser Zugriff begrenzt ist.

Bendewey
quelle
1

Ich hoffe, Sie haben inzwischen eine gute Lösung gefunden. Wenn nicht, ist die folgende Lösung eine ES6-Versprechen-Implementierung, die auf js-spark-md5 basiert

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();
  });
Zico Deng
quelle
1

Das folgende Snippet zeigt ein Beispiel, mit dem ein Durchsatz von 400 MB / s beim Lesen und Hashing der Datei archiviert werden kann.

Es wird eine Bibliothek namens Hash-Wasm verwendet , die auf WebAssembly basiert und den Hash schneller berechnet als Nur-JS-Bibliotheken. Ab 2020 unterstützen alle modernen Browser 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>

Biró Dani
quelle
0

Mit dem aktuellen HTML5 sollte es möglich sein, den md5-Hash einer Binärdatei zu berechnen. Ich denke jedoch, dass der vorherige Schritt darin besteht, die banalen Daten BlobBuilder in einen String zu konvertieren. Ich versuche diesen Schritt auszuführen: war aber nicht erfolgreich.

Hier ist der Code, den ich ausprobiert habe: Konvertieren eines BlobBuilder in einen String in HTML5-Javascript

user820955
quelle
-1

Ich glaube nicht, dass es in Javascript eine Möglichkeit gibt, auf den Inhalt eines Datei-Uploads zuzugreifen. Sie können daher nicht auf den Dateiinhalt schauen, um eine MD5-Summe zu generieren.

Sie können die Datei jedoch an den Server senden, der dann eine MD5-Summe zurücksenden oder den Dateiinhalt zurücksenden kann. Aber das ist viel Arbeit und für Ihre Zwecke wahrscheinlich nicht lohnenswert.

kbosak
quelle