使用Java NIO直接访问Windows磁盘
我正在使用一个使用JavaNIO的库来直接将文件映射到内存,但是直接读取磁盘时遇到了问题 我可以通过UNC使用使用Java NIO直接访问Windows磁盘,java,nio,disk,drive,unc,Java,Nio,Disk,Drive,Unc,我正在使用一个使用JavaNIO的库来直接将文件映射到内存,但是直接读取磁盘时遇到了问题 我可以通过UNC使用FileInputStream直接读取磁盘,例如 File disk = new File("\\\\.\\PhysicalDrive0\\"); try (FileInputStream fis = new FileInputStream(disk); BufferedInputStream bis = new BufferedInputStream(fis)) { b
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");
}
stacktrace(直到原因)是:
我还没有找到一个解决办法,虽然我看到了一些接近。Daniel Alder建议使用GLOBALROOT的答案似乎是相关的,因为答案中使用了FileChannel,但我似乎找不到使用这种模式的驱动器。有没有办法将所有设备列在GLOBALROOT或类似的目录下
目前,我正在考虑用直接InputStream
s替换NIO的使用,但如果可以的话,我想避免这种情况。首先,使用NIO是有原因的,其次,它需要运行大量的代码,需要做大量的工作。最后,我想知道如何实现类似Daniel的解决方案的东西,以便将来可以编写设备或使用NIO
总之:我如何使用Java NIO(而不是InputStream
s)直接访问驱动器,和/或是否有办法列出通过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),在这种情况下,它似乎总是返回可查找的字节流,并且将其强制转换为一个字节流似乎是安全的
从快速和肮脏的测试看来,这些方法确实在寻找,而不仅仅是跳过。我在我的驱动器开始时搜索了一千个位置,它读取了它们。我也这么做了,但添加了磁盘大小一半的偏移量(用于搜索磁盘的背面)。我发现:
- 两种方法花费的时间几乎相同
- 搜索磁盘的开始或结束不影响时间
- 缩小地址范围确实缩短了时间
- 对地址进行排序确实减少了时间,但没有减少多少
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,但我不能使用它;也就是说,错误只是延迟了。以管理员身份运行此操作。它确实有效,因为它只是
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();
}
编辑如果您需要随机访问,则必须使用
随机访问文件
。没有通过频道
从中映射。但是上面的解决方案无论如何都不是NIO,只是FileInput/OutputStream
上的Java NIO层,使用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();
}
是很好的、可行的代码,但我在您的版本和我的版本中都遇到了拒绝访问错误。当访问物理驱动器而不进行缓冲时,您只能读取完整的扇区。这意味着,如果扇区大小为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。把它拿走——代码也会失败,出现同样的异常。谢谢EJP,已经这么做了。当我第一次问我的问题时,我正在使用XP(没有Windows7)。当我在Windows7中运行它时,我得到的正是你提到的错误。然后我以管理员的身份运行,出现了一个新错误:“连接到系统的设备无法正常工作。”(请参阅我文章的底部)。当文件流工作时,设备显然正在运行,它也是我的操作系统磁盘。顺便说一句,同样的事情发生在磁盘1、2和3上。好的,我将尝试剪切和粘贴。啊。的确,您可以通过这种方式获得FileChannel,但它什么也做不了。它只是延迟异常,直到您尝试读取它为止。我添加了一个ByteBuffer并尝试读取它,得到了:java.io.IOException:该参数不正确有趣的是,这不同于“连接的设备…”@EJP“没有通过通道从该设备进行映射”-关于RandomAccessFile.getChannel()
?@apangin,这不是通过通道进行映射。请看我写的东西getChannel()
将遇到与OP已经抱怨的问题完全相同的问题。感谢您的回答。我认为问题不在于引用(\\.\PhysicalDrive0),尽管我会尝试任何方法。我和你的代码有同样的错误。使用FileInputStream的第一段代码确实可以使用该引用,但不幸的是,FileInputStream不能满足我的需要。如果这是参考资料,我想这是行不通的。另外,当我使用无效引用时,我会遇到另一个错误。您没有尝试我的实际代码。尝试使用4096的缓冲区大小,就像我做的那样。你可能需要阅读前文本
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();
}