锁定老帖子 主题:Java 自带MD5 校验算法,别上当
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-10-22
最后修改:2012-10-22
前天第一次发表博客到论坛,关于Java文件监控一文,帖子地址在:http://www.iteye.com/topic/1127281 评论的朋友很多,下载代码的朋友很不少,感谢在论坛上看我帖子的朋友,还有回复评论的朋友,给我提供建议的朋友。
从这些建议中,虽然语言简短,但是却有的是一语中的,这里说一下一下关于帖子的代码中HashFile中的MD5文件校验算法, 该算法是使用Java自带的MessageDigest类,测试结果,获取一个2G文件的MD5码,耗时 971秒,这效率太给力了,可以用坑爹来形容,所以用MD5文件校验码来判断文件是否被修改,对于小文件来说可能还合适,要是对大文件来说,好吧,撞墙死了算了!
HashFile中的代码是这样子的: import java.io.FileInputStream; import java.io.InputStream; import java.security.MessageDigest; public class HashFile { /** * @param args */ public static char[] hexChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; public static String getHash(String fileName, String hashType) throws Exception { InputStream fis; fis = new FileInputStream(fileName); byte[] buffer = new byte[1024]; MessageDigest md5 = MessageDigest.getInstance(hashType); int numRead = 0; while ((numRead = fis.read(buffer)) > 0) { md5.update(buffer, 0, numRead); } fis.close(); return toHexString(md5.digest()); } public static String toHexString(byte[] b) { StringBuilder sb = new StringBuilder(b.length * 2); for (int i = 0; i < b.length; i++) { sb.append(hexChar[(b[i] & 0xf0) >>> 4]); sb.append(hexChar[b[i] & 0x0f]); } return sb.toString(); } }
测试结果:
好吧,自带的MD5算法,上当了,对于检查文件是否更新这个问题来说,现在我使用的解决办法是File 类的lastModified方法,代码这样 private String getHash(String fp){ File file = new File(fp); return String.valueOf(file.lastModified()); } 通过比较文件的最后修改时间来判断文件是否更新,对大文件也轻松拿下, 测试结果是这样:
分析原来HashFile代码,获取MD5校验码的瓶颈是出现在 public static String getHash(String fileName, String hashType) throws Exception { InputStream fis; fis = new FileInputStream(fileName); byte[] buffer = new byte[1024]; MessageDigest md5 = MessageDigest.getInstance(hashType); int numRead = 0; while ((numRead = fis.read(buffer)) > 0) { //瓶颈 md5.update(buffer, 0, numRead); } fis.close(); return toHexString(md5.digest()); } 在上面代码中,while循环N次,2G的文件,循环1024 * 1024 * 2 次,不给力! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-10-22
解决办法呢?
|
|
返回顶楼 | |
发表时间:2012-10-22
superchinaren 写道 解决办法呢?
引用 好吧,自带的MD5算法,上当了,对于检查文件是否更新这个问题来说,现在我使用的解决办法是File 类的lastModified方法,代码这样
对于我之前的程序的解决办法就这样,其他的办法,可能需要另想办法,毕竟MD5文件校验那是需要经过对比每一个字节的,对于大文件来说,还是不建议使用MD5文件校验 |
|
返回顶楼 | |
发表时间:2012-10-22
... ... 这是闹哪样没看明白。
2G的文件校检本来就有那么点慢来着,你去下个工具试试,当然,比起c/c++实现的是慢了不是一星半点。 但是你这个读文件的实现本来也木有充分利用到缓存嘛。 |
|
返回顶楼 | |
发表时间:2012-10-22
来个nio的简单版,看你们老是怀疑java慢
C++ MD5工具验证结果: File: K:\Games\World of Warcraft\Data\common.MPQ Size: 2226587191 bytes Modified: 2008年11月19日 星期三, 12:57:24 MD5: CD9F9C5523F3BA3866B81CCC74ED6476 java运行结果,毫秒 耗时:12672,cd9f9c5523f3ba3866b81ccc74ed6476 核心代码 String hashType = "MD5"; FileInputStream fStream = null; try { MessageDigest md5 = MessageDigest.getInstance(hashType); fStream = new FileInputStream( //"K:\\Games\\World of Warcraft\\Scan.dll"); //"K:\\Games\\World of Warcraft\\Data\\patch-3.MPQ"); "K:\\Games\\World of Warcraft\\Data\\common.MPQ"); FileChannel fChannel = fStream.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(8*1024); long s = System.currentTimeMillis(); for ( int count = fChannel.read( buffer ); count !=-1 ; count = fChannel.read( buffer ) ) { buffer.flip(); md5.update( buffer ); if( !buffer.hasRemaining() ){ //System.out.println("count:"+count); buffer.clear(); } } s = System.currentTimeMillis() - s; System.out.println( "耗时:"+s+","+getString( md5.digest() ) ); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ try { if( fStream!=null ) fStream.close(); } catch (IOException e) { e.printStackTrace(); } } |
|
返回顶楼 | |
发表时间:2012-10-23
chimer 写道 来个nio的简单版,看你们老是怀疑java慢
C++ MD5工具验证结果: File: K:\Games\World of Warcraft\Data\common.MPQ Size: 2226587191 bytes Modified: 2008年11月19日 星期三, 12:57:24 MD5: CD9F9C5523F3BA3866B81CCC74ED6476 java运行结果,毫秒 耗时:12672,cd9f9c5523f3ba3866b81ccc74ed6476 核心代码 String hashType = "MD5"; FileInputStream fStream = null; try { MessageDigest md5 = MessageDigest.getInstance(hashType); fStream = new FileInputStream( //"K:\\Games\\World of Warcraft\\Scan.dll"); //"K:\\Games\\World of Warcraft\\Data\\patch-3.MPQ"); "K:\\Games\\World of Warcraft\\Data\\common.MPQ"); FileChannel fChannel = fStream.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(8*1024); long s = System.currentTimeMillis(); for ( int count = fChannel.read( buffer ); count !=-1 ; count = fChannel.read( buffer ) ) { buffer.flip(); md5.update( buffer ); if( !buffer.hasRemaining() ){ //System.out.println("count:"+count); buffer.clear(); } } s = System.currentTimeMillis() - s; System.out.println( "耗时:"+s+","+getString( md5.digest() ) ); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ try { if( fStream!=null ) fStream.close(); } catch (IOException e) { e.printStackTrace(); } } 看来是不一样的水平写的代码效率差的不是N多,呵呵...多谢指点 |
|
返回顶楼 | |
发表时间:2012-10-23
最后修改:2012-10-23
2G文件不是一次性读取,要访问外存,所以慢了。
2G已经不能说明问题了,要用3 4G的。 |
|
返回顶楼 | |
发表时间:2012-10-23
somefuture 写道 2G文件不是一次性读取,要访问外存,所以慢了。
2G已经不能说明问题了,要用3 4G的。 哈哈,那得等到脖子比长颈鹿还长... |
|
返回顶楼 | |
发表时间:2012-10-23
两者似乎没多大的区别吧
public class MD5 { public static char[] hexChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; public static String toHexString(byte[] b) { StringBuilder sb = new StringBuilder(b.length * 2); for (int i = 0; i < b.length; i++) { sb.append(hexChar[(b[i] & 0xf0) >>> 4]); sb.append(hexChar[b[i] & 0x0f]); } return sb.toString(); } public static String getHash(String fileName, String hashType) throws Exception { InputStream fis = new FileInputStream(fileName); byte[] buffer = new byte[1024]; MessageDigest md5 = MessageDigest.getInstance(hashType); int numRead = 0; while ((numRead = fis.read(buffer)) > 0) { // 瓶颈 md5.update(buffer, 0, numRead); } fis.close(); return toHexString(md5.digest()); } public static String getHashNio(String fileName, String hashType) throws Exception { FileInputStream fStream = null; String hash = null; try { MessageDigest md5 = MessageDigest.getInstance(hashType); fStream = new FileInputStream(fileName); FileChannel fChannel = fStream.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(8 * 1024); for (int count = fChannel.read(buffer); count != -1; count = fChannel.read(buffer)) { buffer.flip(); md5.update(buffer); if (!buffer.hasRemaining()) { // System.out.println("count:"+count); buffer.clear(); } } hash = toHexString(md5.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (fStream != null) fStream.close(); } catch (IOException e) { e.printStackTrace(); } } return hash; } public static void main(String[] args) throws Exception { String fileName = "G:/QQDownload/The.Man.From.Earth.2007.mkv"; long t1 = System.currentTimeMillis(); System.out.println(MD5.getHash(fileName, "MD5")); long t2 = System.currentTimeMillis(); System.out.println("耗时:" + (t2 - t1) + " ms"); System.out.println(MD5.getHashNio(fileName, "MD5")); System.out.println("耗时:" + (System.currentTimeMillis() - t2) + " ms"); } } 运行3次结果如下: d1b3870825234c4fbf0f21993ad9e00d 耗时:25516 ms d1b3870825234c4fbf0f21993ad9e00d 耗时:24578 ms d1b3870825234c4fbf0f21993ad9e00d 耗时:27875 ms d1b3870825234c4fbf0f21993ad9e00d 耗时:34344 ms d1b3870825234c4fbf0f21993ad9e00d 耗时:29531 ms d1b3870825234c4fbf0f21993ad9e00d 耗时:28781 ms NIO的版本性能似乎没什么优势 |
|
返回顶楼 | |
发表时间:2012-10-23
而且,两者之间的些微差别会不会仅仅只是Byte Buffer大小造成的
|
|
返回顶楼 | |