zoukankan      html  css  js  c++  java
  • 判断文件的唯一性MD5

    JAVA中获取文件MD5值的四种方法

     

      JAVA中获取文件MD5值的四种方法其实都很类似,因为核心都是通过JAVA自带的MessageDigest类来实现。获取文件MD5值主要分为三个步骤,第一步获取文件的byte信息,第二步通过MessageDigest类进行MD5加密,第三步转换成16进制的MD5码值。几种方法的不同点主要在第一步和第三步上。具体可以看下面的例子:

    方法一、

    复制代码
     1   private final static String[] strHex = { "0", "1", "2", "3", "4", "5",
     2             "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
     3 
     4     public static String getMD5One(String path) {
     5         StringBuffer sb = new StringBuffer();
     6         try {
     7             MessageDigest md = MessageDigest.getInstance("MD5");
     8             byte[] b = md.digest(FileUtils.readFileToByteArray(new File(path)));
     9             for (int i = 0; i < b.length; i++) {
    10                 int d = b[i];
    11                 if (d < 0) {
    12                     d += 256;
    13                 }
    14                 int d1 = d / 16;
    15                 int d2 = d % 16;
    16                 sb.append(strHex[d1] + strHex[d2]);
    17             }
    18         } catch (NoSuchAlgorithmException e) {
    19             e.printStackTrace();
    20         } catch (IOException e) {
    21             e.printStackTrace();
    22         }
    23         return sb.toString();
    24     }
    复制代码

      方法一是比较原始的一种实现方法,首先将文件一次性读入内存,然后通过MessageDigest进行MD5加密,最后再手动将其转换为16进制的MD5值。

    方法二、

    复制代码
     1   public static String getMD5Two(String path) {
     2         StringBuffer sb = new StringBuffer("");
     3         try {
     4             MessageDigest md = MessageDigest.getInstance("MD5");
     5             md.update(FileUtils.readFileToByteArray(new File(path)));
     6             byte b[] = md.digest();
     7             int d;
     8             for (int i = 0; i < b.length; i++) {
     9                 d = b[i];
    10                 if (d < 0) {
    11                     d = b[i] & 0xff;
    12                     // 与上一行效果等同
    13                     // i += 256;
    14                 }
    15                 if (d < 16)
    16                     sb.append("0");
    17                 sb.append(Integer.toHexString(d));
    18             }
    19         } catch (NoSuchAlgorithmException e) {
    20             e.printStackTrace();
    21         } catch (IOException e) {
    22             e.printStackTrace();
    23         }
    24         return sb.toString();
    25     }
    复制代码

      方法二与方法一不同的地方主要是在步骤三,这里借助了Integer类的方法实现16进制的转换,比方法一更简洁一些。PS:JAVA中byte是有负数的,代码中&0xff的操作与计算机中数据存储的原理有关,即负数存储的是二进制的补码,有兴趣的童鞋可以挖一下,这里不展开说。

    方法三、

    复制代码
     1   public static String getMD5Three(String path) {
     2         BigInteger bi = null;
     3         try {
     4             byte[] buffer = new byte[8192];
     5             int len = 0;
     6             MessageDigest md = MessageDigest.getInstance("MD5");
     7             File f = new File(path);
     8             FileInputStream fis = new FileInputStream(f);
     9             while ((len = fis.read(buffer)) != -1) {
    10                 md.update(buffer, 0, len);
    11             }
    12             fis.close();
    13             byte[] b = md.digest();
    14             bi = new BigInteger(1, b);
    15         } catch (NoSuchAlgorithmException e) {
    16             e.printStackTrace();
    17         } catch (IOException e) {
    18             e.printStackTrace();
    19         }
    20         return bi.toString(16);
    21     }
    复制代码

      方法三与前面两个方法相比,在读入文件信息上有点不同。这里是分多次将一个文件读入,对于大型文件而言,比较推荐这种方式,占用内存比较少。步骤三则是通过BigInteger类提供的方法进行16进制的转换,与方法二类似。

    方法四、

    1   DigestUtils.md5Hex(new FileInputStream(path));

      方法四应该是最便捷的吧,哈哈,好东西要留在最后,如果你只需要使用标准的MD5,其实一行代码就够了,JAVA自带的commons-codec包就提供了获取16进制MD5值的方法。其底层实现上,也是分多次将一个文件读入,类似方法三。所以性能上也不错。

      总结:其实方法都是类似的,推荐使用方法四,简洁且性能不错,当然,如果要做一些调整什么的,可以根据自己的需求进行方法的选择。

    PS:其实还有一个重点,就是如何知道自己生成的MD5值是否正确呢?

      方法很多,其实有一个挺简单的方法,不需要另外安装什么软件。使用windows自带的命令即可:certutil -hashfile [文件路径] MD5,例子如下:

    最后给MD5的Java实现:

    public class MD5{
        /*
        *四个链接变量
        */
        private final int A=0x67452301;
        private final int B=0xefcdab89;
        private final int C=0x98badcfe;
        private final int D=0x10325476;
        /*
        *ABCD的临时变量
        */
        private int Atemp,Btemp,Ctemp,Dtemp;
         
        /*
        *常量ti
        *公式:floor(abs(sin(i+1))×(2pow32)
        */
        private final int K[]={
            0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
            0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
            0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
            0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
            0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
            0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
            0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
            0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
            0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
            0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
            0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
            0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
            0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
        /*
        *向左位移数,计算方法未知
        */
        private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
            12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
            4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
            15,21,6,10,15,21,6,10,15,21,6,10,15,21};
         
         
        /*
        *初始化函数
        */
        private void init(){
            Atemp=A;
            Btemp=B;
            Ctemp=C;
            Dtemp=D;
        }
        /*
        *移动一定位数
        */
        private    int    shift(int a,int s){
            return(a<<s)|(a>>>(32-s));//右移的时候,高位一定要补零,而不是补充符号位
        }
        /*
        *主循环
        */
        private void MainLoop(int M[]){
            int F,g;
            int a=Atemp;
            int b=Btemp;
            int c=Ctemp;
            int d=Dtemp;
            for(int i = 0; i < 64; i ++){
                if(i<16){
                    F=(b&c)|((~b)&d);
                    g=i;
                }else if(i<32){
                    F=(d&b)|((~d)&c);
                    g=(5*i+1)%16;
                }else if(i<48){
                    F=b^c^d;
                    g=(3*i+5)%16;
                }else{
                    F=c^(b|(~d));
                    g=(7*i)%16;
                }
                int tmp=d;
                d=c;
                c=b;
                b=b+shift(a+F+K[i]+M[g],s[i]);
                a=tmp;
            }
            Atemp=a+Atemp;
            Btemp=b+Btemp;
            Ctemp=c+Ctemp;
            Dtemp=d+Dtemp;
         
        }
        /*
        *填充函数
        *处理后应满足bits≡448(mod512),字节就是bytes≡56(mode64)
        *填充方式为先加一个0,其它位补零
        *最后加上64位的原来长度
        */
        private int[] add(String str){
            int num=((str.length()+8)/64)+1;//以512位,64个字节为一组
            int strByte[]=new int[num*16];//64/4=16,所以有16个整数
            for(int i=0;i<num*16;i++){//全部初始化0
                strByte[i]=0;
            }
            int    i;
            for(i=0;i<str.length();i++){
                strByte[i>>2]|=str.charAt(i)<<((i%4)*8);//一个整数存储四个字节,小端序
            }
            strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
            /*
            *添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个,这里长度只用了32位
            */
            strByte[num*16-2]=str.length()*8;
                return strByte;
        }
        /*
        *调用函数
        */
        public String getMD5(String source){
            init();
            int strByte[]=add(source);
            for(int i=0;i<strByte.length/16;i++){
            int num[]=new int[16];
            for(int j=0;j<16;j++){
                num[j]=strByte[i*16+j];
            }
            MainLoop(num);
            }
            return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);
         
        }
        /*
        *整数变成16进制字符串
        */
        private String changeHex(int a){
            String str="";
            for(int i=0;i<4;i++){
                str+=String.format("%2s", Integer.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ''0');
     
            }
            return str;
        }
        /*
        *单例
        */
        private static MD5 instance;
        public static MD5 getInstance(){
            if(instance==null){
                instance=new MD5();
            }
            return instance;
        }
         
        private MD5(){};
         
        public static void main(String[] args){
            String str=MD5.getInstance().getMD5("");
            System.out.println(str);
        }
    }
  • 相关阅读:
    非对称加密-RSA公钥加密,私钥解密,私钥加签,公钥验签
    设置mysql数据库本地连接或外部可连接
    mysql自增长主键,删除数据后,将主键顺序重新排序
    非Service层和Controller层调用ssm框架中的方法
    DES加密算法(密文只有字符串和数字)java和android加密的结果一致(可放在url中)
    SpringBoot ajax Restful整合
    java中线程执行流程详解
    在 CSS 中直接引用 fontawesome 图标(附码表)
    C++内存管理~
    操作系统那些事儿
  • 原文地址:https://www.cnblogs.com/jingcaijueyan/p/9456254.html
Copyright © 2011-2022 走看看