• md5 大文件


    测试 mail.rar , 一个1.59GB 的大文件,结果如下:
    // 分别测试3次,下面是测试结果; 我的电脑是 intel i5 4核, 8G 内存; 不运行此程序且 不做任何操作 的时候, 观察发现: cpu 10%, mem 70%
    // md5ByJavaIO , 观察发现,大概的平均是: cpu 35%, mem 71%
    // md5:8ba6b147475af653bc4609d4cdf72148 time:74s 74s 74s; 测试N多次都是74s, 非常稳定

    // md5ByFileChannel , 观察发现,大概的平均是: cpu 31%, mem 71%
    // md5:8ba6b147475af653bc4609d4cdf72148 time:75s 72s 73s

    // md5ByFileChannelAndMappedByteBuffer , 观察发现,大概的平均是: cpu 30%, mem 71%
    // md5:8ba6b147475af653bc4609d4cdf72148 time:52s 53s 54s

    // fastMd5 , 观察发现,大概的平均是: cpu 31%, mem 80%
    // md5:8ba6b147475af653bc4609d4cdf72148 time:51s 52s 51s

    // 实际计算时间的时候, 需要减去休眠的100 * 10, 即1s,

    代码如下:

    
    
    import com.twmacinta.util.MD5;
    import com.twmacinta.util.MD5OutputStream;

    import java.io.*;

    import java.nio.ByteBuffer;
    import java.nio.MappedByteBuffer;

    import java.nio.channels.FileChannel;

    import java.security.MessageDigest;

    import java.security.NoSuchAlgorithmException;

    public class MD5Util {
    /**
    * 默认的密码字符串组合,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 nsaex) {
    System.err.println(MD5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");

    nsaex.printStackTrace();

    }

    }

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

    File big = new File("D:\office2013破解版.ISO"); // 810M
    // big = new File("C:\Users\xd\Desktop\工作重要资料\xxx.zip"); // 2.25G; 但不能超过2G, 否则 at java.base/sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:994)
    big = new File("D:\lk\mail.rar");// 1.59G

    String md5 = null;

    // 获取文件的 md5; 如果是仅仅是获取字符串的md5, 当然不需要这么复杂。。
    for (int i = 0; i < 10; i++) {
    // md5 = md5ByJavaIO(big); // java io 的方式
    // md5 = md5ByFileChannel(big); // java nio 的方式
    // md5 = md5ByFileChannelAndMappedByteBuffer(big); // fast md5
    md5 = fastMd5(big);
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

    long end = System.currentTimeMillis();

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


    // 分别测试3次,下面是测试结果; 我的电脑是 intel i5 4核, 8G 内存; 不运行此程序且 不做任何操作 的时候, 观察发现: cpu 10%, mem 70%
    // md5ByJavaIO , 观察发现,大概的平均是: cpu 35%, mem 71%
    // md5:8ba6b147475af653bc4609d4cdf72148 time:74s 74s 74s; 测试N多次都是74s, 非常稳定

    // md5ByFileChannel , 观察发现,大概的平均是: cpu 31%, mem 71%
    // md5:8ba6b147475af653bc4609d4cdf72148 time:75s 72s 73s

    // md5ByFileChannelAndMappedByteBuffer , 观察发现,大概的平均是: cpu 30%, mem内存几乎毫无增长; ssd是 读300M/S; 写入220K-2M
    // md5:8ba6b147475af653bc4609d4cdf72148 time:52s 53s 54s

    // fastMd5 , 观察发现,大概的平均是: cpu 开始有个高峰,后面趋于平稳,后面是30% ; 大概是80%, mem xxx %
    // md5:8ba6b147475af653bc4609d4cdf72148 time:51s 52s 51s

    // 实际计算时间的时候, 需要减去休眠的100 * 10, 即1s,

    }

    /**
    * 适用于上G大的文件
    *
    * @param file
    * @return
    * @throws IOException
    */

    public static String md5ByFileChannelAndMappedByteBuffer(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);

    in.close();

    return bufferToHex(messagedigest.digest());

    }

    private static String md5ByFileChannel(File big) {
    if (big == null) {
    return null;
    }
    MessageDigest md;
    FileInputStream inputStream = null;
    try {
    int read = 0;
    inputStream = new FileInputStream(big);
    md = MessageDigest.getInstance("MD5");
    FileChannel channel = inputStream.getChannel();
    ByteBuffer buff = ByteBuffer.allocate(2048);
    while (channel.read(buff) != -1) {
    buff.flip();
    md.update(buff);
    buff.clear();
    }
    return bufferToHex(md.digest());
    } catch (NoSuchAlgorithmException e) {
    return null;
    } catch (IOException e) {
    return null;
    } finally {
    try {
    if (inputStream != null) inputStream.close();
    } catch (IOException e) {
    }
    }
    }

    // private String generateMD5(SequenceInputStream inputStream){
    private static String md5ByJavaIO(File big) {
    if (big == null) {
    return null;
    }
    MessageDigest md;
    InputStream inputStream = null;
    try {
    int read = 0;
    inputStream = new FileInputStream(big);
    byte[] buf = new byte[2048];
    md = MessageDigest.getInstance("MD5");
    while ((read = inputStream.read(buf)) > 0) {
    md.update(buf, 0, read);
    }
    return bufferToHex(md.digest());
    } catch (NoSuchAlgorithmException e) {
    return null;
    } catch (IOException e) {
    return null;
    } finally {
    try {
    if (inputStream != null) inputStream.close();
    } catch (IOException e) {
    // ...
    }
    }
    }


    private static String fastMd5(File big) {
    String md5Str = null;
    try {
    MD5OutputStream out = new MD5OutputStream(new com.twmacinta.io.NullOutputStream());
    InputStream in = new BufferedInputStream(new FileInputStream(big));
    byte[] buf = new byte[65536];// 65536 是最佳数值, 大于或小于这个数字都会 导致耗时增加, 不知道为什么..
    int num_read;
    long total_read = 0;
    while ((num_read = in.read(buf)) != -1) {
    total_read += num_read;
    out.write(buf, 0, num_read);
    }
    md5Str = MD5.asHex(out.hash());
    //System.out.println(md5Str + " " + big);
    in.close();
    out.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    return md5Str;
    }

    public static String getMD5String(String s) {
    return getMD5String(s.getBytes());
    }

    public static String getMD5String(byte[] bytes) {
    messagedigest.update(bytes);
    return bufferToHex(messagedigest.digest());
    }

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

    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 void appendHexPair(byte bt, StringBuffer stringbuffer) {
    char c0 = hexDigits[(bt & 0xf0) >> 4];
    char c1 = hexDigits[bt & 0xf];
    stringbuffer.append(c0);
    stringbuffer.append(c1);
    }

    public static boolean checkPassword(String password, String md5PwdStr) {
    String s = getMD5String(password);
    return s.equals(md5PwdStr);
    }

    }
     

    可见,大文件的时候, 最好还是不要java IO到内存, 可能映射过程就需要比较久时间。NIO MPP是很好的选择。

    另外, 我们是否可以继续提高 大文件md5 的计算速度呢? 据说 多线程分块校验。。

    我感觉 行得通,MD5算法本身是分块的,其他很多类似的算法比如SHA-1也是的,所以可以支持流式计算,读一块算一块,最后再一次性生成完整hash,完全没有内存爆炸的可能。  除非 算法本身不支持这样做。

    我们之前也使用过这样的做法, 就是取每100m的前1m作md5.然后整体再md5一次。但是我感觉不太安全, 也就是说不准确。虽然呢时间少花了很多,但恐怕是容易发生碰撞

    最后,我这边使用了一个 fastMD5的java 库, 发现确实可以稍微提升一点速度。 fastMD5 主要作用,大概是 不要NIO, 直接普通FIleInputStream 即可, 但是呢, 使用FIleInputStream 文件最大是2G 。

     

    确实, 现在前端也可以很快完成md5 计算!

    补充前端计算的方法,webuploader 支持分片计算md5,设置的分片不要太大就好了。


    版权声明
    本文原创发表于 博客园,作者为 阿K .     本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
    欢迎关注本人微信公众号:觉醒的码农,或者扫码进群:

  • 相关阅读:
    webpack
    npm
    关于js click事件、touch事件的 screen 、client
    同源策略、jsonp、阻塞事件
    关于height、width、top
    新建空白图片
    配置环境
    异常02
    异常01
    集合框架08
  • 原文地址:https://www.cnblogs.com/FlyAway2013/p/15417768.html
Copyright © 2020-2023  润新知