慎用 MappedByteBuffer!

本文介绍了一种使用MD5进行大文件校验的方法,通过NIO提高效率,但遇到了文件删除的问题。文章探讨了解决方案,包括尝试释放内存映射缓冲区的句柄以及使用传统InputStream方式。

<script type="text/javascript"></script>

最近使用MD5进行大文件验证,固使用NIO这种高效率的模式来进行文件映射:

FileInputStream in = new FileInputStream(file);
FileChannel ch = in.getChannel();
MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
messageDigest.update(byteBuffer);
String md5 = bufferToHex(messageDigest.digest());
ch.close();
in.close();

本来想如果文件md5与数据库存储的值不同就删掉该文件的,结果出现了文件无法删除的情况。

抛出的违例
java.io.FileNotFoundException: E:\hello.jar (请求的操作无法在使用用户映射区域打开的文件上执行。)

后来经查,原来是当使用 FileChannel.map 方法时,MappedByteBuffer 已经在系统内占用了一个句柄,而使用 FileChannel.close 方法是无法释放这个句柄的,且FileChannel有没有提供类似 unmap 的方法,因此会出现无法删除文件的情况。

 

此问题一直让我很郁闷,后来到网上查询到两种方法...

 

第一种:

AccessController.doPrivileged(new PrivilegedAction() {
  public Object run() {
    try {
      Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
      getCleanerMethod.setAccessible(true);
      sun.misc.Cleaner cleaner = (sun.misc.Cleaner) 
      getCleanerMethod.invoke(byteBuffer, new Object[0]);
      cleaner.clean();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
});

此种方法需要JDK支持,我用的是JRE 1.6,提示没有 sun.misc.Cleaner 类,JDK包太大,项目又不让用。

 

第二种方法是显性设置byteBuffer为null,并调用GC,没什么实际意义。

 

实在没招了,又回来使用InputStream了...

FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream((int)file.length());
byte[] cache = new byte[1048576];
for(int i = in.read(cache);i != -1;i = in.read(cache)){
  out.write(cache, 0, i);
}
in.close();
out.close();
messageDigest.update(out.toByteArray());
md5 = bufferToHex(messageDigest.digest());

不知道SUN为啥要搞这种半截子工程,大家有什么其他解决方法么?

如果你想使用MappedByteBuffer来处理FileChannel数据并进行反向解析,你可以按照以下步骤进行操作: 1. 创建FileChannel和MappedByteBuffer: ```java RandomAccessFile file = new RandomAccessFile("path/to/file", "r"); FileChannel fileChannel = file.getChannel(); MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); ``` 2. 反向解析数据: ```java int blockSize = 1024; // 假设每个文件块的大小为1024字节 long totalSize = file.length(); // 获取文件总大小 long currentPosition = totalSize; // 从文件末尾开始解析 while (currentPosition > 0) { // 计算当前文件块的位置和大小 long blockSizeToRead = Math.min(blockSize, currentPosition); long offset = currentPosition - blockSizeToRead; // 映射当前文件块的数据到MappedByteBuffer MappedByteBuffer blockBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, blockSizeToRead); // 反向解析当前文件块的数据,并进行相应的处理 // 例如,输出当前文件块的内容 for (int i = blockBuffer.limit() - 1; i >= 0; i--) { byte data = blockBuffer.get(i); System.out.print((char) data); } currentPosition -= blockSizeToRead; } ``` 在上述代码示例中,假设每个文件块的大小为1024字节,我们从文件末尾开始反向解析数据。首先,我们获取文件的总大小,然后根据每个文件块的大小和当前位置,计算出要解析的文件块的位置和大小。然后,我们使用FileChannel的map方法将当前文件块的数据映射到MappedByteBuffer中。最后,我们对MappedByteBuffer进行反向解析,并根据需要进行相应的处理。 请根据你的需求和实际情况修改以上代码示例。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值