生成文件md5校验码可满足大于2G情况
2015-12-18 17:17
330 查看
对于文件生成md5校验码,在实际开发中用处很大,有利于比较服务器上是否已经存在该文件,哪怕文件名不一致也无所谓。
由于存在超大文件,经过从网上查找,得到两种方法,一种是利用MappedByteBuffer对于超大文件缓冲,效率高
代码片段如下:
但是我个人觉得,在这里只是将文件流读了一下,这里使用
所以查询到如下代码:
该方法适用于所有文件读写,经测试了一个超过2G小于3G的大文件,生成校验码值本机在15s左右。网上有人对该方法深恶痛绝,说达到950多s,我想是因为他的机器性能太差的缘故吧。我对文件块读写改造后,同样的文件生成速度可以降低到9s,不过在小于2G的文件读写上,性能不如上面的代码,本机测试,大概差了2s-4s。所以我个人经过整合,出现如下代码小于2g, 利用第一个的性能,大于2G利用后者的代码如下,如果保守可以只用>2g的操作:
由于存在超大文件,经过从网上查找,得到两种方法,一种是利用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"); } }
相关文章推荐
- 短消息调试笔记
- Linux安装Cacti
- iOS开发----百度地图定位之KCLErrorDomain Code = 0的解决方法
- break & continue
- 移动端reset.css
- Android 获取view的位置和设置view的位置
- ios的安全机制
- 基于ThinkPHP实现批量删除
- 临时表空间优化
- 用PopupWindow做一个简单的弹出窗体
- Ajax工作原理
- vim 介绍
- overridePendingTransition
- 电动真空拔罐器
- hdu1548 最短路
- C#中常用的ToString()方法总结
- Jackson框架:Java与Json互转
- 微信多客服
- Android apk动态加载机制的研究(二)
- ios的安全机制