Чи є спосіб обчислити хеш файлу MD5 перед завантаженням на сервер за допомогою Javascript?
Чи є спосіб обчислити хеш файлу MD5 перед завантаженням на сервер за допомогою Javascript?
Відповіді:
Незважаючи на те, що JS реалізує алгоритм MD5, старші браузери, як правило, не можуть читати файли з локальної файлової системи .
Я це писав у 2009 році. То як щодо нових браузерів?
За допомогою браузера, що підтримує FileAPI , ви можете * прочитати вміст файлу - користувач повинен обрати його, будь-яким <input>
елементом або перетягуванням. Станом на січень 2013 року, ось як складати основні браузери:
Я створив бібліотеку, яка реалізує додаткові md5, щоб ефективно хеш-файли. В основному ви читаєте файл шматками (щоб зберегти пам'ять) і хеште його поступово. Ви отримали основне використання та приклади в readme.
Майте на увазі, що вам потрібен HTML5 FileAPI, тому обов'язково перевірте це. У тестовій папці є повний приклад.
.end()
методом. Якщо ви знову зателефонуєте до цього методу, він наступного разу дає неправильний результат. Тому що .end()
дзвонить .reset()
внутрішньо. Це катастрофа кодування і не корисна для написання бібліотеки.
досить легко обчислити хеш MD5 за допомогою функції MD5 CryptoJS та API FileReader HTML5 . Наступний фрагмент коду показує, як можна читати двійкові дані та обчислювати хеш 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.
readAsBinaryString()
вона не була стандартизована і не підтримується Internet Explorer. Я не тестував його в Edge, але навіть IE11 не підтримує його.
readAsBinaryString()
: caniuse.com/#feat=filereader - Microsoft Edge підтримує це.
readAsBinaryString()
оскільки це не підтримується старими браузерами. Я знайшов альтернативу SparkMD5. Він також використовує API FileReader, але метод readAsArrayBuffer
, який підтримується IE. І він може обробляти величезні файли, читаючи їх шматками.
CryptoJS.lib.WordArray.create(arrayBuffer);
spark-md5
іQ
Якщо припустити, що ви використовуєте сучасний браузер (що підтримує API файлу HTML5), ось як ви обчислите хеш 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>
Вам потрібно використовувати FileAPI. Він доступний в останніх FF та Chrome, але не в IE9. Захопіть будь-яку реалізацію JS md5, запропоновану вище Я спробував це і відмовився від цього, оскільки 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));
}
}
reader
змінна буде останнім файлом до моменту запуску функцій завантаження.
CryptoJS.lib.WordArray.create(arrayBuffer);
Окрім неможливості отримати доступ до файлової системи в JS, я б взагалі не довіряв контрольній сумі, створеній клієнтом. Тому генерація контрольної суми на сервері є обов'язковою у будь-якому випадку. - Томалак 20 квітня '09 о 14:05
Що в більшості випадків марно. Ви хочете, щоб MD5 було обчислено на стороні клієнта, щоб ви могли порівняти його з кодом, перерахованим на стороні сервера, і зробити висновок, що завантаження пішло не так, якщо вони відрізняються. Мені це потрібно було зробити в додатках, що працюють з великими файлами наукових даних, де ключовим було отримання непошкоджених файлів. Мої випадки були простими, тому що користувачі вже обчислили MD5 зі своїх інструментів аналізу даних, тому мені просто потрібно було запитати їх у текстовому полі.
Щоб отримати хеш файлів, існує маса варіантів. Зазвичай проблема полягає в тому, що отримувати хеш великих файлів дуже повільно.
Я створив невелику бібліотеку, яка отримує хеш файлів, з 64 кб початку файлу і 64 кб в кінці.
Приклад наживо: http://marcu87.github.com/hashme/ та бібліотека: https://github.com/marcu87/hashme
В Інтернеті є кілька сценаріїв, щоб створити хеш MD5.
Той, що з webtoolkit, хороший, http://www.webtoolkit.info/javascript-md5.html
Хоча, я не вірю, що він матиме доступ до локальної файлової системи, оскільки цей доступ обмежений.
сподіваємось, ви вже знайшли хороше рішення. Якщо ні, рішення нижче - це реалізація обіцянок ES6 на основі 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();
});
Наступний фрагмент показує приклад, який може архівувати пропускну здатність 400 Мб / с під час читання та хешування файлу.
Він використовує бібліотеку під назвою hash-wasm , яка базується на WebAssembly і обчислює хеш швидше, ніж лише бібліотеки js. Станом на 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>
За допомогою поточного HTML5 слід обчислити хэш md5 бінарного файлу, але я думаю, що кроком до цього було б перетворення даних банарів BlobBuilder в String, я намагаюся зробити цей крок: але не досяг успіху.
Ось код, який я спробував: Перетворення BlobBuilder в рядок, в HTML5 Javascript
Я не вірю, що в JavaScript існує спосіб отримати доступ до вмісту завантаженого файлу. Тому ви не можете переглянути вміст файлу для отримання суми MD5.
Однак ви можете надіслати файл на сервер, який потім може відправити назад суму MD5 або повернути вміст файлу назад .., але це багато роботи і, мабуть, не вартує для ваших цілей.