วิธีคำนวณ md5 hash ของไฟล์โดยใช้ javascript


104

มีวิธีคำนวณแฮช MD5 ของไฟล์ก่อนอัปโหลดไปยังเซิร์ฟเวอร์โดยใช้ Javascript หรือไม่


1
เกี่ยวข้องอย่างยิ่ง: [วิธีสร้างการตรวจสอบและแปลงเป็น 64 บิตใน Javascript สำหรับไฟล์ขนาดใหญ่มากโดยไม่มี RAM ล้น? ] ( stackoverflow.com/q/51987434/514235 )
iammilind

คำตอบ:


92

แม้ว่าจะมีการใช้งาน JSของอัลกอริทึม MD5 แต่โดยทั่วไปเบราว์เซอร์รุ่นเก่าไม่สามารถอ่านไฟล์จากระบบไฟล์ภายในเครื่องได้

ฉันเขียนว่าในปี 2009 แล้วเบราว์เซอร์ใหม่ล่ะ

ด้วยเบราว์เซอร์ที่รองรับFileAPIคุณ * สามารถ * อ่านเนื้อหาของไฟล์ได้ - ผู้ใช้จะต้องเลือกโดยใช้<input>องค์ประกอบหรือลากแล้วปล่อย ในเดือนมกราคม 2013 ต่อไปนี้เป็นวิธีที่เบราว์เซอร์หลัก ๆ ซ้อนกัน:


30
นอกเหนือจากความเป็นไปไม่ได้ที่จะเข้าถึงระบบไฟล์ใน JS ฉันจะไม่ไว้วางใจใด ๆ เลยในการตรวจสอบที่ไคลเอ็นต์สร้างขึ้น ดังนั้นการสร้าง checksum บนเซิร์ฟเวอร์จึงมีผลบังคับใช้ไม่ว่าในกรณีใด ๆ
Tomalak

4
@Tomalak นอกจากนี้ยังจำเป็นต้องทำบนไคลเอนต์หากคุณต้องการอัปโหลดหากแตกต่างจากที่คุณมีอยู่แล้วเท่านั้น
ยอห์น

2
@ จอห์นคำพูดของฉันไม่ได้ออกกฎนี้ การตรวจสอบฝั่งไคลเอ็นต์เป็นไปอย่างเคร่งครัดเพื่อความสะดวกของผู้ใช้ (ดังนั้นจึงเป็นทางเลือกมากหรือน้อยขึ้นอยู่กับความสะดวกที่คุณต้องการ) ในทางกลับกันการตรวจสอบฝั่งเซิร์ฟเวอร์มีผลบังคับใช้
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

ขอบคุณสำหรับห้องสมุด! รวบรวมรหัสขั้นต่ำ: dev.to/micmo/compute-md5-checksum-for-a-file-in-typescript-59a4
Qortex

27

มันสวยง่ายในการคำนวณกัญชา MD5 โดยใช้ฟังก์ชั่นของ MD5 CryptoJSและHTML5 FileReader API ข้อมูลโค้ดต่อไปนี้แสดงวิธีการอ่านข้อมูลไบนารีและคำนวณแฮช 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 เพื่อดูพื้นที่ Drag & Drop:

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

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

สามารถดูข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชัน Drag & Drop ได้ที่นี่: 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 มันใช้ FileReader API ด้วย แต่เป็นวิธีการ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

สมมติว่าคุณใช้เบราว์เซอร์รุ่นใหม่ (ที่รองรับ HTML5 File API) ต่อไปนี้คือวิธีคำนวณMD5 Hashของไฟล์ขนาดใหญ่ (จะคำนวณแฮชในส่วนของตัวแปร)

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 วินาทีสำหรับไฟล์หลาย MB: webtoolkit.info/javascript-md5.html
Aleksandar Totic

1
ฉันจัดการเพื่อให้มันใช้งานได้และแฮช md5 เดียวกันกำลังสร้าง (php: md5_file (... )) สำหรับไฟล์ข้อความ แต่รูปภาพให้ผลลัพธ์ที่แตกต่างกัน? นี่คือสิ่งที่เกี่ยวข้องกับข้อมูลไบนารีหรือวิธีการอัปโหลด?
ปราสาท

ฉันค่อนข้างมั่นใจว่ารหัสนี้ใช้ไม่ได้กับไฟล์หลายไฟล์เนื่องจาก onload เป็นการเรียกกลับreaderตัวแปรจะเป็นไฟล์สุดท้ายเมื่อมีการเรียกใช้ฟังก์ชัน onload
Dave

CryptoJS รองรับการแปลงจาก ArrayBuffer เป็น Binary / WordArray ผ่าน:CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

4

นอกเหนือจากความเป็นไปไม่ได้ที่จะเข้าถึงระบบไฟล์ใน JS ฉันจะไม่ไว้วางใจใด ๆ เลยในการตรวจสอบที่ไคลเอ็นต์สร้างขึ้น ดังนั้นการสร้าง checksum บนเซิร์ฟเวอร์จึงมีผลบังคับใช้ไม่ว่าในกรณีใด ๆ - Tomalak 20 เม.ย. 52 เวลา 14:05 น

ซึ่งไม่มีประโยชน์ในกรณีส่วนใหญ่. คุณต้องการให้ MD5 คำนวณที่ฝั่งไคลเอ็นต์เพื่อให้คุณสามารถเปรียบเทียบกับโค้ดที่คำนวณใหม่ที่ฝั่งเซิร์ฟเวอร์และสรุปว่าการอัปโหลดผิดพลาดหากแตกต่างกัน ฉันจำเป็นต้องทำเช่นนั้นในแอปพลิเคชันที่ทำงานกับไฟล์ข้อมูลวิทยาศาสตร์ขนาดใหญ่ซึ่งการรับไฟล์ที่ไม่เสียหายเป็นกุญแจสำคัญ กรณีของฉันเป็นเรื่องง่ายเนื่องจากผู้ใช้มี MD5 ที่คำนวณจากเครื่องมือวิเคราะห์ข้อมูลอยู่แล้วดังนั้นฉันจึงต้องถามพวกเขาด้วยช่องข้อความ


3

ในการรับแฮชของไฟล์มีตัวเลือกมากมาย โดยปกติปัญหาคือมันช้ามากในการรับแฮชของไฟล์ขนาดใหญ่

ฉันสร้างไลบรารีเล็ก ๆ ที่ได้รับแฮชของไฟล์โดยมี 64kb ของจุดเริ่มต้นของไฟล์และ 64kb ของส่วนท้าย

ตัวอย่างสด: http://marcu87.github.com/hashme/และห้องสมุด: https://github.com/marcu87/hashme


2

มีสคริปต์สองสามรายการบนอินเทอร์เน็ตเพื่อสร้าง MD5 Hash

หนึ่งจาก 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 MB / s ในขณะที่อ่านและแฮชไฟล์

ใช้ไลบรารีที่เรียกว่าhash-wasmซึ่งใช้ WebAssembly และคำนวณแฮชได้เร็วกว่าไลบรารี js-only ในปี 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


-1

ฉันไม่เชื่อว่ามีวิธีในจาวาสคริปต์ในการเข้าถึงเนื้อหาของการอัปโหลดไฟล์ ดังนั้นคุณจึงไม่สามารถดูเนื้อหาของไฟล์เพื่อสร้างผลรวม MD5 ได้

อย่างไรก็ตามคุณสามารถส่งไฟล์ไปยังเซิร์ฟเวอร์ซึ่งสามารถส่งผลรวม MD5 กลับหรือส่งเนื้อหาไฟล์กลับได้ .. แต่นั่นเป็นงานที่ต้องใช้มากและอาจไม่คุ้มกับวัตถุประสงค์ของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.