【发布时间】:2014-04-05 09:34:00
【问题描述】:
我正在使用一个使用 Java NIO 的库来直接将文件映射到内存,但是我无法直接读取磁盘。
我可以直接使用FileInputStream和UNC读取磁盘,比如
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)
虽然我在How to access specific raw data on disk from java 上看到了一些接近的东西,但我一直无法找到解决方案。 Daniel Alder 建议使用 GLOBALROOT 的答案似乎是相关的,因为答案在答案中使用了 FileChannel,但我似乎无法找到使用这种模式的驱动器。有没有办法列出 GLOBALROOT 下的所有设备或类似的东西?
目前我正在考虑用直接的InputStreams 替换 NIO 的使用,但如果可以的话,我想避免这种情况。首先,使用 NIO 是有原因的,其次,它要运行大量代码,需要大量工作。最后,我想知道如何实现类似 Daniel 的解决方案,以便将来可以写入设备或使用 NIO。
总而言之:如何使用 Java NIO(而不是 InputStreams)直接访问驱动器,和/或有没有办法列出通过 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 机器上运行我的代码(我最初没有),我得到了一个稍微不同的异常(见下文)。这是以管理员权限运行的,第一段代码在相同的条件下仍然有效。
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,但我不能使用它;也就是说,错误只是被延迟了。
【问题讨论】:
-
你没有尝试我的实际代码。像我一样尝试使用 4096 的缓冲区大小。您可能必须读取确切的磁盘块倍数。
-
我现在不在电脑附近,所以我会在星期一尝试。我正在使用 NO,以便我可以快速随机访问磁盘。也许我应该在问题中这么说。所以我需要能够寻找、抓取零件(一个块应该可以工作)并且最好使用 transfer from 和 transfer to。
-
我准备打赌原始磁盘驱动程序不支持部分块读取。如果这是您的要求,您最好使用
FileInput/OutputStreams和BufferedInput/OutputStreams。 -
不幸的是,FileInput 流无法完成这项工作。从头到尾阅读都可以,但是随机访问存在问题。它要么不允许,要么需要永远。如果招待任何其他跨平台 Java 解决方案。我和蔚来没有关系。
-
确实如此,但是(尽管我承认我可能弄错了)我认为所有 FileChannel 对象都支持映射以及 transferTo 和 transferFrom。我使用的库使用 NIO 来提高通过映射处理大文件的效率。我对 NIO 的经验很少,因为在我有一个大文件(整个磁盘或磁盘映像)之前,FileInputStream 的效率差不多(它在 java 6 或 7 上效率更高;只是重新编译加快了我的代码)。但是,寻找比跳过快很多。