Existe-t-il un moyen de calculer le hachage MD5 d'un fichier avant le téléchargement sur le serveur en utilisant Javascript?
Existe-t-il un moyen de calculer le hachage MD5 d'un fichier avant le téléchargement sur le serveur en utilisant Javascript?
Réponses:
Bien qu'il existe des implémentations JS de l'algorithme MD5, les anciens navigateurs sont généralement incapables de lire les fichiers à partir du système de fichiers local .
J'ai écrit ça en 2009. Alors qu'en est-il des nouveaux navigateurs?
Avec un navigateur qui prend en charge FileAPI , vous * pouvez * lire le contenu d'un fichier - l'utilisateur doit l'avoir sélectionné, soit avec un <input>
élément , soit par glisser-déposer. Depuis janvier 2013, voici comment les principaux navigateurs s'empilent:
J'ai créé une bibliothèque qui implémente md5 incrémentiel afin de hacher efficacement de gros fichiers. En gros, vous lisez un fichier par morceaux (pour garder la mémoire faible) et le hachez de manière incrémentielle. Vous avez une utilisation de base et des exemples dans le readme.
Sachez que vous avez besoin de HTML5 FileAPI, alors assurez-vous de le vérifier. Il y a un exemple complet dans le dossier de test.
.end()
méthode. Si vous appelez à nouveau cette méthode, cela donne un résultat erroné les fois suivantes. Parce que les .end()
appels en .reset()
interne. C'est un désastre de codage et pas bon pour l'écriture de bibliothèque.
il est assez facile de calculer le hachage MD5 en utilisant la fonction MD5 de CryptoJS et l' API HTML5 FileReader . L'extrait de code suivant montre comment vous pouvez lire les données binaires et calculer le hachage MD5 à partir d'une image qui a été glissée dans votre navigateur:
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);
};
Je recommande d'ajouter du CSS pour voir la zone Drag & Drop:
#holder {
border: 10px dashed #ccc;
width: 300px;
height: 300px;
}
#holder.hover {
border: 10px dashed #333;
}
Pour en savoir plus sur la fonctionnalité Drag & Drop, cliquez ici: File API & FileReader
J'ai testé l'exemple dans Google Chrome version 32.
readAsBinaryString()
n'a pas été normalisé et n'est pas pris en charge par Internet Explorer. Je ne l'ai pas testé dans Edge, mais même IE11 ne le prend pas en charge.
readAsBinaryString()
: caniuse.com/#feat=filereader - Microsoft Edge le prend en charge.
readAsBinaryString()
car il n'est pas pris en charge par les navigateurs plus anciens. Une alternative que j'ai trouvée est SparkMD5. Il utilise également l'API FileReader mais la méthode readAsArrayBuffer
, qui est prise en charge par IE. Et il peut gérer d'énormes fichiers en les lisant en morceaux.
CryptoJS.lib.WordArray.create(arrayBuffer);
spark-md5
etQ
En supposant que vous utilisez un navigateur moderne (qui prend en charge l'API de fichier HTML5), voici comment calculer le hachage MD5 d'un gros fichier (il calculera le hachage sur des blocs variables)
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>
Vous devez utiliser FileAPI. Il est disponible dans les derniers FF et Chrome, mais pas dans IE9. Prenez n'importe quelle implémentation md5 JS suggérée ci-dessus. J'ai essayé et abandonné parce que JS était trop lent (minutes sur de gros fichiers image). Pourrait le revoir si quelqu'un réécrit MD5 en utilisant des tableaux typés.
Le code ressemblerait à ceci:
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 sera le dernier fichier au moment où les fonctions de chargement sont exécutées.
CryptoJS.lib.WordArray.create(arrayBuffer);
Hormis l'impossibilité d'obtenir un accès au système de fichiers dans JS, je ne mettrais aucune confiance dans une somme de contrôle générée par le client. La génération de la somme de contrôle sur le serveur est donc obligatoire dans tous les cas. - Tomalak 20 avril 09 à 14:05
Ce qui est inutile dans la plupart des cas. Vous voulez que le MD5 soit calculé côté client, afin de pouvoir le comparer avec le code recalculé côté serveur et conclure que le téléchargement s'est mal passé s'ils diffèrent. J'ai eu besoin de le faire dans des applications fonctionnant avec de gros fichiers de données scientifiques, où la réception de fichiers non corrompus était la clé. Mes cas étaient simples, car les utilisateurs avaient déjà le MD5 calculé à partir de leurs outils d'analyse de données, alors j'avais juste besoin de le leur demander avec un champ de texte.
Pour obtenir le hachage des fichiers, il existe de nombreuses options. Normalement, le problème est qu'il est très lent d'obtenir le hachage de gros fichiers.
J'ai créé une petite bibliothèque qui récupère le hachage des fichiers, avec les 64kb du début du fichier et les 64kb de la fin.
Exemple en direct: http://marcu87.github.com/hashme/ et bibliothèque: https://github.com/marcu87/hashme
Il existe quelques scripts sur Internet pour créer un hachage MD5.
Celui de webtoolkit est bon, http://www.webtoolkit.info/javascript-md5.html
Cependant, je ne pense pas qu'il aura accès au système de fichiers local car cet accès est limité.
j'espère que vous avez trouvé une bonne solution maintenant. Sinon, la solution ci-dessous est une implémentation de promesse ES6 basée sur js-spark-md5
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();
});
L'extrait de code suivant montre un exemple, qui peut archiver un débit de 400 Mo / s lors de la lecture et du hachage du fichier.
Il utilise une bibliothèque appelée hash-wasm , qui est basée sur WebAssembly et calcule le hachage plus rapidement que les bibliothèques js uniquement. À partir de 2020, tous les navigateurs modernes prennent en charge 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>
Avec HTML5 actuel, il devrait être possible de calculer le hachage md5 d'un fichier binaire, mais je pense que l'étape précédente serait de convertir les données banary BlobBuilder en chaîne, j'essaie de faire cette étape: mais je n'ai pas réussi.
Voici le code que j'ai essayé: Conversion d'un BlobBuilder en string, en HTML5 Javascript
Je ne crois pas qu'il existe un moyen en javascript d'accéder au contenu d'un téléchargement de fichier. Vous ne pouvez donc pas regarder le contenu du fichier pour générer une somme MD5.
Vous pouvez cependant envoyer le fichier au serveur, qui peut alors renvoyer une somme MD5 ou renvoyer le contenu du fichier .. mais c'est beaucoup de travail et probablement pas utile pour vos besoins.