Как рассчитать md5-хэш файла с помощью javascript

104

Есть ли способ рассчитать хэш файла MD5 перед загрузкой на сервер с помощью Javascript?

ЛюРст
источник
2
Сильно связан: [Как сгенерировать контрольную сумму и преобразовать в 64-битный Javascript для очень больших файлов без переполнения ОЗУ? ] ( stackoverflow.com/q/51987434/514235 )
iammilind

Ответы:

92

Хотя существуют JS-реализации алгоритма MD5, старые браузеры обычно не могут читать файлы из локальной файловой системы .

Я написал это в 2009 году. А что насчет новых браузеров?

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

Пол Диксон
источник
30
Помимо невозможности получить доступ к файловой системе в JS, я бы вообще не стал доверять контрольной сумме, генерируемой клиентом. Так что создание контрольной суммы на сервере в любом случае обязательно.
Tomalak
4
@Tomalak Это также обязательно сделать на клиенте, если вы хотите загрузить его только в том случае, если он отличается от того, что у вас уже есть.
Джон
2
@John Ну, мое утверждение не исключает этого. Проверки на стороне клиента выполняются строго для удобства пользователя (и, следовательно, более или менее необязательны, в зависимости от того, насколько удобно вы хотите это сделать). С другой стороны, проверки на стороне сервера являются обязательными.
Tomalak
Функция md5 в pajhome.org.uk/crypt/md5 не поддерживает двоичный код на входе? Я считаю, что необходимо рассчитать двоичный поток для загруженного изображения в браузере. Спасибо.
jiajianrong
Если можете, добавьте к своему ответу пример кода. Это бы очень помогло.
cbdeveloper
30

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

Имейте в виду, что вам нужен HTML5 FileAPI, поэтому обязательно проверьте его. В тестовой папке есть полный пример.

https://github.com/satazor/SparkMD5

сатазор
источник
@Biswa вот моя реализация. gist.github.com/marlocorridor/3e6484ae5a646bd7c625
marlo
1
Эй, это отлично работает! Я попробовал CryptoJS и по какой-то причине так и не смог получить точный MD5, это работает как шарм! Есть планы на sha256? @satazor
cameck
@cameck, библиотека хорошая. Однако я попробовал это сегодня, и кажется, что есть проблема с .end()методом. Если вы вызовете этот метод снова, в следующий раз он даст неправильный результат. Потому что .end()звонит .reset()внутренне. Это катастрофа с кодированием и не подходит для написания библиотеки.
iammilind
Спасибо за библиотеку! Составьте
Qortex
27

довольно легко вычислить хеш MD5, используя функцию MD5 CryptoJS и API HTML5 FileReader . В следующем фрагменте кода показано, как вы можете прочитать двоичные данные и вычислить хэш 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;
}

Подробнее о функциях перетаскивания можно найти здесь: File API & FileReader

Я тестировал образец в Google Chrome версии 32.

Бенни Нойгебауэр
источник
2
Проблема в том, что readAsBinaryString()он не стандартизирован и не поддерживается Internet Explorer. Я не тестировал его в Edge, но даже IE11 его не поддерживает.
StanE
@ user25163 Internet Explorer (и Opera Mini), похоже, единственные современные браузеры, которые не поддерживают readAsBinaryString(): caniuse.com/#feat=filereader - Microsoft Edge поддерживает.
Benny Neugebauer
Благодарим за информацию о MS Edge! Я работаю в компании. И вы знаете, что клиенты часто используют старое программное обеспечение, и как трудно убедить их обновить свое программное обеспечение. Я просто хотел указать, что при использовании нужно быть осторожным, readAsBinaryString()поскольку он не поддерживается старыми браузерами. Я нашел альтернативу SparkMD5. Он также использует API FileReader, но метод readAsArrayBuffer, который поддерживается IE. И он может обрабатывать огромные файлы, читая их по частям.
StanE
2
CryptoJS теперь поддерживает преобразование из ArrayBuffer в двоичный / WordArray через:CryptoJS.lib.WordArray.create(arrayBuffer);
Уоррен Парад,
@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. Возьмите любую реализацию md5 JS, предложенную выше. Я пробовал это и отказался от него, потому что 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 секунд для
Александр Тотич
1
Мне удалось заставить это работать, и тот же хеш md5 генерируется (php: md5_file (...)) для текстовых файлов, но изображения дают разные результаты? Это как-то связано с двоичными данными или способом их загрузки?
Замки
Я почти уверен, что этот код не работает с несколькими файлами, потому что onload - это обратный вызов, readerпеременная будет последним файлом к ​​моменту запуска функций onload.
Дэйв
CryptoJS теперь поддерживает преобразование из ArrayBuffer в двоичный / WordArray через:CryptoJS.lib.WordArray.create(arrayBuffer);
Уоррен Парад,
5

Помимо невозможности получить доступ к файловой системе в JS, я бы вообще не стал доверять контрольной сумме, генерируемой клиентом. Так что создание контрольной суммы на сервере в любом случае обязательно. - Tomalak 20 апр.

Что в большинстве случаев бесполезно. Вы хотите, чтобы 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

Хотя я не верю, что у него будет доступ к локальной файловой системе, поскольку этот доступ ограничен.

Bendewey
источник
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 в строку, я пытаюсь сделать этот шаг: но безуспешно.

Вот код, который я пробовал: преобразование BlobBuilder в строку в HTML5 Javascript

user820955
источник
-1

Я не верю, что в javascript есть способ получить доступ к содержимому загружаемого файла. Таким образом, вы не можете просматривать содержимое файла для генерации суммы MD5.

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

кбосак
источник