【踩坑】java实现文件分块后合并文件使得源文件与合并文件hash值不一致问题

7 篇文章 0 订阅
3 篇文章 0 订阅

最近在做一个类BT的私有资源下载网络系统,大家都知道BT下载的思路是把源文件分割为多个块实现多点下载、多点缓存,最后把从多个下载节点中的得到的块合并为一个文件。


涉及到多点下载势必涉及到文件分割,我的思路是使用输入输出流的方式。首先打开文件,获取文件输入流,然后获取文件的大小,最后读取文件输入流并按固定长度进行分割为多份单独存储。

Tracer服务器负责记录在线的节点,这些节点为下载终端提供自己缓存的分块文件,文件下载并不占用Tracer服务器的带宽,而是平摊给了各个下载节点。这些下载节点在担任文件缓存服务器的同时自己同时也可能是一个下载终端,从其他节点服务器下载文件。在传统的BT或者PT下载网络中经常会面临死种的问题,也就是说这个文件在这个网络中无法通过分割块凑齐一份完整的源文件且网络中也没有完整的源文件提供下载。虽然PT网络下载系统一定程度上改善了这个问题,但是依旧有些问题无法避免。

正题 

按照常理,如果一个被分割成多块后再合并,只要没有改变其内容,那么合并前的文件和合并后的文件的Hash值应该是一样的。但是我最近在Windows的NTFS文件系统中就出现了一个合并后Hash不一致的问题。

首先问题出现的原因,我们读取文件的时候为了减小内存占用一般都是分块读取的。分块读取肯定要先设定一个缓冲区Buffer,然后再把这个Buffer中的数据写入到输出流中。这个时候问题就出现了,我们的缓冲区的大小是一致,然而我们的文件大小却不一定能够被缓冲区大小整除

例如,我们的源文件大小是1025KB,而我们缓冲区是2KB1025KB mod 2KB = 1KB,然后这个剩余的1KB依旧会被写入2KB的缓冲区中。而这个缓冲区如果我们不做任何处理那么最后我们的输出流一共得到了1026KB的数据。源文件1025KB,输出流生成的文件是1026KB,这两个文件虽然都能够正常打开,但是两个文件的HASH值肯定是不一样了。

而解决问题的方法很简单,那就是别直接使用write(Buffer)这种方式写入输出流,因为我们在读取输入流的时候read(Buffer)方法会返回一个整型数据P,P即当前读取到的数据长度,建议使用write(Buffer,0,p)这种方式写入输出流。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
文件hash 可以通过 Java 的 MessageDigest 类来计算,具体实现步骤如下: 1. 读取文件内容,可以使用 FileInputStream 类来读取文件; 2. 使用 MessageDigest 类来计算文件内容的 hash ; 3. 将 hash 转换为十六进制字符串,可以使用 BigInteger 类来实现; 4. 将每个文件hash 存储到一个 Set 集合中,用于判断文件是否重复。 以下是一个示例代码: ```java import java.io.File; import java.io.FileInputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.util.HashSet; import java.util.Set; public class FileHashDeduplication { public static void main(String[] args) { String directoryPath = "C:/path/to/directory"; Set<String> hashSet = new HashSet<>(); File directory = new File(directoryPath); if (directory.isDirectory()) { File[] files = directory.listFiles(); if (files != null) { for (File file : files) { try { String hash = getFileHash(file); if (!hashSet.contains(hash)) { hashSet.add(hash); // do something with the file System.out.println(file.getPath()); } } catch (Exception e) { e.printStackTrace(); } } } } } private static String getFileHash(File file) throws Exception { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); FileInputStream fileInputStream = new FileInputStream(file); byte[] buffer = new byte[1024]; int len; while ((len = fileInputStream.read(buffer)) != -1) { messageDigest.update(buffer, 0, len); } fileInputStream.close(); BigInteger bigInteger = new BigInteger(1, messageDigest.digest()); return bigInteger.toString(16); } } ``` 在上面的代码中,getFileHash() 方法用于计算文件hash ,使用 MD5 算法来计算,计算出的结果以十六进制字符串的形式返回。getFileHash() 方法中的代码可以自行修改,例如可以使用 SHA-256 算法来计算文件hash 。使用 Set 集合来判断文件是否重复,如果集合中已经包含了该文件hash ,则说明该文件已经存在,否则将该文件hash 添加到集合中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值