使用Java NIO直接访问Windows磁盘

时间:2014-03-04 02:56:37

标签: java nio disk drive unc

我正在使用一个使用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");
}

stacktrace(由原因决定)是:

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下的所有设备或类似的东西?

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

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

答案摘要: 我保留了过去的编辑(下面)以避免混淆。在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,但我无法使用它;也就是说,错误只是推迟了。

3 个答案:

答案 0 :(得分:3)

在没有缓冲的情况下访问物理驱动器时,您只能读取完整的扇区。这意味着,如果扇区大小为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();
}

请参阅Alignment and File Access Requirements

您的原始FileInputStream代码显然可以正常工作,因为BufferedInputStream的默认缓冲区大小为8192.将其删除 - 代码将失败并出现相同的异常。

答案 1 :(得分:1)

以管理员身份运行此操作。它确实有效,因为它只是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层。

答案 2 :(得分:1)

使用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();
}

是好的,可行的代码,但我的版本和我的版本都出现了拒绝访问错误。

相关问题