查找FileStore的目录

14
我正在寻找一种方法来检测U盘是否已插入电脑。目前,我找到的解决方案是轮询FileSystem#getFileStores以查看变化。这确实告诉我U盘何时被插入,但据我所知,没有办法检索其位置。FileStore#typeFileStore#name似乎都不太可靠,因为它们的返回值是实现特定的,但它们似乎是唯一可能返回任何相关信息的方法,可以帮助找到FileStore的目录。

基于此,请参考以下代码:

public class Test {
    public static void main(String[] args) throws IOException {
        for (FileStore store : FileSystems.getDefault().getFileStores()) {
            System.out.println(store);
            System.out.println("\t" + store.name());
            System.out.println("\t" + store.type());
            System.out.println();
        }
    }
}

给我输出了这个结果:
/ (/dev/sda5)
    /dev/sda5
    ext4

/* snip */

/media/TI103426W0D (/dev/sda2)
    /dev/sda2
    fuseblk

/media/flashdrive (/dev/sdb1)
    /dev/sdb1
    vfat

事实证明,FileStore#type返回驱动器的格式,FileStore#name返回驱动器设备文件的位置。就我所知,唯一具有驱动器位置的方法是toString方法,但从中提取路径名似乎很危险,因为我不确定该特定解决方案在其他操作系统和Java的未来版本中是否能够持续有效。
这里是否有什么我错过的东西或者纯粹使用Java不可能做到这一点?
系统信息:
$ java -version
java version "1.7.0_03"
OpenJDK Runtime Environment (IcedTea7 2.1.1pre) (7~u3-2.1.1~pre1-1ubuntu2)
OpenJDK Client VM (build 22.0-b10, mixed mode, sharing)

$ uname -a
Linux jeffrey-pc 3.2.0-24-generic-pae #37-Ubuntu SMP Wed Apr 25 10:47:59 UTC 2012 i686 athlon i386 GNU/Linux
4个回答

10

在更好的解决方案出现之前,以下是一个临时解决方法:

public Path getRootPath(FileStore fs) throws IOException {
    Path media = Paths.get("/media");
    if (media.isAbsolute() && Files.exists(media)) { // Linux
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(media)) {
            for (Path p : stream) {
                if (Files.getFileStore(p).equals(fs)) {
                    return p;
                }
            }
        }
    } else { // Windows
        IOException ex = null;
        for (Path p : FileSystems.getDefault().getRootDirectories()) {
            try {
                if (Files.getFileStore(p).equals(fs)) {
                    return p;
                }
            } catch (IOException e) {
                ex = e;
            }
        }
        if (ex != null) {
            throw ex;
        }
    }
    return null;
}
据我所知,这个解决方案只适用于Windows和Linux系统。
由于当您尝试检索其中一个CD驱动器的FileStore时,如果CD驱动器中没有CD,则会抛出异常。因此在Windows循环中必须捕获IOException。这可能发生在您遍历每个根目录之前。

这对于Windows真的有效吗?看起来它会忽略我在C:\ Data挂载的驱动器。我使用这个较新的API的整个目的是它承诺找到我所有的挂载点而不仅仅是根。 - Hakanai
还有,/media?你是不是指的是/mnt? - Hakanai
@Trejkaz,我不知道你可以这样做。它适用于驱动器被安装为字母的标准情况。至少在Ubuntu上,默认情况下会将东西安装到/媒体。您还可以在Linux系统上使用/ etc / mtab进行更灵活的解决方法(我不怎么使用Windows,因此我不知道其他方法)。 - Jeffrey
1
Linux领域的人似乎推荐运行mount并解析输出 - 因为这些工具的输出足够规则,以便于shell脚本解析处理,Java也没有太大问题。然而,这仍然不是一个非常令人满意的解决方法,我又回到了在Windows上没有任何解决方案的状态。此外,我认为你指的是常见情况,而不是标准情况。“标准”这个词很少适用于Windows。 :D - Hakanai
1
@Trejkaz 好的。我之前向 Oracle 提交了一个 RFE,希望他们在这十年内能够对此采取一些措施 ;) - Jeffrey
1
的确。与此同时,有一些技巧。 :D - Hakanai

4
这是我最终采取的做法。它仅适用于Windows + UNIX,但避免使用外部工具或附加库调用。它窃取了Java在FileStore对象中已有的信息。 LinuxFileStore明确扩展了UnixFileStore,因此它可以使用。同样的情况也适用于Solaris。由于Mac OS X是UNIX,它可能有效,但我不确定,因为我找不到任何地方显示它的子类。
public class FileStoreHacks {
    /**
     * Stores the known hacks.
     */
    private static final Map<Class<? extends FileStore>, Hacks> hacksMap;
    static {
        ImmutableMap.Builder<Class<? extends FileStore>, Hacks> builder =
            ImmutableMap.builder();

        try {
            Class<? extends FileStore> fileStoreClass =
                Class.forName("sun.nio.fs.WindowsFileStore")
                    .asSubclass(FileStore.class);
            builder.put(fileStoreClass, new WindowsFileStoreHacks(fileStoreClass));
        } catch (ClassNotFoundException e) {
            // Probably not running on Windows.
        }

        try {
            Class<? extends FileStore> fileStoreClass =
                Class.forName("sun.nio.fs.UnixFileStore")
                    .asSubclass(FileStore.class);
            builder.put(fileStoreClass, new UnixFileStoreHacks(fileStoreClass));
        } catch (ClassNotFoundException e) {
            // Probably not running on UNIX.
        }

        hacksMap = builder.build();
    }

    private FileStoreHacks() {
    }

    /**
     * Gets the path from a file store. For some reason, NIO2 only has a method
     * to go in the other direction.
     *
     * @param store the file store.
     * @return the path.
     */
    public static Path getPath(FileStore store) {
        Hacks hacks = hacksMap.get(store.getClass());
        if (hacks == null) {
            return null;
        } else {
            return hacks.getPath(store);
        }
    }

    private static interface Hacks {
        Path getPath(FileStore store);
    }

    private static class WindowsFileStoreHacks implements Hacks {
        private final Field field;

        public WindowsFileStoreHacks(Class<?> fileStoreClass) {
            try {
                field = fileStoreClass.getDeclaredField("root");
                field.setAccessible(true);
            } catch (NoSuchFieldException e) {
                throw new IllegalStateException("file field not found", e);
            }
        }

        @Override
        public Path getPath(FileStore store) {
            try {
                String root = (String) field.get(store);
                return FileSystems.getDefault().getPath(root);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Denied access", e);
            }
        }
    }

    private static class UnixFileStoreHacks implements Hacks {
        private final Field field;

        private UnixFileStoreHacks(Class<?> fileStoreClass) {
            try {
                field = fileStoreClass.getDeclaredField("file");
                field.setAccessible(true);
            } catch (NoSuchFieldException e) {
                throw new IllegalStateException("file field not found", e);
            }
        }

        @Override
        public Path getPath(FileStore store) {
            try {
                return (Path) field.get(store);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Denied access", e);
            }
        }
    }
}

2
提示他人:由于私有字段可能随时更改,因此此方法仅适用于当前版本的Java,直到另行确认。 - Jeffrey
1
是的。如果您使用它,请一定添加单元测试来检测行为变化。 - Hakanai
在CentOS(6)上无法工作。FileStore是LinuxFileStore,但在hacksMap中被添加为UnixFileStore。而且Hacks hacks = hacksMap.get(store.getClass());什么也没有找到。更糟糕的是,getDeclaredField(“file”)-返回原始文件,而不是隐藏在UnixMountEntry条目内部的容器字节数组dir中的挂载点。 - Сергей Никитин
是的,你需要为LinuxFileStore实现一个新的hack,但在你实现之后它会起作用。 - Hakanai

0
这是针对Windows的:
    public Path getFileStoreRootPath(FileStore fs) throws Exception {
    for (Path root : FileSystems.getDefault().getRootDirectories()) {
        if (Files.isDirectory(root) && Files.getFileStore(root).equals(fs)) {
            return root;
        }
    }

    throw new RuntimeException("Root directory for filestore " + fs + " not found");
}

基本上,通过筛选条件 Files.isDirectory(root),我们排除了所有的 CD / DVD 驱动器,这将在未插入光盘时抛出 IOException。

0

我并没有深入研究过Java的这个领域,但我找到了this,它似乎与此相关。它使用了File.listRoots()

那里还有一些相关的问题链接。


2
File.listRoots 只适用于 Windows,我已经查看了那些问题,但没有找到答案。它们都是在 nio2 出现之前提出的。 - Jeffrey
啊> _>我会继续寻找,如果我找到任何东西,我会编辑这个答案。 - Perry Monschau
@Jeffrey,即使在Windows上也无法工作,因为在Windows上,您可以拥有未挂载到驱动器字母的驱动器。 - Hakanai
最好使用FileSystems.getDefault().getRootDirectories()而不是File.listRoots()。 - Adrian Romanelli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接