您的位置:首页 > 其它

MD5文件加密以及关于NIO中的FileChannel.map的一点看法

2010-12-17 15:10 513 查看
前些天忽然对MD5的加密很感兴趣。而也发现J***A的API中java.security.MessageDigest 并没有提供直接用于文件的方法。而我其实挺需要这个方法的,所以决定自己写一个。
最初的版本是把文件全读入内存为byte[],然后用API加密:
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MD5Util{
public static void main(String[] args){
if(args.length != 1){
System.out.println("Incorrect parameter counts.");
System.exit(-1);
}
FileInputStream fin = null;
try{
fin = new FileInputStream(args[0]);
System.out.println(DigestUtils.md5Hex(IOUtils.toByteArray(fin)));
}catch(FileNotFoundException e){
System.out.println("File not found: "+args[0]);
System.exit(-1);
}catch(IOException e){
System.out.println("File IO error: "+e);
System.exit(-1);
}

}
}

这样对小文件的处理还是很快的,不过文件一大起来,它就会造成内存溢出。
初时不知怎么处理,上网查了一下,发现有两个解决办法的:
一,用NIO的ByteBuffer,先用FileChannel.map取得一个ByteBuffer,把文件映射入内存,然后再干。
FileChannel ch = new FileInputStream(file).getChannel();
MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
messagedigest.update(byteBuffer);
return messagedigest.digest();
二, 研究了一下MD5的C源码之后,发现其实HASH加密算法都是分段计算的。再看看java.security.MessageDigest类的说明,原来提供了很好的方法,就是新建一个MD类,分段地把byte[]调用update(byte[])。等到了文件末尾,再一次过调用digest()就可以返回想要的结果的。So cool!
public static byte[] MD5File(String fileName) {
byte[] buf = new byte[4096]; //这个byte[]的长度可以是任意的。
MessageDigest md;
boolean fileIsNull = true;

try {
FileInputStream fis = new FileInputStream(fileName);
int len = 0;
md = MessageDigest.getInstance("MD5");

len = fis.read(buf);
if (len > 0) {
fileIsNull = false;
while (len > 0){
md.update(buf, 0, len);
len = fis.read(buf);
}
}
} catch (Exception e) {
return null;
}

if (fileIsNull)
return null;
else
return md.digest();
}

最后试了一下两种方式的效率,再与linux内建的md5sum(C版本)对比,发现用FileChannel.map的效率并不高,而且文件的长度超过Integer.MAX_VALUE之后,map方法就会抛出异常,说文件太大了…我倒^_^!!!
而我用分段读取的方式挺好的,在文件小的情况下跟md5sum差不多,中等大的(几百M)文件比C版本的md5sum慢几秒钟,而对于5G多的文件,都是用了2分多钟,这就差不多了!HOHO!
也试过将FileInputStream.read改为NIO中FileChannel.read,发现速度竟然慢了,鉴于java的IO用NIO重写过了,所以我就只用普通的IO了,而且普通IO的接口更友好些,我个人觉得。

至于NIO中的内存映射,我用过后觉得真是一种鸡肋。它的文档上说了,对于小文件,由于创建文件内存MAPPING这么大件事又划不来,1G多的文件才值得。而实际上,3G以上的文件有很多的啊,至少我硬盘上就很多。但超过了int的值的它就支持不了!这样……唉,适用范围太狭窄了。而且它map了之后不会释放资源的,我就试过调试的时候,在eclipse里,run了这程序两次,就死机了,这是它不释放这个mapping资源而耗尽了我4G的内存。唉,这个东西,慎用!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐