자바 스크립트를 사용하여 파일의 md5 해시를 계산하는 방법


104

Javascript를 사용하여 서버에 업로드하기 전에 파일의 MD5 해시를 계산하는 방법이 있습니까?


1
강력하게 관련됨 : [RAM 오버플로없이 매우 큰 파일에 대해 체크섬을 생성하고 Javascript에서 64 비트로 변환하는 방법? ] ( stackoverflow.com/q/51987434/514235 )
iammilind

답변:


92

MD5 알고리즘의 JS 구현 이 있지만 이전 브라우저는 일반적으로 로컬 파일 시스템에서 파일을 읽을 수 없습니다 .

2009 년에 썼습니다. 그렇다면 새 브라우저는 어떻습니까?

FileAPI 를 지원하는 브라우저를 사용하면 파일의 내용을 * 읽을 수 * 있습니다 . 사용자가 <input>요소 또는 드래그 앤 드롭 으로 파일 을 선택해야합니다 . 2013 년 1 월 현재 주요 브라우저가 누적되는 방식은 다음과 같습니다.


30
JS에서 파일 시스템 액세스를 얻을 수 없다는 점을 제외하고는 클라이언트가 생성 한 체크섬을 전혀 신뢰하지 않습니다. 따라서 서버에서 체크섬을 생성하는 것은 어떤 경우에도 필수입니다.
Tomalak

4
@Tomalak 이미 가지고있는 것과 다른 경우에만 업로드하려는 경우 클라이언트에서 수행해야합니다.
John

2
@John 글쎄, 내 진술은 이것을 배제하지 않습니다. 클라이언트 측 검사는 엄격하게 사용자 편의를위한 것입니다 (따라서 원하는 방식에 따라 다소 선택 사항입니다). 반면에 서버 측 검사는 필수입니다.
Tomalak

pajhome.org.uk/crypt/md5의 md5 함수가 바이너리를 입력으로 지원하지 않습니까? 브라우저에 업로드 된 이미지의 바이너리 스트림을 계산해야한다고 생각합니다. 감사합니다.
jiajianrong

가능한 경우 답변에 예제 코드를 추가하십시오. 많은 도움이 될 것입니다.
cbdeveloper 19

30

대용량 파일을 효율적으로 해시하기 위해 증분 md5를 구현하는 라이브러리를 만들었습니다. 기본적으로 파일을 청크 단위로 읽고 (메모리를 낮게 유지하기 위해) 점진적으로 해시합니다. Readme에 기본적인 사용법과 예제가 있습니다.

HTML5 FileAPI가 필요하므로 확인해야합니다. 테스트 폴더에 전체 예제가 있습니다.

https://github.com/satazor/SparkMD5


여기 @ Biswa가 내 구현입니다. gist.github.com/marlocorridor/3e6484ae5a646bd7c625
마를

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

CryptoJSMD5 함수HTML5 FileReader API를 사용하여 MD5 해시를 계산하는 것은 매우 쉽습니다 . 다음 코드 스 니펫은 바이너리 데이터를 읽고 브라우저로 드래그 한 이미지에서 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입니다. FileReader API도 사용하지만 readAsArrayBufferIE에서 지원 하는 메서드를 사용합니다 . 그리고 대용량 파일을 청크로 읽어서 처리 할 수 ​​있습니다.
StanE

2
CryptoJS는 이제 다음을 통해 ArrayBuffer에서 Binary / WordArray 로의 변환을 지원합니다.CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

@WarrenParad 그러면 위의 코드가 ArrayBuffer와 함께 작동하도록 어떻게 수정됩니까? 아, 여기를 발견 stackoverflow.com/questions/28437181/...
TheStoryCoder

9

HTML5 + spark-md5Q

HTML5 파일 API를 지원하는 최신 브라우저를 사용한다고 가정하면 다음 은 대용량 파일 의 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));
     }
 }

bendewey가 지적한 Webtoolkit MD5는 훨씬 더 나은 성능을
보였습니다

1
이 작업을 수행하고 동일한 md5 해시가 텍스트 파일에 대해 생성되지만 이미지가 다른 결과를 제공합니까? (php : md5_file (...)) 이진 데이터 또는 업로드 방식과 관련이 있습니까?
Castles

onload가 콜백이기 때문에이 코드가 여러 파일에서 작동하지 않는다고 확신합니다. reader변수는 onload 함수가 실행될 때 마지막 파일이됩니다.
Dave

CryptoJS는 이제 다음을 통해 ArrayBuffer에서 Binary / WordArray 로의 변환을 지원합니다.CryptoJS.lib.WordArray.create(arrayBuffer);
Warren Parad

4

JS에서 파일 시스템 액세스를 얻을 수 없다는 점을 제외하고는 클라이언트가 생성 한 체크섬을 전혀 신뢰하지 않습니다. 따라서 서버에서 체크섬을 생성하는 것은 어떤 경우에도 필수입니다. – Tomalak 2009 년 4 월 20 일 14:05

대부분의 경우 쓸모가 없습니다. 클라이언트 측에서 MD5를 계산하여 서버 측에서 재 계산 된 코드와 비교하여 서로 다른 경우 업로드가 잘못되었다고 결론을 내릴 수 있습니다. 나는 손상되지 않은 파일을받는 것이 중요한 과학 데이터의 대용량 파일로 작업하는 응용 프로그램에서이를 수행해야했습니다. 제 경우는 간단했습니다. 사용자는 이미 데이터 분석 도구에서 MD5를 계산했기 때문에 텍스트 필드로 요청하면되었습니다.



2

인터넷에 MD5 해시를 만드는 스크립트가 몇 개 있습니다.

webtoolkit의 것이 좋습니다. http://www.webtoolkit.info/javascript-md5.html

그러나 액세스가 제한되어 있기 때문에 로컬 파일 시스템에 대한 액세스 권한이 있다고 생각하지 않습니다.


1

지금까지 좋은 해결책을 찾았기를 바랍니다. 그렇지 않은 경우 아래 솔루션은 js-spark-md5를 기반으로하는 ES6 약속 구현입니다.

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

다음 스 니펫은 파일을 읽고 해싱하는 동안 400MB / s의 처리량을 보관할 수있는 예를 보여줍니다.

WebAssembly 를 기반으로하며 js 전용 라이브러리보다 빠르게 해시를 계산하는 hash -wasm 이라는 라이브러리를 사용하고 있습니다 . 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 해시를 계산할 수 있지만 그 이전 단계는 banary 데이터 BlobBuilder를 문자열로 변환하는 것이라고 생각합니다.이 단계를 수행하려고하지만 성공하지 못했습니다.

내가 시도한 코드는 다음과 같습니다. HTML5 Javascript에서 BlobBuilder를 문자열로 변환


-1

파일 업로드 내용에 액세스하는 방법이 자바 스크립트에 있다고 생각하지 않습니다. 따라서 MD5 합계를 생성하기 위해 파일 내용을 볼 수 없습니다.

그러나 파일을 서버로 보내면 MD5 합계를 다시 보내거나 파일 내용을 다시 보낼 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.