JavaScript kullanarak bir dosyanın md5 karması nasıl hesaplanır


104

Javascript kullanarak sunucuya yüklemeden önce bir dosyanın MD5 karmasını hesaplamanın bir yolu var mı?


2
Kesinlikle İlgili: [Sağlama toplamı nasıl oluşturulur ve çok büyük dosyalar için Javascript'te RAM taşması olmadan 64 bit'e nasıl dönüştürülür? ] ( stackoverflow.com/q/51987434/514235 )
iammilind

Yanıtlar:


92

MD5 algoritmasının JS uygulamaları varken , eski tarayıcılar genellikle yerel dosya sisteminden dosyaları okuyamaz .

Bunu 2009'da yazdım. Peki ya yeni tarayıcılar?

FileAPI'yi destekleyen bir tarayıcıyla , bir dosyanın içeriğini * okuyabilirsiniz - kullanıcının bir <input>öğe veya sürükle ve bırak ile seçmiş olması gerekir . Ocak 2013 itibariyle, başlıca tarayıcıların yığılması şu şekildedir:


30
JS'de dosya sistemi erişimi elde etmenin imkansızlığının yanı sıra, istemci tarafından oluşturulan bir sağlama toplamına hiç güvenmem. Bu nedenle, sunucuda sağlama toplamı oluşturmak her durumda zorunludur.
Tomalak

4
@Tomalak Zaten sahip olduğunuzdan farklıysa, yalnızca yüklemek istiyorsanız, istemcide yapmanız da zorunludur.
John

2
@John Eh, benim ifadem bunu ekarte etmiyor. İstemci tarafı kontrolleri kesinlikle kullanıcı rahatlığı içindir (ve bu nedenle, ne kadar kullanışlı yapmak istediğinize bağlı olarak az çok isteğe bağlıdır). Öte yandan sunucu tarafı kontrolleri zorunludur.
Tomalak

pajhome.org.uk/crypt/md5 içindeki md5 işlevi girdi olarak ikiliyi desteklemiyor mu? Tarayıcıya yüklenen bir görüntü için ikili akışı hesaplamanın gerekli olduğunu düşünüyorum. Teşekkür ederim.
jiajianrong

Yapabiliyorsanız cevabınıza bazı örnek kodlar ekleyin. Çok yardımcı olur.
cbdeveloper

31

Büyük dosyaları verimli bir şekilde hash hale getirmek için artımlı md5 uygulayan bir kitaplık yaptım. Temel olarak bir dosyayı parçalar halinde okursunuz (hafızayı düşük tutmak için) ve aşamalı olarak hashing uyguluyorsunuz. Benioku dosyasında temel kullanım ve örnekler var.

HTML5 FileAPI'ye ihtiyacınız olduğunu unutmayın, bu yüzden kontrol ettiğinizden emin olun. Test klasöründe tam bir örnek var.

https://github.com/satazor/SparkMD5



1
Hey bu harika çalışıyor! CryptoJS'i denedim ve herhangi bir nedenle ondan asla doğru bir MD5 elde edemedim, bu bir cazibe gibi çalışıyor! Sha256 için planınız var mı? @satazor
cameck

@cameck, kütüphane iyidir. Ancak bugün denedim ve görünen o ki .end()yöntemle ilgili bir sorun var . Bu yöntemi tekrar çağırırsanız, sonraki sefer yanlış sonuç verir. Çünkü dahili .end()aramalar .reset(). Bu bir kodlama felaketi ve kütüphane yazımı için iyi değil.
iammilind

Kütüphane için teşekkürler! Minimum bir kodu bir araya getirin: dev.to/micmo/compute-md5-checksum-for-a-file-in-typescript-59a4
Qortex

28

CryptoJS'nin MD5 işlevini ve HTML5 FileReader API'yi kullanarak MD5 karmasını hesaplamak oldukça kolaydır . Aşağıdaki kod parçacığı, ikili verileri nasıl okuyabileceğinizi ve Tarayıcınıza sürüklenen bir görüntüden MD5 karmasını nasıl hesaplayabileceğinizi gösterir:

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);
};

Sürükle ve Bırak alanını görmek için biraz CSS eklemenizi tavsiye ederim:

#holder {
  border: 10px dashed #ccc;
  width: 300px;
  height: 300px;
}

#holder.hover {
  border: 10px dashed #333;
}

Sürükle ve Bırak işlevi hakkında daha fazla bilgiyi burada bulabilirsiniz: Dosya API ve Dosya Okuyucu

Örneği Google Chrome Sürüm 32'de test ettim.


2
Sorun şu ki readAsBinaryString(), standartlaştırılmamış ve Internet Explorer tarafından desteklenmiyor. Edge'de test etmedim, ancak IE11 bile desteklemiyor.
StanE

@ user25163 Internet Explorer (ve Opera Mini) desteklemeyen tek modern tarayıcı gibi görünüyor readAsBinaryString(): caniuse.com/#feat=filereader - Microsoft Edge bunu destekliyor.
Benny Neugebauer

MS Edge ile ilgili bilgiler için teşekkür ederiz! Bir şirket için çalışıyorum. Ve biliyorsunuz ki, müşteriler genellikle eski yazılımları kullanıyor ve onları yazılımlarını güncellemeye ikna etmenin ne kadar zor olduğunu. readAsBinaryString()Eski tarayıcılar tarafından desteklenmediği için dikkatli kullanmak gerektiğini belirtmek istedim . Bulduğum bir alternatif SparkMD5. FileReader API'yi de kullanır, ancak readAsArrayBufferIE tarafından desteklenen yöntemi kullanır . Ve büyük dosyaları parçalar halinde okuyarak işleyebilir.
StanE

2
CryptoJS artık bir ArrayBuffer'dan Binary / WordArray'e dönüştürmeyi şu şekilde destekliyor:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

@WarrenParad Ve yukarıdaki kod ArrayBuffer ile çalışacak şekilde nasıl değiştirilir? Ahh, burada buldum: stackoverflow.com/questions/28437181/…
TheStoryCoder

9

HTML5 + spark-md5veQ

Modern bir tarayıcı (HTML5 Dosya API'sini destekleyen) kullandığınızı varsayarsak, büyük bir dosyanın MD5 Hash'ini şu şekilde hesaplayabilirsiniz (değişken parçalar üzerindeki hash'ı hesaplayacaktır)

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 kullanmanız gerekir. En son FF ve Chrome'da mevcuttur, ancak IE9'da yoktur. Yukarıda önerilen herhangi bir md5 JS uygulamasını edinin. Bunu denedim ve bıraktım çünkü JS çok yavaştı (büyük resim dosyalarında dakikalar). Birisi MD5'i yazılan dizileri kullanarak yeniden yazarsa tekrar ziyaret edebilir.

Kod şunun gibi görünecektir:

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));
     }
 }

Bendewey tarafından gösterilen Webtoolkit MD5 çok daha iyi performans gösterdi, çoklu MB dosyası için 16 saniye
Aleksandar Totic

1
Bunu çalıştırmayı başardım ve metin dosyaları için aynı md5 hash'i (php: md5_file (...)) üretiyor ancak görüntüler bana farklı sonuçlar veriyor? Bunun ikili verilerle mi yoksa yüklenme şekliyle mi ilgisi var?
Kaleler

Bu kodun birden fazla readerdosyayla çalışmadığından oldukça eminim, çünkü onload bir geri çağırmadır, değişken, onload fonksiyonları çalıştırıldığında son dosya olacaktır.
Dave

CryptoJS artık bir ArrayBuffer'dan Binary / WordArray'e dönüştürmeyi şu şekilde destekliyor:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

5

JS'de dosya sistemi erişimi elde etmenin imkansızlığının yanı sıra, istemci tarafından oluşturulan bir sağlama toplamına hiç güvenmem. Bu nedenle, sunucuda sağlama toplamı oluşturmak her durumda zorunludur. - Tomalak 20 Nisan '09 saat 14: 05

Çoğu durumda bu işe yaramaz. MD5'in istemci tarafında hesaplanmasını istiyorsunuz, böylece onu sunucu tarafında yeniden hesaplanan kodla karşılaştırabilir ve farklılarsa yüklemenin yanlış gittiği sonucuna varabilirsiniz. Bunu, bozulmamış dosyaları almanın önemli olduğu büyük bilimsel veri dosyalarıyla çalışan uygulamalarda yapmam gerekiyordu. Vakalarım basitti, çünkü kullanıcılar MD5'i veri analiz araçlarından zaten hesaplamıştı, bu yüzden onlara bir metin alanıyla sormam gerekiyordu.




1

umarım şimdiye kadar iyi bir çözüm bulmuşsunuzdur. Değilse, aşağıdaki çözüm js-spark- md5'e dayalı bir ES6 taahhüt uygulamasıdır.

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

Aşağıdaki kod parçası, dosyayı okurken ve hashing uygularken 400 MB / s'lik bir aktarım hızını arşivleyebilen bir örnek gösterir.

WebAssembly tabanlı olan ve hash'i yalnızca js kitaplıklarından daha hızlı hesaplayan hash-wasm adlı bir kitaplık kullanıyor . 2020 itibariyle, tüm modern tarayıcılar WebAssembly'ı desteklemektedir.

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>



-1

Bir dosya yüklemesinin içeriğine erişmek için javascript'te bir yol olduğuna inanmıyorum. Bu nedenle, bir MD5 toplamı oluşturmak için dosya içeriğine bakamazsınız.

Bununla birlikte, dosyayı sunucuya gönderebilirsiniz, bu da daha sonra bir MD5 toplamını geri gönderebilir veya dosya içeriğini geri gönderebilir .. ama bu çok iş ve muhtemelen amaçlarınız için değerli değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.