• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

使用md5来实现文件去重

武飞扬头像
菜菜小陈不怕困难
帮助1

可以使用MD5算法来实现文件去重,因为它可以接受任意大小的数据并输出固定长度的哈希值。所以两个不一样的文件一般情况下使用MD5计算出来的hash值是不可能会相等的。

所以一旦两个文件计算出来的hash值相同那么他们的文件就是相同的。

这时文件上传的一个例子,先使用md5算法计算文件的hash值,再检测我们磁盘是否有相同的文件名的文件,如果有那我们就不上传直接返回访问路径,如果没有才上传

  1.  
    @Override
  2.  
    public String uploadFile(MultipartFile file, String path) {
  3.  
    try {
  4.  
    // 获取文件md5值
  5.  
    String md5 = FileUtils.getMd5(file.getInputStream());
  6.  
    // 获取文件扩展名
  7.  
    String extName = FileUtils.getExtName(file.getOriginalFilename());
  8.  
    // 重新生成文件名
  9.  
    String fileName = md5 extName;
  10.  
    System.out.println("filename " fileName);
  11.  
    // 判断文件是否已存在
  12.  
    if (!exists(path fileName)) {
  13.  
    // 不存在则继续上传
  14.  
    upload(path, fileName, file.getInputStream());
  15.  
    }
  16.  
    // 返回文件访问路径
  17.  
    return getFileAccessUrl(path fileName);
  18.  
    } catch (Exception e) {
  19.  
    e.printStackTrace();
  20.  
    throw new BizException("文件上传失败");
  21.  
    }
  22.  
    }
学新通

我这就先看一下MD5得到Hash值的逻辑

  1.  
    public static String getMd5(InputStream inputStream) {
  2.  
    try {
  3.  
    MessageDigest md5 = MessageDigest.getInstance("md5");
  4.  
    byte[] buffer = new byte[8192];
  5.  
    int length;
  6.  
    while ((length = inputStream.read(buffer)) != -1) {
  7.  
    md5.update(buffer, 0, length);
  8.  
    }
  9.  
    return new String(Hex.encodeHex(md5.digest()));
  10.  
    } catch (Exception e) {
  11.  
    e.printStackTrace();
  12.  
    return null;
  13.  
    } finally {
  14.  
    try {
  15.  
    if (inputStream != null) {
  16.  
    inputStream.close();
  17.  
    }
  18.  
    } catch (IOException e) {
  19.  
    e.printStackTrace();
  20.  
    }
  21.  
    }
  22.  
    }
学新通

这里使用的是JDK自带的MD5算法,也可以替换成其他库提供的MD5算法。讲一下得到MD5算法摘要的大致逻辑,当然不看 得到MD5算法摘要的的逻辑也是可以的,没有影响

先传入我们要得到的算法名称

MessageDigest.getInstance("md5");

MessageDigest.getInstance又会调用这个方法

  1.  
    GetInstance.Instance instance = GetInstance.getInstance("MessageDigest",
  2.  
    MessageDigestSpi.class, algorithm);

这里是先得到所有的算法类提供商,再在提供商列表中查找第一个支持该算法的提供商提供的服务类,如果这个服务类能够提供指定的clazz的实例就直接将这个Instance返回,如果不能就找下一个能提供该算法的提供商看行不行。

我们这里的clazz就是MessageDigestSpi类,该类为MessageDigest类定义服务提供者接口(Service Provider Interface, SPI),该类提供消息摘要算法的功能,如MD5或SHA。消息摘要是安全的单向哈希函数,它接受任意大小的数据并输出固定长度的哈希值。

  1.  
    public static Instance getInstance(String type, Class<?> clazz,
  2.  
    String algorithm) throws NoSuchAlgorithmException {
  3.  
    // in the almost all cases, the first service will work
  4.  
    // avoid taking long path if so
  5.  
    ProviderList list = Providers.getProviderList();
  6.  
    Service firstService = list.getService(type, algorithm);
  7.  
    if (firstService == null) {
  8.  
    throw new NoSuchAlgorithmException
  9.  
    (algorithm " " type " not available");
  10.  
    }
  11.  
    NoSuchAlgorithmException failure;
  12.  
    try {
  13.  
    return getInstance(firstService, clazz);
  14.  
    } catch (NoSuchAlgorithmException e) {
  15.  
    failure = e;
  16.  
    }
  17.  
     
  18.  
    for (Service s : list.getServices(type, algorithm)) {
  19.  
    if (s == firstService) {
  20.  
    // do not retry initial failed service
  21.  
    continue;
  22.  
    }
  23.  
    try {
  24.  
    return getInstance(s, clazz);
  25.  
    } catch (NoSuchAlgorithmException e) {
  26.  
    failure = e;
  27.  
    }
  28.  
    }
  29.  
    throw failure;
  30.  
    }
学新通

最后返回我们期望的消息摘要算法MessageDigest。除了有MD5以外还有SHA1等等算法可以选择

MessageDigest的方法:

  • getInstance 得到算法摘要
  • update 处理这些数据
  • digest 转换并返回结果,也是字节数组

在校验文件重复性的时候,我们最后一步就是将MD5校验返回的字节数组编码成16进制的字符数组,然后用这个字符数组转换为字符串作为我们文件的名字,如果以后还有同样的文件被上传了,会对比是否有文件名相同的文件。

因为一个字节需要2个16进制数来表示,所以字符数组的大小是字节数组的大小的2倍

  1.  
    public static String encodeHex(byte[] byteArray) {
  2.  
     
  3.  
    // 首先初始化一个字符数组,用来存放每个16进制字符
  4.  
     
  5.  
    char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };
  6.  
     
  7.  
     
  8.  
     
  9.  
    // new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方))
  10.  
     
  11.  
    char[] resultCharArray =new char[byteArray.length * 2];
  12.  
     
  13.  
     
  14.  
     
  15.  
    // 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
  16.  
     
  17.  
    int index = 0;
  18.  
     
  19.  
    for (byte b : byteArray) {
  20.  
     
  21.  
    resultCharArray[index ] = hexDigits[b>>> 4 & 0xf];
  22.  
     
  23.  
    resultCharArray[index ] = hexDigits[b& 0xf];
  24.  
     
  25.  
    }
  26.  
     
  27.  
     
  28.  
     
  29.  
    // 字符数组组合成字符串返回
  30.  
     
  31.  
    return new String(resultCharArray);
  32.  
     
  33.  
    }
学新通

对于计算文件的MD5 的Hash值时我们可以像这样使用基础的InputStrem

  1.  
    MessageDigest md5 = MessageDigest.getInstance("md5");
  2.  
    byte[] buffer = new byte[8192];
  3.  
    int length;
  4.  
    while ((length = inputStream.read(buffer)) != -1) {
  5.  
    md5.update(buffer, 0, length);
  6.  
    }
  7.  
    byte[] resultByteArray = MD5.digest();

也可以使用DigestInputStream

  1.  
    MessageDigest messageDigest =MessageDigest.getInstance("MD5");
  2.  
    // 使用DigestInputStream
  3.  
     
  4.  
    DigestInputStream digestInputStream = new DigestInputStream(inputStream,messageDigest);
  5.  
     
  6.  
    // read的过程中进行MD5处理,直到读完文件
  7.  
     
  8.  
    byte[] buffer =new byte[bufferSize];
  9.  
     
  10.  
    while (digestInputStream.read(buffer) > 0);
  11.  
     
  12.  
    // 获取最终的MessageDigest
  13.  
    messageDigest= digestInputStream.getMessageDigest();
  14.  
     
  15.  
    byte[] resultByteArray = messageDigest.digest();
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfihhih
系列文章
更多 icon
同类精品
更多 icon
继续加载