Як обчислити хеш файлу md5 за допомогою JavaScript


104

Чи є спосіб обчислити хеш файлу MD5 перед завантаженням на сервер за допомогою Javascript?


1
Сильно пов’язані: [Як генерувати контрольну суму та конвертувати в 64-бітний Javascript для дуже великих файлів без переповнення оперативної пам’яті? ] ( Stackoverflow.com/q/51987434/514235 )
iammilind

Відповіді:


92

Незважаючи на те, що JS реалізує алгоритм MD5, старші браузери, як правило, не можуть читати файли з локальної файлової системи .

Я це писав у 2009 році. То як щодо нових браузерів?

За допомогою браузера, що підтримує FileAPI , ви можете * прочитати вміст файлу - користувач повинен обрати його, будь-яким <input>елементом або перетягуванням. Станом на січень 2013 року, ось як складати основні браузери:


30
Окрім неможливості отримати доступ до файлової системи в JS, я б взагалі не довіряв контрольній сумі, створеній клієнтом. Тому генерація контрольної суми на сервері є обов'язковою у будь-якому випадку.
Томалак

4
@Tomalak Це також обов'язково робити це на клієнті, якщо ви хочете лише завантажити його, якщо він відрізняється від того, що ви вже маєте.
Іван

2
@John Ну, моє твердження не виключає цього. Клієнтські чеки строго призначені для зручності користувача (і, таким чином, більш-менш необов'язково, залежно від того, наскільки зручно ви хочете зробити це). З іншого боку, перевірки на стороні сервера є обов'язковими.
Томалак

Функція md5 в pajhome.org.uk/crypt/md5 не підтримує бінарний вхід? Я думаю, що для обчисленого зображення в браузері необхідно обчислити двійковий потік. Дякую.
jiajianrong

Якщо можете, додайте до своєї відповіді якийсь прикладний код. Це дуже допомогло б.
cbdeveloper

30

Я створив бібліотеку, яка реалізує додаткові md5, щоб ефективно хеш-файли. В основному ви читаєте файл шматками (щоб зберегти пам'ять) і хеште його поступово. Ви отримали основне використання та приклади в readme.

Майте на увазі, що вам потрібен HTML5 FileAPI, тому обов'язково перевірте це. У тестовій папці є повний приклад.

https://github.com/satazor/SparkMD5


@ Бісла тут моя реалізація. gist.github.com/marlocorridor/3e6484ae5a646bd7c625
marlo

1
Гей, це чудово працює! Я спробував CryptoJS і ніколи не міг отримати точний MD5 з нього чомусь, це працює як шарм! Якісь плани щодо sha256? @satazor
comeck

@cameck, бібліотека хороша. Однак я спробував це сьогодні, і, схоже, існує проблема з .end()методом. Якщо ви знову зателефонуєте до цього методу, він наступного разу дає неправильний результат. Тому що .end()дзвонить .reset()внутрішньо. Це катастрофа кодування і не корисна для написання бібліотеки.
iammilind

Дякую за бібліотеку! Складіть мінімальний код: dev.to/micmo/compute-md5-checksum-for-a-file-in-typescript-59a4
Qortex

27

досить легко обчислити хеш 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.


2
Проблема в тому, що readAsBinaryString()вона не була стандартизована і не підтримується Internet Explorer. Я не тестував його в Edge, але навіть IE11 не підтримує його.
СТЕН

@ user25163 Internet Explorer (і Opera Mini) здаються єдиними сучасними браузерами, які не підтримують readAsBinaryString(): caniuse.com/#feat=filereader - Microsoft Edge підтримує це.
Бенні Нойгебауер

Дякуємо за інформацію про MS Edge! Я працюю в компанії. І ви знаєте, що клієнти часто використовують старе програмне забезпечення і як важко переконати їх оновити програмне забезпечення. Я просто хотів зазначити, що треба бути обережними, readAsBinaryString()оскільки це не підтримується старими браузерами. Я знайшов альтернативу SparkMD5. Він також використовує API FileReader, але метод readAsArrayBuffer, який підтримується IE. І він може обробляти величезні файли, читаючи їх шматками.
StanE

2
Тепер CryptoJS підтримує перетворення з ArrayBuffer в Binary / WordArray через:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

@WarrenParad А як би потім змінено вищезазначений код для роботи з ArrayBuffer? Ах, знайшов це тут: stackoverflow.com/questions/28437181 / ...
TheStoryCoder

9

HTML5 + 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>


8

Вам потрібно використовувати 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));
     }
 }

Webtoolkit MD5, накреслений bendewey, виступив набагато краще, 16-ти для файлу з кількома МБ: webtoolkit.info/javascript-md5.html
Олександр Тотік,

1
Мені вдалося зробити це робочим, і той самий хеш md5 генерує (php: md5_file (...)) для текстових файлів, але зображення дають мені різні результати? Це щось стосується двійкових даних або способу їх завантаження?
Замки

Я впевнений, що цей код не працює з декількома файлами, оскільки onload - це зворотний виклик, readerзмінна буде останнім файлом до моменту запуску функцій завантаження.
Дейв

Тепер CryptoJS підтримує перетворення з ArrayBuffer в Binary / WordArray через:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

4

Окрім неможливості отримати доступ до файлової системи в JS, я б взагалі не довіряв контрольній сумі, створеній клієнтом. Тому генерація контрольної суми на сервері є обов'язковою у будь-якому випадку. - Томалак 20 квітня '09 о 14:05

Що в більшості випадків марно. Ви хочете, щоб MD5 було обчислено на стороні клієнта, щоб ви могли порівняти його з кодом, перерахованим на стороні сервера, і зробити висновок, що завантаження пішло не так, якщо вони відрізняються. Мені це потрібно було зробити в додатках, що працюють з великими файлами наукових даних, де ключовим було отримання непошкоджених файлів. Мої випадки були простими, тому що користувачі вже обчислили MD5 зі своїх інструментів аналізу даних, тому мені просто потрібно було запитати їх у текстовому полі.


3

Щоб отримати хеш файлів, існує маса варіантів. Зазвичай проблема полягає в тому, що отримувати хеш великих файлів дуже повільно.

Я створив невелику бібліотеку, яка отримує хеш файлів, з 64 кб початку файлу і 64 кб в кінці.

Приклад наживо: http://marcu87.github.com/hashme/ та бібліотека: https://github.com/marcu87/hashme


2

В Інтернеті є кілька сценаріїв, щоб створити хеш MD5.

Той, що з webtoolkit, хороший, http://www.webtoolkit.info/javascript-md5.html

Хоча, я не вірю, що він матиме доступ до локальної файлової системи, оскільки цей доступ обмежений.


1

сподіваємось, ви вже знайшли хороше рішення. Якщо ні, рішення нижче - це реалізація обіцянок 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();
  });

1

Наступний фрагмент показує приклад, який може архівувати пропускну здатність 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>


0

За допомогою поточного HTML5 слід обчислити хэш md5 бінарного файлу, але я думаю, що кроком до цього було б перетворення даних банарів BlobBuilder в String, я намагаюся зробити цей крок: але не досяг успіху.

Ось код, який я спробував: Перетворення BlobBuilder в рядок, в HTML5 Javascript


-1

Я не вірю, що в JavaScript існує спосіб отримати доступ до вмісту завантаженого файлу. Тому ви не можете переглянути вміст файлу для отримання суми MD5.

Однак ви можете надіслати файл на сервер, який потім може відправити назад суму MD5 або повернути вміст файлу назад .., але це багато роботи і, мабуть, не вартує для ваших цілей.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.