FileChannel ByteBuffer und Hashing-Dateien

Ich habe eine Datei-Hashing-Methode in Java erstellt, die die Eingabezeichenfolgendarstellung von a übernimmtfilepath+filename und berechnet dann den Hash dieser Datei. Der Hash kann ein beliebiger Java-Hashing-Algorithmus sein, wie zMD2 durchSHA-512.

Ich versuche, jeden Tropfen der Leistung herauszufinden, da diese Methode ein wesentlicher Bestandteil eines Projekts ist, an dem ich arbeite. Mir wurde geraten, es mit zu versuchenFileChannel anstelle eines regulärenFileInputStream.

Meine ursprüngliche Methode:

    /**
     * Gets Hash of file.
     * 
     * @param file String path + filename of file to get hash.
     * @param hashAlgo Hash algorithm to use. <br/>
     *     Supported algorithms are: <br/>
     *     MD2, MD5 <br/>
     *     SHA-1 <br/>
     *     SHA-256, SHA-384, SHA-512
     * @return String value of hash. (Variable length dependent on hash algorithm used)
     * @throws IOException If file is invalid.
     * @throws HashTypeException If no supported or valid hash algorithm was found.
     */
    public String getHash(String file, String hashAlgo) throws IOException, HashTypeException {
        StringBuffer hexString = null;
        try {
            MessageDigest md = MessageDigest.getInstance(validateHashType(hashAlgo));
            FileInputStream fis = new FileInputStream(file);

            byte[] dataBytes = new byte[1024];

            int nread = 0;
            while ((nread = fis.read(dataBytes)) != -1) {
                md.update(dataBytes, 0, nread);
            }
            fis.close();
            byte[] mdbytes = md.digest();

            hexString = new StringBuffer();
            for (int i = 0; i < mdbytes.length; i++) {
                hexString.append(Integer.toHexString((0xFF & mdbytes[i])));
            }

            return hexString.toString();

        } catch (NoSuchAlgorithmException | HashTypeException e) {
            throw new HashTypeException("Unsuppored Hash Algorithm.", e);
        }
    }

Refactored Methode:

    /**
     * Gets Hash of file.
     * 
     * @param file String path + filename of file to get hash.
     * @param hashAlgo Hash algorithm to use. <br/>
     *     Supported algorithms are: <br/>
     *     MD2, MD5 <br/>
     *     SHA-1 <br/>
     *     SHA-256, SHA-384, SHA-512
     * @return String value of hash. (Variable length dependent on hash algorithm used)
     * @throws IOException If file is invalid.
     * @throws HashTypeException If no supported or valid hash algorithm was found.
     */
    public String getHash(String fileStr, String hashAlgo) throws IOException, HasherException {

        File file = new File(fileStr);

        MessageDigest md = null;
        FileInputStream fis = null;
        FileChannel fc = null;
        ByteBuffer bbf = null;
        StringBuilder hexString = null;

        try {
            md = MessageDigest.getInstance(hashAlgo);
            fis = new FileInputStream(file);
            fc = fis.getChannel();
            bbf = ByteBuffer.allocate(1024); // allocation in bytes

            int bytes;

            while ((bytes = fc.read(bbf)) != -1) {
                md.update(bbf.array(), 0, bytes);
            }

            fc.close();
            fis.close();

            byte[] mdbytes = md.digest();

            hexString = new StringBuilder();

            for (int i = 0; i < mdbytes.length; i++) {
                hexString.append(Integer.toHexString((0xFF & mdbytes[i])));
            }

            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            throw new HasherException("Unsupported Hash Algorithm.", e);
        }
    }

Beide geben einen korrekten Hash zurück, die überarbeitete Methode scheint jedoch nur bei kleinen Dateien zusammenzuarbeiten. Wenn ich eine große Datei übergebe, wird sie vollständig erstickt und ich kann nicht herausfinden, warum. Ich bin neu inNIO also bitte beraten.

EDIT: Ich habe vergessen zu erwähnen, dass ich SHA-512's zum Testen durchwerfe.

UPDATE: Aktualisierung mit meiner jetzt aktuellen Methode.

    /**
     * Gets Hash of file.
     * 
     * @param file String path + filename of file to get hash.
     * @param hashAlgo Hash algorithm to use. <br/>
     *     Supported algorithms are: <br/>
     *     MD2, MD5 <br/>
     *     SHA-1 <br/>
     *     SHA-256, SHA-384, SHA-512
     * @return String value of hash. (Variable length dependent on hash algorithm used)
     * @throws IOException If file is invalid.
     * @throws HashTypeException If no supported or valid hash algorithm was found.
     */
    public String getHash(String fileStr, String hashAlgo) throws IOException, HasherException {

        File file = new File(fileStr);

        MessageDigest md = null;
        FileInputStream fis = null;
        FileChannel fc = null;
        ByteBuffer bbf = null;
        StringBuilder hexString = null;

        try {
            md = MessageDigest.getInstance(hashAlgo);
            fis = new FileInputStream(file);
            fc = fis.getChannel();
            bbf = ByteBuffer.allocateDirect(8192); // allocation in bytes - 1024, 2048, 4096, 8192

            int b;

            b = fc.read(bbf);

            while ((b != -1) && (b != 0)) {
                bbf.flip();

                byte[] bytes = new byte[b];
                bbf.get(bytes);

                md.update(bytes, 0, b);

                bbf.clear();
                b = fc.read(bbf);
            }

            fis.close();

            byte[] mdbytes = md.digest();

            hexString = new StringBuilder();

            for (int i = 0; i < mdbytes.length; i++) {
                hexString.append(Integer.toHexString((0xFF & mdbytes[i])));
            }

            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            throw new HasherException("Unsupported Hash Algorithm.", e);
        }
    }

Daher habe ich versucht, das MD5 einer 2,92-GB-Datei anhand meines ursprünglichen Beispiels und des Beispiels meines neuesten Updates einem Benchmark zu unterziehen. Natürlich ist jeder Benchmark relativ, da OS- und Disk-Caching und andere "magische" Dinge vor sich gehen, die das wiederholte Lesen der gleichen Dateien verzerren ... aber hier ist ein Überblick über einige Benchmarks. Ich lud jede Methode auf und feuerte sie fünfmal ab, nachdem ich sie neu kompiliert hatte. Der Benchmark wurde aus dem letzten (5.) Durchlauf genommen, da dies der "heißeste" Durchlauf für diesen Algorithmus und jede "Magie" wäre (in meiner Theorie jedenfalls).

Here's the benchmarks so far: 

    Original Method - 14.987909 (s) 
    Latest Method - 11.236802 (s)

Das ist ein25.03% decrease in der Zeit genommen, um die gleiche 2.92GB-Datei zu hashen. Ziemlich gut.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage