Gibt es eine Möglichkeit, den MD5-Hash einer Datei vor dem Hochladen auf den Server mit Javascript zu berechnen?
Gibt es eine Möglichkeit, den MD5-Hash einer Datei vor dem Hochladen auf den Server mit Javascript zu berechnen?
Antworten:
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:
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.
.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.
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.
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.
readAsBinaryString()
: caniuse.com/#feat=filereader - Microsoft Edge unterstützt dies.
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.
CryptoJS.lib.WordArray.create(arrayBuffer);
spark-md5
undQ
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>
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));
}
}
reader
Variable zum Zeitpunkt der Ausführung der Onload-Funktionen die letzte Datei ist.
CryptoJS.lib.WordArray.create(arrayBuffer);
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.
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
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.
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();
});
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>
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
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.