Cara menghitung hash md5 file menggunakan javascript


104

Apakah ada cara untuk menghitung hash MD5 file sebelum diunggah ke server menggunakan Javascript?


1
Sangat Terkait: [Bagaimana cara menghasilkan checksum & mengkonversi ke 64 bit di Javascript untuk file yang sangat besar tanpa RAM yang berlebihan? ] ( stackoverflow.com/q/51987434/514235 )
iammilind

Jawaban:


92

Meskipun ada implementasi JS dari algoritme MD5, browser lama biasanya tidak dapat membaca file dari sistem file lokal .

Saya menulisnya di tahun 2009. Lalu bagaimana dengan browser baru?

Dengan browser yang mendukung FileAPI , Anda * dapat * membaca konten file - pengguna harus memilihnya, baik dengan <input>elemen atau drag-and-drop. Mulai Jan 2013, berikut ini susunan browser utama:


30
Terlepas dari ketidakmungkinan untuk mendapatkan akses sistem file di JS, saya tidak akan menaruh kepercayaan sama sekali pada checksum yang dibuat klien. Jadi, membuat checksum di server adalah wajib dalam hal apa pun.
Tomalak

4
@Tomalak Ini juga wajib dilakukan di klien jika Anda hanya ingin mengunggahnya jika berbeda dari yang sudah Anda miliki.
Yohanes

2
@ John Nah, pernyataan saya tidak mengesampingkan hal ini. Pemeriksaan sisi klien secara ketat untuk kenyamanan pengguna (dan dengan demikian lebih atau kurang opsional, tergantung pada seberapa nyaman Anda ingin membuatnya). Sebaliknya, pemeriksaan sisi server adalah wajib.
Tomalak

fungsi md5 di pajhome.org.uk/crypt/md5 tidak mendukung biner sebagai input? Saya pikir itu perlu untuk menghitung aliran biner untuk gambar yang diunggah di browser. Terima kasih.
jiajianrong

Jika bisa, tambahkan beberapa kode contoh ke jawaban Anda. Ini akan sangat membantu.
cbdeveloper

30

Saya telah membuat perpustakaan yang mengimplementasikan incremental md5 untuk melakukan hash file besar secara efisien. Pada dasarnya Anda membaca file dalam potongan (untuk menjaga memori rendah) dan melakukan hash secara bertahap. Anda mendapatkan penggunaan dasar dan contoh di readme.

Ketahuilah bahwa Anda memerlukan FileAPI HTML5, jadi pastikan untuk memeriksanya. Ada contoh lengkap di folder tes.

https://github.com/satazor/SparkMD5


@Biswa di sini adalah implementasi saya. gist.github.com/marlocorridor/3e6484ae5a646bd7c625
marlo

1
Hei, ini bagus! Saya mencoba CryptoJS dan tidak pernah bisa mendapatkan MD5 yang akurat karena alasan tertentu, ini berfungsi seperti pesona! Ada rencana untuk sha256? @satazor
cameck

@cameck, perpustakaannya bagus. Namun saya mencobanya hari ini dan tampaknya ada masalah dengan .end()metode tersebut. Jika Anda memanggil metode ini lagi maka itu memberikan hasil yang salah di lain waktu. Karena .end()panggilan .reset()internal. Ini adalah bencana pengkodean dan tidak baik untuk penulisan perpustakaan.
iammilind

Terima kasih untuk perpustakaannya! Kumpulkan kode minimal: dev.to/micmo/compute-md5-checksum-for-a-file-in-typescript-59a4
Qortex

27

sangat mudah untuk menghitung hash MD5 menggunakan fungsi MD5 dari CryptoJS dan API FileReader HTML5 . Potongan kode berikut menunjukkan bagaimana Anda dapat membaca data biner dan menghitung hash MD5 dari gambar yang telah diseret ke Browser Anda:

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

Saya merekomendasikan untuk menambahkan beberapa CSS untuk melihat area Drag & Drop:

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

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

Lebih lanjut tentang fungsionalitas Seret & Jatuhkan dapat ditemukan di sini: API File & FileReader

Saya menguji sampel di Google Chrome Versi 32.


2
Masalahnya adalah, itu readAsBinaryString()belum distandarisasi dan tidak didukung oleh Internet Explorer. Saya tidak mengujinya di Edge, tetapi bahkan IE11 tidak mendukungnya.
StanE

@ user25163 Internet Explorer (dan Opera Mini) tampaknya satu-satunya browser modern yang tidak mendukung readAsBinaryString(): caniuse.com/#feat=filereader - Microsoft Edge mendukungnya.
Benny Neugebauer

Terima kasih atas info tentang MS Edge! Saya bekerja untuk sebuah perusahaan. Dan tahukah Anda, bahwa pelanggan sering menggunakan perangkat lunak lama dan betapa sulitnya meyakinkan mereka untuk memperbarui perangkat lunak mereka. Saya hanya ingin menunjukkan, bahwa pengguna harus berhati-hati readAsBinaryString()karena tidak didukung oleh browser lama. Alternatif yang saya temukan adalah SparkMD5. Ia menggunakan API FileReader juga tetapi metode readAsArrayBuffer, yang didukung oleh IE. Dan itu bisa menangani file besar dengan membacanya dalam potongan.
StanE

2
CryptoJS sekarang mendukung konversi dari ArrayBuffer ke Binary / WordArray melalui:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

@WarrenParad Dan bagaimana kode di atas kemudian dimodifikasi untuk bekerja dengan ArrayBuffer? Ahh, temukan di sini: stackoverflow.com/questions/28437181/…
TheStoryCoder

9

HTML5 + spark-md5danQ

Dengan asumsi Anda menggunakan browser modern (yang mendukung API File HTML5), berikut ini cara Anda menghitung Hash MD5 dari file besar (ini akan menghitung hash pada potongan variabel)

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

Anda perlu menggunakan FileAPI. Ini tersedia di FF & Chrome terbaru, tetapi tidak di IE9. Ambil implementasi JS md5 yang disarankan di atas. Saya sudah mencoba ini dan meninggalkannya karena JS terlalu lambat (menit pada file gambar besar). Dapat mengunjungi kembali jika seseorang menulis ulang MD5 menggunakan array yang diketik.

Kode akan terlihat seperti ini:

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 yang ditunjukkan oleh bendewey berkinerja jauh lebih baik, 16s untuk file multi-MB: webtoolkit.info/javascript-md5.html
Aleksandar Totic

1
Saya telah berhasil membuat ini berfungsi dan hash md5 yang sama sedang dihasilkan (php: md5_file (...)) untuk file teks tetapi gambar memberi saya hasil yang berbeda? Apakah ini ada hubungannya dengan data biner atau cara mengunggahnya?
Kastil

Saya cukup yakin kode ini tidak berfungsi dengan banyak file, karena onload adalah callback, readervariabel akan menjadi file terakhir pada saat fungsi onload dijalankan.
Dave

CryptoJS sekarang mendukung konversi dari ArrayBuffer ke Binary / WordArray melalui:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

4

Terlepas dari ketidakmungkinan untuk mendapatkan akses sistem file di JS, saya tidak akan menaruh kepercayaan sama sekali pada checksum yang dibuat klien. Jadi, membuat checksum di server adalah wajib dalam hal apa pun. - Tomalak 20 April '09 pukul 14:05

Yang tidak berguna dalam banyak kasus. Anda ingin MD5 dihitung di sisi klien, sehingga Anda dapat membandingkannya dengan kode yang dihitung ulang di sisi server dan menyimpulkan bahwa unggahan salah jika berbeda. Saya perlu melakukan itu dalam aplikasi yang bekerja dengan file besar data ilmiah, di mana menerima file yang tidak rusak adalah kuncinya. Kasus saya sederhana, karena MD5 pengguna sudah dihitung dari alat analisis data mereka, jadi saya hanya perlu menanyakannya kepada mereka dengan bidang teks.




1

harap Anda telah menemukan solusi yang baik sekarang. Jika tidak, solusi di bawah ini adalah implementasi janji ES6 berdasarkan 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

Cuplikan berikut menunjukkan contoh, yang dapat mengarsipkan throughput 400 MB / dtk saat membaca dan mencirikan file.

Ini menggunakan pustaka yang disebut hash-wasm , yang didasarkan pada WebAssembly dan menghitung hash lebih cepat daripada pustaka js-only. Mulai tahun 2020, semua browser modern mendukung 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>



-1

Saya tidak yakin ada cara di javascript untuk mengakses konten unggahan file. Jadi, Anda tidak dapat melihat konten file untuk menghasilkan jumlah MD5.

Namun Anda dapat mengirim file ke server, yang kemudian dapat mengirim kembali jumlah MD5 atau mengirim kembali konten file .. tetapi itu banyak pekerjaan dan mungkin tidak bermanfaat untuk tujuan Anda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.