使用Java NIO直接访问Windows磁盘

我正在使用一个使用Java NIO的库,以便直接将文件映射到内存,但是我无法直接读取磁盘。

可以使用带有UNC的FileInputStream直接读取磁盘,例如

 File disk = new File("\\\\.\\PhysicalDrive0\\"); try (FileInputStream fis = new FileInputStream(disk); BufferedInputStream bis = new BufferedInputStream(fis)) { byte[] somebytes = new byte[10]; bis.read(somebytes); } catch (Exception ex) { System.out.println("Oh bother"); } 

但是,我无法将其扩展到NIO:

 File disk = new File("\\\\.\\PhysicalDrive0\\"); Path path = disk.toPath(); try (FileChannel fc = FileChannel.open(path, StandardOpenOption.READ)){ System.out.println("No exceptions! Yay!"); } catch (Exception ex) { System.out.println("Oh bother"); } 

堆栈跟踪(由原因决定)是:

 java.nio.file.FileSystemException: \\.\PhysicalDrive0\: The parameter is incorrect. at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) at sun.nio.fs.WindowsFileSystemProvider.newFileChannel(WindowsFileSystemProvider.java:115) at java.nio.channels.FileChannel.open(FileChannel.java:287) at java.nio.channels.FileChannel.open(FileChannel.java:334) at hdreader.HDReader.testcode(HDReader.java:147) 

我找不到解决方案,但我看到了如何从java访问磁盘上的特定原始数据 。 Daniel Alder的回答建议使用GLOBALROOT似乎是相关的,因为答案在答案中使用了FileChannel,但我似乎无法使用这种模式找到驱动器。 有没有办法列出GLOBALROOT下的所有设备或类似的东西?

目前我正在考虑用直接的InputStream替换NIO的使用,但是如果可以的话我想避免这种情况。 首先,使用NIO是有原因的,其次,它运行了大量代码,需要大量工作。 最后,我想知道如何实现Daniel的解决方案,以便我可以在未来写入设备或使用NIO。

总结一下:如何直接使用Java NIO(而不是InputStream )访问驱动器,和/或有没有办法列出可通过GLOBALROOT访问的所有设备,以便我可以使用Daniel Alser的解决方案?

答案摘要:我保留了过去的编辑(下面)以避免混淆。 在EJP和Apangin的帮助下,我认为我有一个可行的解决方案。 就像是

 private void rafMethod(long posn) { ByteBuffer buffer = ByteBuffer.allocate(512); buffer.rewind(); try (RandomAccessFile raf = new RandomAccessFile(disk.getPath(), "r"); SeekableByteChannel sbc = raf.getChannel()) { sbc.read(buffer); } catch (Exception ex) { System.out.println("Oh bother: " + ex); ex.printStackTrace(); } return buffer; } 

只要posn参数是扇区大小的倍数(在这种情况下设置为512),这将起作用。 请注意,这也适用于Channels.newChannel(FileInputStream),在这种情况下似乎总是返回一个SeekableByteStream, 看起来可以安全地将其转换为一个。

从快速和肮脏的测试看来 ,这些方法似乎真正寻求并且不仅仅是跳过。 我在驱动器开始时搜索了一千个位置,然后读取它们。 我做了同样的事情,但增加了一半磁盘大小的偏移量(搜索磁盘背面)。 我发现:

  • 两种方法几乎都用了相同的时间。
  • 搜索磁盘的开头或结尾不会影响时间。
  • 缩小地址范围确实减少了时间。
  • 对地址进行排序确实减少了时间,但不是很多。

这告诉我,这是真正的追求,而不仅仅是阅读和跳过(如流程所趋)。 在这个阶段速度仍然很糟糕,它使我的硬盘听起来像一台洗衣机,但代码是为快速测试而设计的,并且尚未变得漂亮。 它仍然可以正常工作。

感谢EJP和Apangin的帮助。 阅读他们各自答案的更多内容。

编辑:我已经在Windows 7机器上运行我的代码(我最初没有),我得到一个稍微不同的exception(见下文)。 这是使用管理员权限运行的,第一段代码仍然在相同条件下运行。

 java.nio.file.FileSystemException: \\.\PhysicalDrive0\: A device attached to the system is not functioning. at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) at sun.nio.fs.WindowsFileSystemProvider.newFileChannel(WindowsFileSystemProvider.java:115) at java.nio.channels.FileChannel.open(FileChannel.java:287) at java.nio.channels.FileChannel.open(FileChannel.java:335) at testapp.TestApp.doStuff(TestApp.java:30) at testapp.TestApp.main(TestApp.java:24) 

编辑2:为了回应EJP,我尝试过:

  byte[] bytes = new byte[20]; ByteBuffer bb = ByteBuffer.wrap(bytes); bb.rewind(); File disk = new File("\\\\.\\PhysicalDrive0\\"); try (FileInputStream fis = new FileInputStream(disk); ReadableByteChannel rbc = Channels.newChannel(new FileInputStream(disk))) { System.out.println("Channel created"); int read = rbc.read(bb); System.out.println("Read " + read + " bytes"); System.out.println("No exceptions! Yay!"); } catch (Exception ex) { System.out.println("Oh bother: " + ex); } 

当我尝试这个时,我得到以下输出:

频道已创建
哦麻烦:java.io.IOException:参数不正确

所以我似乎可以创建一个FileChannel或ReadableByteChannel,但我无法使用它; 也就是说,错误只是推迟了。

在没有缓冲的情况下访问物理驱动器时,您只能读取完整的扇区。 这意味着,如果扇区大小为512字节,则只能读取512个字节的倍数。 将缓冲区长度更改为512或4096(无论您的扇区大小是多少), FileChannel将正常工作:

 ByteBuffer buf = ByteBuffer.allocate(512); try (RandomAccessFile raf = new RandomAccessFile("\\\\.\\PhysicalDrive0", "r"); FileChannel fc = raf.getChannel()) { fc.read(buf); System.out.println("It worked! Read bytes: " + buf.position()); } catch (Exception e) { e.printStackTrace(); } 

请参阅对齐和文件访问要求 。

你的原始FileInputStream代码显然是因为BufferedInputStream的默认缓冲区大小为8192.把它带走 – 代码将失败并出现相同的exception。

使用NIO,您的原始代码只需要稍微改变一下。

 Path disk = Paths.get("d:\\."); try (ByteChannel bc = Files.newByteChannel(disk, StandardOpenOption.READ)) { ByteBuffer buffer = ByteBuffer.allocate(10); bc.read(buffer); } catch (Exception e){ e.printStackTrace(); } 

是好的,可行的代码,但我在你的版本和我的版本中都获得了访问被拒绝错误。

以管理员身份运行。 它确实有效,因为它只是java.io:一个瘦包装器java.io:

  try (FileInputStream fis = new FileInputStream(disk); ReadableByteChannel fc = Channels.newChannel(fis)) { System.out.println("No exceptions! Yay!"); ByteBuffer bb = ByteBuffer.allocate(4096); int count = fc.read(bb); System.out.println("read count="+count); } catch (Exception ex) { System.out.println("Oh bother: "+ex); ex.printStackTrace(); } 

编辑如果您需要随机访问,那么您将无法使用RandomAccessFile 。 通过Channels没有映射。 但是上面的解决方案不是NIO,只是FileInput/OutputStream的Java NIO层。