2009-11-23 9 views
5

Sto cercando di tenere traccia di un insieme di file, che possono avere lo stesso nome e metadati. Mi piacerebbe usare un hash per differenziarlo e usarlo come ID univoco, ma non sono sicuro di quale utilizzare? I file sono relativamente piccoli (nell'intervallo di 100 kb) e mi piacerebbe essere in grado di eseguire l'hash in meno di 10 secondi. Quale hash (che viene integrato in Java 1.5) potrebbe soddisfare le mie esigenze?Quale hash usare per l'univocità del file in Java

risposta

15

Si noti che un hash di questo tipo sarà mai essere unico però, con l'uso fuori da uno efficace si ha una buona possibilità di non avere mai una collisione.

Se non si è interessati alla sicurezza (ad esempio qualcuno che cerca deliberatamente di rompere il proprio hash), usare semplicemente l'hash MD5 vi darà un eccellente hash con il minimo sforzo.

È probabile che si possa eseguire un hash SHA di 100Kb in ben meno di 10 secondi e, sebbene lo SHA-1 sia ancora teoricamente difettoso, è più resistente di MD5.

MessageDigest otterrà una implementazione di entrambi.

Ecco alcuni examples of using it with streams.

Inoltre, dovrei notare che this excellent answer from jarnbjo indica che anche l'hash SHA fornito in Java è in grado di superare i 20 MB/s anche su hardware x86 relativamente modesto. Ciò implicherebbe 5-10 millisecondi di prestazioni di livello su 100 KB di dati di input (in memoria) in modo che l'obiettivo di meno di 10 secondi sia una sovrastima enorme dello sforzo richiesto. È probabile che sarai interamente limitato dalla velocità con cui puoi leggere i file dal disco piuttosto che dall'algoritmo di hash che usi.

Se si ha bisogno di strong hashing crittografico si dovrebbe indicare questo nella domanda. Anche se SHA di un certo sapore superiore a 1 è ancora probabilmente la scelta migliore a meno che non si desideri utilizzare una libreria esterna come Bouncy Castle poiché non si dovrebbe mai tentare di eseguire il proprio crypto se esiste un'implementazione ben consolidata.

Per alcuni esempi di codice ragionevolmente efficiente suggerisco this how to I punti salienti dei quali può essere distillato in quanto segue (regolare la dimensione del buffer, come si vede in forma):

import java.io.*; 
import java.security.MessageDigest; 

public class Checksum 
{  
    const string Algorithm = "SHA-1"; // or MD5 etc. 

    public static byte[] createChecksum(String filename) throws 
     Exception 
    { 
     InputStream fis = new FileInputStream(filename); 
     try 
     { 
      byte[] buffer = new byte[1024]; 
      MessageDigest complete = MessageDigest.getInstance("MD5"); 
      int numRead; 
      do 
      { 
       numRead = fis.read(buffer); 
       if (numRead > 0) 
       { 
        complete.update(buffer, 0, numRead); 
       } 
      } while (numRead != -1); 
      return complete.digest(); 
     } 
     finally 
     { 
      fis.close(); 
     } 
    } 
} 
+1

+1 per la nota che gli hash non saranno mai univoci. – PSpeed

+0

Ottima risposta in tutto, grazie. –

0

utilizzare un hash SHA1 basato sul contenuto è ciò che utilizzo. MD5 è più debole e più veloce, ma la velocità dei processori moderni non è un problema.

5

si potrebbe usare MessageDigest con SHA1:

MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); 
    InputStream is = new FileInputStream(aFile); 
    int res; 

    while ((res = inputStream.read()) != -1) { 
     digester.update((byte) res); 
    } 

    byte[] digest = messageDigest.digest(); 
+0

@downvoter: per favore spiega il tuo downvote o è inutile – dfa

+0

senza indizio, ma è una risposta ragionevole, quindi ecco un +1 per compensare – ShuggyCoUk

0

qui è il mio modo di farlo, penso che questo dovrebbe funzionare veloce, controllare se si completa in 10 secondi

package utils; 

import java.security.MessageDigest; 
import java.security.NoSuchAlgorithmException; 

/** 
* This class used to compute the hash value of any string 
*/ 
public class MyHasher { 
private static final String ALGORITHM = "MD5"; 
static MessageDigest md = null; 

static{ 
    try { 
     md = MessageDigest.getInstance(ALGORITHM); 
    } catch (NoSuchAlgorithmException e) { 
     MyLogger.error("Can't find implementation of "+ALGORITHM+" algorithm", e); 
    } 
} 

/** 
* Compute hash value of any string 
* @param arg the string to compute hash value of. 
* @return the hex hash value as a string. 
*/ 
public static String getHash(String arg) { 
    md.update(arg.getBytes()); 
    byte[] hashValue = md.digest(); 

    return convertToHex(hashValue); 
} 
/** 
* Converts byte array to the human readable string of hex'es 
* @param data the byte array to convert 
* @return string representation of the hex'es of the byte array 
*/ 
public static String convertToHex(byte[] data){ 
    StringBuffer buf = new StringBuffer(); 
    for(int i=0;i<data.length;i++){ 
     int halfbyte = (data[i]>>>3)&0x0F; 
     int two_halfs = 0; 
     do{ 
      if((0<=halfbyte) && (halfbyte <=9)) 
       buf.append((char) ('0'+halfbyte)); 
      else 
       buf.append((char) ('a'+(halfbyte-10))); 
      halfbyte = data[i] & 0x0F; 
     }while(two_halfs++ <1); 
    }  
    return buf.toString(); 
} 
} 
+0

Aggiungerei la clausola a questo che forzando l'intero file da leggere in memoria tramite una stringa è è probabile che sia inefficiente, richiede più memoria del necessario e ha implicazioni se si desidera eseguire l'hash di un grezzo file files byte piuttosto che forzarlo a diventare caratteri larghi prima (ad esempio se si desidera che uno strumento standard esterno sia anche in grado di cancellarlo) – ShuggyCoUk