Groovy 에서도 100MB 넘는 파일 중에서 중복된 파일을 찾는 프로그램을 만들어보았다. 모든 언어를 공부하면서 만들어보는 것인데, MD5 Checksum 이 같으면 중복된 파일로 인식하고, 카운트하여 정렬하고 출력하도록 하였다. 파일에 대한 MD5 Checksum 기능이 없어서 직접 구현되었고, 이 부분을 제외하면 Ruby 와 거의 비슷하다.
// -----------------------------------------------------------------------------
// 100MB 이상의 파일중에서 중복된 파일 찾기
// -----------------------------------------------------------------------------
 
import java.security.MessageDigest
 
final TARGET_DIR = "C:\\"
final LIMIT_SIZE = 100000000
 
def md5sum(final file) {
    MessageDigest digest = MessageDigest.getInstance("MD5")
    file.withInputStream() { is ->          
        byte[] buffer = new byte[8192]
        int read = 0
        while( (read = is.read(buffer)) > 0) {
            digest.update(buffer, 0, read);
        }
    }                                                        
    byte[] md5 = digest.digest()
    BigInteger bigInt = new BigInteger(1, md5)
    return bigInt.toString(16).padLeft(32, '0')
}
 
def file_list     = []
def distinct_list = [:]
def startDate = new Date().format('yyyy/MM/dd HH:mm:ss')
 
new File(TARGET_DIR).eachFileRecurse { file -> 
    if( file.size() > LIMIT_SIZE ) {
        def md5 = null
        try {
            md5 = md5sum(file)
        } catch(ex) {
            md5 = null
        }
        if (md5 != null) { 
            if(distinct_list[md5] == null)
                distinct_list[md5] = 1
            else
                distinct_list[md5] += 1
            file_list << (md5+"|"+file)
        }
    }
}
 
distinct_list = distinct_list.sort { a, b -> b.value <=> a.value }
 
distinct_list.each { md5, cnt ->
    if( cnt > 1) {
       println "\n[ $md5 ]"
       file_list.each { file -> 
           def (md5_2, filename) = file.split(/\|/)
           if(md5 == md5_2) { println filename }
       }
    }
}
 
def endDate = new Date().format('yyyy/MM/dd HH:mm:ss')
println "\n\n\n"
println "================================================================================"
println "Start Time : $startDate"
println "End   Time : $endDate"
println "================================================================================"



Groovy에서 md5sum 을 구하려면, Java 의 MessageDigest 모듈을 이용해야 한다. 안타깝게도 file 에 대한 md5 checksum 을 구하는 부분은 구현되어 있지 않기 때문에 별도로 구현해야 한다. 다른 언어에서의 방법과 유사하므로 그리 어렵지는 않다. Java 의 모듈을 그대로 이용해야하기 때문에, 구현은 Java 의 그것과 거의 같다.
import java.security.MessageDigest
 
def md5sum(final file) {
    MessageDigest digest = MessageDigest.getInstance("MD5")
    file.withInputStream() { is ->          
        byte[] buffer = new byte[8192]
        int read = 0
        while( (read = is.read(buffer)) > 0) {
            digest.update(buffer, 0, read);
        }
    }                                                        
    byte[] md5 = digest.digest()
    BigInteger bigInt = new BigInteger(1, md5)
    return bigInt.toString(16).padLeft(32, '0')
}
 
println md5sum(new File("md5sum.groovy"))



기본 모듈 crypto 를 이용해서 파일의 md5sum 을 구하는 스크립트를 만들어보자. 별도로 제공이 안되므로 직접 만들어써야 한다. 일단 아래와 같이 만들어보면 md5 뿐만 아니라 sha1 에 대한 checksum 을 쉽게 구할 수도 있을 것이다.
  • 동기IO를 이용한 방법 (파일을 조금씩 읽어서 처리하는 방법) => Windows 에서는 에러가 발생하고 제대로 실행되지 않는다. 원인을 찾아보려고 했으나, 알 수가 없다. 혹시 누구 아시는분? node.js v0.6.11 에서 문제가 있었으나, v0.6.12 에서 잘 되고 있다.

    var fs     = require('fs');
    var crypto = require('crypto');
     
    var filename = 'walk_test.js';
    function md5sum(filename) {
      var genChecksum = null;
      var checksum = crypto.createHash('md5');
      var bytesRead = 1;
      var pos = 0
      var buffer = new Buffer(1024*64); // 64Kbyte
      var fd = fs.openSync(filename, 'r');
      var data = null;
      while (bytesRead > 0) {
        bytesRead = fs.readSync(fd, buffer, 0, buffer.length, pos);
        pos += bytesRead;
        if (bytesRead === buffer.length) {
          checksum.update(buffer);
        } else {
          data = buffer.slice(0, bytesRead);
          checksum.update(data);
        }
      }
      fs.closeSync(fd);
      genChecksum = checksum.digest('hex');
      return genChecksum;
    }
    
    console.log('checksum is : ' + md5sum(filename));
    
  • 동기IO를 이용한 방법 (파일은 한번에 읽어서 처리하는 방법) ⇒ 주의 : 한번에 큰 파일을 읽어서 처리하므로, 메모리를 많이 소모하게 될 것이다.

    var fs     = require('fs');
    var crypto = require('crypto');
     
    var filename = 'walk_test.js';
     
    function md5sum(filename) {
      var genChecksum = null;
      var checksum = crypto.createHash('md5');
      var data = fs.readFileSync(filename);
      checksum.update(data);
      genChecksum = checksum.digest('hex');
      return genChecksum;
    }
     
    console.log('checksum is : ' + md5sum(filename));
    
  • 비동기IO를 이용한 방법

    var fs     = require('fs');
    var crypto = require('crypto');
     
    var filename = 'walk_test.js';
    var genChecksum = null;
    var checksum = crypto.createHash('md5');
    var fin = fs.ReadStream(filename);
    fin.on('data', function(data){
      checksum.update(data);
    });
     
    fin.on('end', function(){
      genChecksum = checksum.digest('hex');
      console.log('checksum is : ' + genChecksum);
    });
    



+ Recent posts