使用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层。