您的位置:首页 > 其它

生成文件md5校验码可满足大于2G情况

2015-12-18 17:17 330 查看
对于文件生成md5校验码,在实际开发中用处很大,有利于比较服务器上是否已经存在该文件,哪怕文件名不一致也无所谓。

由于存在超大文件,经过从网上查找,得到两种方法,一种是利用MappedByteBuffer对于超大文件缓冲,效率高

代码片段如下:

public static String getFileMD5String_old(File file) throws IOException {
FileInputStream in = new FileInputStream(file);
FileChannel ch = in.getChannel();
MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,
file.length());
messagedigest.update(byteBuffer);
return bufferToHex(messagedigest.digest());
}


但是我个人觉得,在这里只是将文件流读了一下,这里使用
MappedByteBuffer
是安全的,它不适合用在拷贝写操作,不能说是bug,适用场景不同罢了。不知道我的理解对不对。该方法经过测试效率还是很高的,接近2G的文件,本机测试5s就完成了md5校验码生成。遗憾的是该种方法只能生成2G以下文件的校验码,原因在于jdk api中限制了2G( Integer.MAX_VALUE)。

所以查询到如下代码:

public static String getFileMD52(File file) {
if (!file.isFile()){
return null;
}
MessageDigest digest = null;
FileInputStream in=null;
byte buffer[] = new byte[1024];
int len;
try {
digest = MessageDigest.getInstance("MD5");
in = new FileInputStream(file);
while ((len = in.read(buffer, 0, 1024)) != -1) {
digest.update(buffer, 0, len);
}
in.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
BigInteger bigInt = new BigInteger(1, digest.digest());
return bigInt.toString(16);
}


该方法适用于所有文件读写,经测试了一个超过2G小于3G的大文件,生成校验码值本机在15s左右。网上有人对该方法深恶痛绝,说达到950多s,我想是因为他的机器性能太差的缘故吧。我对文件块读写改造后,同样的文件生成速度可以降低到9s,不过在小于2G的文件读写上,性能不如上面的代码,本机测试,大概差了2s-4s。所以我个人经过整合,出现如下代码小于2g, 利用第一个的性能,大于2G利用后者的代码如下,如果保守可以只用>2g的操作:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MD5FileUtil {
private static final Logger logger = LoggerFactory
.getLogger(MD5FileUtil.class);

/**
* 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校
* 验下载的文件的正确性用的就是默认的这个组合
*/
protected static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

protected static MessageDigest messagedigest = null;
static {
try {
messagedigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
logger.error("MD5FileUtil messagedigest初始化失败", e);
}
}

/**
* 生成文件的md5校验值
* @param file 文件路径
* @return MD5码返回
* @throws IOException
*/
public static String getFileMD5(File file) throws IOException {
String encrStr="";
// 读取文件
FileInputStream fis = new FileInputStream(file);
// 当文件<2G可以直接读取
if(file.length() <= Integer.MAX_VALUE) {
encrStr = getMD5Lt2G(file, fis);
} else { // 当文件>2G需要切割读取
encrStr = getMD5Gt2G(fis);
}
fis.close();
return encrStr;
}

/**
* 小于2G文件
* @param fis 文件输入流
* @return
* @throws IOException
*/
public static String getMD5Lt2G(File file, FileInputStream fis) throws IOException {
// 加密码
String encrStr="";
FileChannel ch = fis.getChannel();
MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
messagedigest.update(byteBuffer);
encrStr = bufferToHex(messagedigest.digest());
return encrStr;
}

/**
* 超过2G文件的md5算法
* @param fileName
* @param InputStream
* @return
* @throws Exception
*/
public static String getMD5Gt2G(InputStream fis)  throws IOException {
// 自定义文件块读写大小,一般为4M,对于小文件多的情况可以降低
byte[] buffer = new byte[1024*1024*4];
int numRead = 0;
while ((numRead = fis.read(buffer)) > 0) {
messagedigest.update(buffer, 0, numRead);
}
return bufferToHex(messagedigest.digest());
}

/**
*
* @param bt 文件字节流
* @param stringbuffer 文件缓存
*/
private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
// 取字节中高 4 位的数字转换, >>> 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
char c0 = hexDigits[(bt & 0xf0) >> 4];
// 取字节中低 4 位的数字转换
char c1 = hexDigits[bt & 0xf];
stringbuffer.append(c0);
stringbuffer.append(c1);
}

private static String bufferToHex(byte bytes[], int m, int n) {
StringBuffer stringbuffer = new StringBuffer(2 * n);
int k = m + n;
for (int l = m; l < k; l++) {
appendHexPair(bytes[l], stringbuffer);
}
return stringbuffer.toString();
}

private static String bufferToHex(byte bytes[]) {
return bufferToHex(bytes, 0, bytes.length);
}

/**
* 判断字符串的md5校验码是否与一个已知的md5码相匹配
*
* @param password 要校验的字符串
* @param md5PwdStr 已知的md5校验码
* @return
*/
public static boolean checkPassword(String password, String md5PwdStr) {
String s = getMD5String(password);
return s.equals(md5PwdStr);
}
/**
* 生成字符串的md5校验值
*
* @param s
* @return
*/
public static String getMD5String(String s) {
return getMD5String(s.getBytes());
}

/**
* 生成字节流的md5校验值
*
* @param s
* @return
*/
public static String getMD5String(byte[] bytes) {
messagedigest.update(bytes);
return bufferToHex(messagedigest.digest());
}

public static void main(String[] args) throws IOException {
long begin = System.currentTimeMillis();

File big = new File("F:/迅雷下载/港囧.TC1280清晰国语中字.mp4");
String md5 = getFileMD5(big);

long end = System.currentTimeMillis();
System.out.println("md5:" + md5);
System.out.println("time:" + ((end - begin) / 1000) + "s");

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: