byte[]转16进制bug总结

byte[]转16进制bug总结

(一)问题

         项目中需要对文件做md5sum,分两步走:1、对文件流的每个字节用md5实例进行update,然后进行digest2digest返回长度为16byte数组,一般我们需要把byte数组转成16进制字符串(很多开源的md5加密算法如此实现,真正的原因还不是很理解,可能是便于查看和传输)。具体的实现代码如下:

         /**

    * 对文件进行md5 sum操作

    * @param checkFile 要进行做md5 sum的文件

    * @return

    */

    public static String md5sum(File checkFile){

       String md5sumResult = "";

       if(checkFile == null || (!checkFile.exists())){

           return md5sumResult;

       }

       MessageDigest digest = MessageDigest.getInstance("MD5");

       InputStream is = new FileInputStream(checkFile);

       byte[] buffer = new byte[8192];

       int read = 0;

       try {

           while( (read = is.read(buffer)) > 0) {

              digest.update(buffer, 0, read);

           }

           byte[] md5sum = digest.digest();

           BigInteger bigInt = new BigInteger(1, md5sum);

           md5sumResult = bigInt.toString(16);

       }

       catch(IOException e) {

           throw new RuntimeException("Unable to process file for MD5", e);

       }

       finally {

           try {

              is.close();

           }

           catch(IOException e) {

              throw new RuntimeException("Unable to close input stream for MD5 calculation", e);

           }

       }

      

       return md5sumResult;

    }

 

    其中黄色背景色的转换方式是有问题的。为什么用bigint16进制会有问题呢,原因是bigint进行16进制转换的时候第一个0被自动去掉了.

   

 

(二)正确解决方式

那正确的方式是怎么样的呢?下面有两种不同的转换方式,但是原理其实是一致的。

    第一种正确的方式(由王建提供):

    /**

    * 将字节数组转换为16进制字符串

    *

    * @param buffer

    * @return

    */

   public static String toHex(byte[] buffer) {

      StringBuffer sb = new StringBuffer(buffer.length * 2);

      for (int i = 0; i < buffer.length; i++) {

       sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16));

       sb.append(Character.forDigit(buffer[i] & 15, 16));

      }

 

      return sb.toString();

   }

 

    第二种正确的方式:

    public static String bytes2HexString(byte[] b) {

       String ret = "";

       for (int i = 0; i < b.length; i++) {

           String hex = Integer.toHexString(b[i] & 0xFF);

           if (hex.length() == 1) {

              hex = '0' + hex;

           }

           ret += hex;

       }

       return ret;

    }

 

(三)问题分析

    Md5算法对任何长度的字符串进行编码最后输出是128位长整数,也就是长度为16byte数组。我们项目调用的是jdk实现的md5算法,所以一般是没问题的。

    接下来我们要处理的事情,分别循环数组,把每个字节转换成216进制字符,也就是说每4位转成一个16进制字符。

   

    上面正确的两种方式也就是做了这样的事情。

    第一种方式:

       Character.forDigit((buffer[i] & 240) >> 4, 16)把字节的高4位取出右移4位换算成int,然后通过forDigit转换成16进制字符

       Character.forDigit(buffer[i] & 15, 16)把字节的低4位取出换算成int,然后通过forDigit转换成16进制字符

   

    第二种方式:

       Integer.toHexString(b[i] & 0xFF)把整个字节转成int,然后toHexString也就是做高4位和低4位的运算。但是这个方法如果高四位是0的话就不输出任何东西,

所以在输出的字符前加0即可。

       b[i] & 0xFF就是把byte转成int,为什么用与oxff做与运算,是因为如果b[i]是负数的话,从8位变成32位会补1,所以需要与0xff做与运算,可以把前面的24位全部清零,又可以表示成原来的字节了。

   

 

附:

尽量使用开源提供的工具包,比如:

org.apache.commons.codec.digest.DigestUtils.md5Hex(InputStream data)来对文件流进行md5即可,更加方便,可靠。



你可能感兴趣的:(byte[]转16进制bug总结)