Ingrid Cooper
Ingrid Cooper

Reputation: 1211

How to generate an MD5 checksum for a file in Android?

In my app I have a requirement to generate an MD5 checksum for a file. Could you please tell me if there is any way in which this can be achieved?

Thank you.

Upvotes: 75

Views: 97221

Answers (16)

Interkot
Interkot

Reputation: 737

With OKio, it is a one-liner:

val md5_as_hex_string = Okio.buffer(Okio.source(file).readByteString().md5().hex()

Upvotes: 0

Juan Rada
Juan Rada

Reputation: 3766

    fun md5(file: File): String {
        val digest = MessageDigest.getInstance(MD5_ALGORITHM)
        file.inputStream().buffered(BUFFER_SIZE).use { it.iterator().forEach(digest::update) }
        return digest.digest().joinToString("") { "%02x".format(it) }
    }

Upvotes: 0

Toufiqul Haque Mamun
Toufiqul Haque Mamun

Reputation: 121

 public static String md5(String data) throws NoSuchAlgorithmException {
    // Get the algorithm:
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    // Calculate Message Digest as bytes:
    byte[] digest = md5.digest(data.getBytes(StandardCharsets.UTF_8));
    // Convert to 32-char long String:
    return String.format("%032x", new BigInteger(1, digest));
}

Upvotes: 2

broc.seib
broc.seib

Reputation: 22731

Here's clean little kotlin extension function. Works well on large files too.

fun File.md5(): String {
    val md = MessageDigest.getInstance("MD5")
    return this.inputStream().use { fis ->
        val buffer = ByteArray(8192)
        generateSequence {
            when (val bytesRead = fis.read(buffer)) {
                -1 -> null
                else -> bytesRead
            }
        }.forEach { bytesRead -> md.update(buffer, 0, bytesRead) }
        md.digest().joinToString("") { "%02x".format(it) }
    }
}

And the unit test to go with it:

@Test
fun `computes md5 checksum correctly`() {
    val file = File.createTempFile("test-", ".tmp")
    // did md5 on unix machine to comfirm -- put a literal LF at end to compare
    val content = "This is the content of a file." + 0x0a.toChar()
    file.writer().use { w -> w.write(content) }
    assertEquals("a149f5161e873921d84636b2a1b3aad2", file.md5())
}

Upvotes: 12

t3chb0t
t3chb0t

Reputation: 18665

I use these two extensions in Kotlin:

fun File.calcHash(algorithm: String = "MD5", bufferSize: Int = 1024): ByteArray {
    this.inputStream().use { input ->
        val buffer = ByteArray(bufferSize)
        val digest = MessageDigest.getInstance(algorithm)

        read@ while (true) {
            when (val bytesRead = input.read(buffer)) {
                -1 -> break@read
                else -> digest.update(buffer, 0, bytesRead)
            }
        }

        return digest.digest()
    }
}

fun ByteArray.toHexString(): String {
    return this.fold(StringBuilder()) { result, b -> result.append(String.format("%02X", b)) }.toString()
}

Upvotes: 0

Morteza Rastgoo
Morteza Rastgoo

Reputation: 6986

Kotlin version:



fun File.getMD5Hash(path: String): ByteArray {
    val md = MessageDigest.getInstance("MD5")
    val stream: InputStream
    stream = FileInputStream(this)

    val buffer = ByteArray(8192)
    var read: Int
    while (stream.read(buffer).also { read = it } > 0) {
        md.update(buffer, 0, read)
    }
    stream.close()
    return md.digest()
}

Upvotes: 2

ahmedre
ahmedre

Reputation: 1724

If you're using Okio (which most apps use today, directly or indirectly by using OkHttp or Retrofit), you can also do something like this:

return File(path).source().buffer().use { source ->
   HashingSink.md5(blackholeSink()).use { sink ->
     source.readAll(sink)
     sink.hash.hex()
   }
}

This doesn't have to buffer the entire file in memory (the HashingSink will update the md5sum with every write call and then call down to blackholeSink(), which does nothing with the bytes). You can also use HashingSource instead to do something similar.

Upvotes: 8

Mehul Boghra
Mehul Boghra

Reputation: 232

Here is my complete working code. I need to find duplicate files using checksum.

/**
 * this method is used for create check Sum further process...
 *
 * @param models    path of image.
 * @param asyncTask asyncTask of activity
 * @return return array of all files check sum.
 * <p>
 * before put BufferedInputStream
 * with BufferedInputStream (buffer 8192) with Logs
 * with BufferedInputStream (buffer 16384) with Logs
 * with BufferedInputStream (buffer 4194304) with Logs
 * with BufferedInputStream (buffer 32768) with Logs
 * with BufferedInputStream (buffer 32768) without Logs(MD5)
 * with BufferedInputStream (buffer 32768) without Logs (SHA-256)
 */
public static ArrayList<FileModel> generateCheckSum(ScanningListener scanningListener, ArrayList<FileModel> lstAllFile, AsyncTask asyncTask) {
    FileInputStream fis;
    MessageDigest md;
    byte[] buffer;
    int numOfBytesRead;
    byte[] hash;

    long startTime = System.currentTimeMillis();
    for (FileModel s : lstAllFile) {

        if (scanningListener != null)
            scanningListener.onGoingProgress(lstAllFile.size(),lstAllFile.indexOf(s));
        try {
            if (asyncTask.isCancelled()) {
                break;
            }

            fis = new FileInputStream(s.getFilePath());
            md = MessageDigest.getInstance("MD5");
            buffer = new byte[16384];//(1024*2048)


            while ((numOfBytesRead = fis.read(buffer)) > 0) {
                md.update(buffer, 0, numOfBytesRead);
            }

            hash = md.digest();
            s.setChecksum(convertHashToString(hash));
            CustomLog.error("path", String.valueOf(s.getFilePath()));
        } catch (IOException ex) {
            CustomLog.error("IOException", String.valueOf(ex));
        } catch (NoSuchAlgorithmException ex) {
            CustomLog.error("NoSuchAlgorithmException ", String.valueOf(ex));
        }
    }
    long endTime = System.currentTimeMillis();

    long totalTime = endTime - startTime;
    CustomLog.error("Total Time : ", TimeUtils.getDateIn24HrsFormatInUTC(totalTime));
    return lstAllFile;
}

convertHashToString(hash)

/**
 * this method is help for convert hash value into string file and return hash code.
 *
 * @param hash byte array.
 * @return return string of hash code
 */
private static String convertHashToString(byte[] hash) {
    StringBuilder returnVal = new StringBuilder();
    for (byte md5Byte : hash) {
        returnVal.append(Integer.toString((md5Byte & 0xff) + 0x100, 16).substring(1));
    }
    return returnVal.toString();
}

This method will give you a hashmap of all the given files.

I've tried many different types of buffer size as well as MD5 and SHA-1 that you can see in the comments section

Upvotes: 0

hemu
hemu

Reputation: 3261

Convert the file content into string & use the below method:

public static String getMD5EncryptedString(String encTarget){
        MessageDigest mdEnc = null;
        try {
            mdEnc = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            System.out.println("Exception while encrypting to md5");
            e.printStackTrace();
        } // Encryption algorithm
        mdEnc.update(encTarget.getBytes(), 0, encTarget.length());
        String md5 = new BigInteger(1, mdEnc.digest()).toString(16);
        while ( md5.length() < 32 ) {
            md5 = "0"+md5;
        }
        return md5;
    }

Note that this simple approach is suitable for smallish strings, but will not be efficient for large files. For the latter, see dentex's answer.

Upvotes: 45

Mahdi Rafatjah
Mahdi Rafatjah

Reputation: 1072

If you need to calculate MD5 of the big file , you may like to use this:

Import:

import java.security.MessageDigest;

Method:

 private byte[] calculateMD5ofFile(String location) throws IOException, NoSuchAlgorithmException {
        FileInputStream fs= new FileInputStream(location);
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] buffer=new byte[bufferSize];
        int bytes=0;
        do{
            bytes=fs.read(buffer,0,bufferSize);
            if(bytes>0)
                md.update(buffer,0,bytes);

        }while(bytes>0);
        byte[] Md5Sum = md.digest();
        return Md5Sum;
    }

Refrence: https://docs.oracle.com/javase/7/docs/api/java/security/MessageDigest.html


To convert byte array to Hex. use this

public static String ByteArraytoHexString(byte[] bytes) {
    StringBuilder hexString = new StringBuilder();
    for (int i = 0; i < bytes.length; i++) {
        String hex = Integer.toHexString(bytes[i] & 0xFF);
        if (hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }
    return hexString.toString();
}

Refrence In Java, how do I convert a byte array to a string of hex digits while keeping leading zeros?

Upvotes: 5

Dani&#235;l van den Berg
Dani&#235;l van den Berg

Reputation: 2355

I've found the following to work really well:

Process process = Runtime.getRuntime().exec("md5 "+fileLocation);
BufferedReader inputStream = new BufferedReader(new InputStreamReader(process.getInputStream()));
String result = inputStream.readLine().split(" ")[0];

This calls the built-in md5 command. The variable fileLocation is to be set to the location of the file. Of course I do recommend constructing some checks around here to check that the file exists.

Upvotes: 4

gman413
gman413

Reputation: 159

This method worked for me, on a 131MB zip file. MD5 calculated matches that calculated on same file by AccuHash (http://www.accuhash.com)

public static String calculateMD5(File updateFile) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            Log.e("calculateMD5", "Exception while getting Digest", e);
            return null;
        }

        InputStream is;
        try {
            is = new FileInputStream(updateFile);
        } catch (FileNotFoundException e) {
            Log.e("calculateMD5", "Exception while getting FileInputStream", e);
            return null;
        }

        byte[] buffer = new byte[8192];
        int read;
        try {
            while ((read = is.read(buffer)) > 0) {
                digest.update(buffer, 0, read);
            }
            byte[] md5sum = digest.digest();
            BigInteger bigInt = new BigInteger(1, md5sum);
            String output = bigInt.toString(16);
            // Fill to 32 chars
            output = String.format("%32s", output).replace(' ', '0');
            return output;
        } catch (IOException e) {
            throw new RuntimeException("Unable to process file for MD5", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                Log.e("calculateMD5", "Exception on closing MD5 input stream", e);
            }
        }
    }   

Upvotes: 2

XXX
XXX

Reputation: 9072

public static String getMd5OfFile(String filePath)
{
    String returnVal = "";
    try 
    {
        InputStream   input   = new FileInputStream(filePath); 
        byte[]        buffer  = new byte[1024];
        MessageDigest md5Hash = MessageDigest.getInstance("MD5");
        int           numRead = 0;
        while (numRead != -1)
        {
            numRead = input.read(buffer);
            if (numRead > 0)
            {
                md5Hash.update(buffer, 0, numRead);
            }
        }
        input.close();

        byte [] md5Bytes = md5Hash.digest();
        for (int i=0; i < md5Bytes.length; i++)
        {
            returnVal += Integer.toString( ( md5Bytes[i] & 0xff ) + 0x100, 16).substring( 1 );
        }
    } 
    catch(Throwable t) {t.printStackTrace();}
    return returnVal.toUpperCase();
}

Upvotes: 6

dentex
dentex

Reputation: 3263

This code is from the CMupdater, from the CyanogenMod 10.2 android ROM. It tests the downloaded ROMs into the updater App.

code: https://github.com/CyanogenMod/android_packages_apps_CMUpdater/blob/cm-10.2/src/com/cyanogenmod/updater/utils/MD5.java

It works like a charm:

/*
 * Copyright (C) 2012 The CyanogenMod Project
 *
 * * Licensed under the GNU GPLv2 license
 *
 * The text of the license can be found in the LICENSE file
 * or at https://www.gnu.org/licenses/gpl-2.0.txt
 */

package com.cyanogenmod.updater.utils;

import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5 {
    private static final String TAG = "MD5";

    public static boolean checkMD5(String md5, File updateFile) {
        if (TextUtils.isEmpty(md5) || updateFile == null) {
            Log.e(TAG, "MD5 string empty or updateFile null");
            return false;
        }

        String calculatedDigest = calculateMD5(updateFile);
        if (calculatedDigest == null) {
            Log.e(TAG, "calculatedDigest null");
            return false;
        }

        Log.v(TAG, "Calculated digest: " + calculatedDigest);
        Log.v(TAG, "Provided digest: " + md5);

        return calculatedDigest.equalsIgnoreCase(md5);
    }

    public static String calculateMD5(File updateFile) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "Exception while getting digest", e);
            return null;
        }

        InputStream is;
        try {
            is = new FileInputStream(updateFile);
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Exception while getting FileInputStream", e);
            return null;
        }

        byte[] buffer = new byte[8192];
        int read;
        try {
            while ((read = is.read(buffer)) > 0) {
                digest.update(buffer, 0, read);
            }
            byte[] md5sum = digest.digest();
            BigInteger bigInt = new BigInteger(1, md5sum);
            String output = bigInt.toString(16);
            // Fill to 32 chars
            output = String.format("%32s", output).replace(' ', '0');
            return output;
        } catch (IOException e) {
            throw new RuntimeException("Unable to process file for MD5", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                Log.e(TAG, "Exception on closing MD5 input stream", e);
            }
        }
    }
}

Upvotes: 159

yuralife
yuralife

Reputation: 1582

I had the same task and this code works excellent:

public static String fileToMD5(String filePath) {
    InputStream inputStream = null;
    try {
        inputStream = new FileInputStream(filePath);
        byte[] buffer = new byte[1024];
        MessageDigest digest = MessageDigest.getInstance("MD5");
        int numRead = 0;
        while (numRead != -1) {
            numRead = inputStream.read(buffer);
            if (numRead > 0)
                digest.update(buffer, 0, numRead);
        }
        byte [] md5Bytes = digest.digest();
        return convertHashToString(md5Bytes);
    } catch (Exception e) {
        return null;
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) { }
        }
    }
}

private static String convertHashToString(byte[] md5Bytes) {
    String returnVal = "";
    for (int i = 0; i < md5Bytes.length; i++) {
        returnVal += Integer.toString(( md5Bytes[i] & 0xff ) + 0x100, 16).substring(1);
    }
    return returnVal.toUpperCase();
}

Upvotes: 12

Ronak Mehta
Ronak Mehta

Reputation: 5979

buddy try following code

MessageDigest md = MessageDigest.getInstance("MD5");
InputStream is = new FileInputStream("file.txt");
try {
      is = new DigestInputStream(is, md);
      // read stream to EOF as normal...
    }
finally {
      is.close();
   }
byte[] digest = md.digest();

Upvotes: 3

Related Questions