Значение хэш-файла JavaScript генерируется с частью файла

Я работаю с JavaScript для создания файла HASH VALUE для уникальных значений файлов. Просьба проверить приведенный ниже код для механизма генерации хеша, который работает хорошо.

<script type="text/javascript"> // Reference: https://code.google.com/p/crypto-js/#MD5 function handleFileSelect(evt) { var files = evt.target.files; // FileList object // Loop through the FileList and render image files as thumbnails. for (var i = 0, f; f = files[i]; i++) { var reader = new FileReader(); // Closure to capture the file information. reader.onload = (function(theFile) { return function(e) { var span = document.createElement('span'); var test = e.target.result; //var hash = hex_md5(test); var hash = CryptoJS.MD5(test); var elem = document.getElementById("hashValue"); elem.value = hash; }; })(f); // Read in the image file as a data URL. reader.readAsBinaryString(f); } } document.getElementById('videoupload').addEventListener('change', handleFileSelect, false); </script> 

Однако я сталкиваюсь с проблемой при создании HASH VALUE для больших файлов, как на стороне клиента, браузер Crashed.

Up-till 30MB HASHING работает хорошо, но если я попытаюсь загрузить больше, чем система выйдет из строя.

Мой вопрос:

  1. Могу ли я генерировать значение HASH для части файла, чем чтение файлов LARGE и получение сбоев? Если да, могу ли я знать, как сделать эту ширину «FileReader»;

  2. Могу ли я указать любое количество байтов, например 2000 символов файла, чтобы генерировать значение HASH, а затем генерирование для больших файлов.

Я надеюсь, что вышеупомянутое два решения будут работать для больших и маленьких файлов. Есть ли другие варианты?

Моя скриптовая демонстрация

  1. Могу ли я генерировать значение HASH для части файла, чем чтение файлов LARGE и получение сбоев? Если да, могу ли я знать, как сделать эту ширину «FileReader»;

Да, вы можете это сделать, и это называется Progressive Hashing .

 var md5 = CryptoJS.algo.MD5.create(); md5.update("file part 1"); md5.update("file part 2"); md5.update("file part 3"); var hash = md5.finalize(); 
  1. Могу ли я указать любое количество байтов, например 2000 символов файла, чтобы генерировать значение HASH, а затем генерирование для больших файлов.

Есть статья HTML5Rocks о том, как можно использовать File.slice для передачи разрезанного файла в FileReader :

 var blob = file.slice(startingByte, endindByte); reader.readAsArrayBuffer(blob); 

Полное решение

Я объединил оба. Трудная часть заключалась в синхронизации чтения файла, потому что FileReader.readAsArrayBuffer() является асинхронным. Я написал небольшую функцию series которая смоделирована после функции серии async.js . Это нужно делать один за другим, потому что нет никакого способа добраться до внутреннего состояния хэширующей функции CryptoJS.

Кроме того, CryptoJS не понимает, что такое ArrayBuffer , поэтому его нужно преобразовать в собственное представление данных, которое является так называемым WordArray:

 function arrayBufferToWordArray(ab) { var i8a = new Uint8Array(ab); var a = []; for (var i = 0; i < i8a.length; i += 4) { a.push(i8a[i] << 24 | i8a[i + 1] << 16 | i8a[i + 2] << 8 | i8a[i + 3]); } return CryptoJS.lib.WordArray.create(a, i8a.length); } 

Другое дело, что хеширование – это синхронная операция, в которой нет возможности продолжить выполнение в другом месте. Из-за этого браузер замерзает, поскольку JavaScript является однопоточным. Решение состоит в том, чтобы использовать Web Workers для выгрузки хеширования в другой поток, чтобы поток пользовательского интерфейса сохранял отзывчивость.
Веб-работники ожидают файл сценария в своих конструкторах, поэтому я использовал это решение Rob W для создания встроенного скрипта.

 function series(tasks, done){ if(!tasks || tasks.length === 0) { done(); } else { tasks[0](function(){ series(tasks.slice(1), done); }); } } function webWorkerOnMessage(e){ if (e.data.type === "create") { md5 = CryptoJS.algo.MD5.create(); postMessage({type: "create"}); } else if (e.data.type === "update") { function arrayBufferToWordArray(ab) { var i8a = new Uint8Array(ab); var a = []; for (var i = 0; i < i8a.length; i += 4) { a.push(i8a[i] << 24 | i8a[i + 1] << 16 | i8a[i + 2] << 8 | i8a[i + 3]); } return CryptoJS.lib.WordArray.create(a, i8a.length); } md5.update(arrayBufferToWordArray(e.data.chunk)); postMessage({type: "update"}); } else if (e.data.type === "finish") { postMessage({type: "finish", hash: ""+md5.finalize()}); } } // URL.createObjectURL window.URL = window.URL || window.webkitURL; // "Server response", used in all examples var response = "importScripts('https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/md5.js');"+ "var md5;"+ "self.onmessage = "+webWorkerOnMessage.toString(); var blob; try { blob = new Blob([response], {type: 'application/javascript'}); } catch (e) { // Backwards-compatibility window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder; blob = new BlobBuilder(); blob.append(response); blob = blob.getBlob(); } var worker = new Worker(URL.createObjectURL(blob)); var files = evt.target.files; // FileList object var chunksize = 1000000; // the chunk size doesn't make a difference var i = 0, f = files[i], chunks = Math.ceil(f.size / chunksize), chunkTasks = [], startTime = (new Date()).getTime(); worker.onmessage = function(e) { // create callback for(var j = 0; j < chunks; j++){ (function(j, f){ chunkTasks.push(function(next){ var blob = f.slice(j * chunksize, Math.min((j+1) * chunksize, f.size)); var reader = new FileReader(); reader.onload = function(e) { var chunk = e.target.result; worker.onmessage = function(e) { // update callback document.getElementById('num').innerHTML = ""+(j+1)+"/"+chunks; next(); }; worker.postMessage({type: "update", chunk: chunk}); }; reader.readAsArrayBuffer(blob); }); })(j, f); } series(chunkTasks, function(){ var elem = document.getElementById("hashValueSplit"); var telem = document.getElementById("time"); worker.onmessage = function(e) { // finish callback elem.value = e.data.hash; telem.innerHTML = "in " + Math.ceil(((new Date()).getTime() - startTime) / 1000) + " seconds"; }; worker.postMessage({type: "finish"}); }); // blocking way ahead... if (document.getElementById("singleHash").checked) { var reader = new FileReader(); // Closure to capture the file information. reader.onloadend = (function(theFile) { function arrayBufferToWordArray(ab) { var i8a = new Uint8Array(ab); var a = []; for (var i = 0; i < i8a.length; i += 4) { a.push(i8a[i] << 24 | i8a[i + 1] << 16 | i8a[i + 2] << 8 | i8a[i + 3]); } return CryptoJS.lib.WordArray.create(a, i8a.length); } return function(e) { var test = e.target.result; var hash = CryptoJS.MD5(arrayBufferToWordArray(test)); //var hash = "none"; var elem = document.getElementById("hashValue"); elem.value = hash; }; })(f); // Read in the image file as a data URL. reader.readAsArrayBuffer(f); } }; worker.postMessage({type: "create"}); 

DEMO, похоже, работает для больших файлов, но это занимает довольно много времени. Возможно, это можно улучшить, используя более быструю реализацию MD5. Потребовалось около 23 минут для хэширования файла размером 3 ГБ.

Этот мой ответ показывает пример без веб-мастеров для SHA-256.