问题 找到FileStore的目录


我正试图找到一种方法来检测闪存驱动器何时插入我的计算机。到目前为止,我找到的解决方案是进行民意调查 FileSystem#getFileStores 为了改变。这确实告诉我何时插入了闪存驱动器,但据我所知,没有办法检索它的位置。 FileStore#type 和 FileStore#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

4548
2018-05-21 00:25


起源



答案:


这是一个临时工作,直到找到更好的解决方案:

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系统。

你必须赶上 IOException 在Windows循环中,因为如果CD驱动器中没有CD,则在尝试检索时会抛出异常 FileStore 为了它。在迭代每个根之前可能会发生这种情况。


10
2018-04-09 05:15



这真的适用于Windows吗?好像它会错过我在C:\ Data上挂载的驱动器。我正在使用这个更新的API的重点是它承诺找到我所有的挂载点,而不仅仅是根。 - Trejkaz
还有/媒体?你的意思是/ mnt? - Trejkaz
@Trejkaz我不知道你能做到这一点。它适用于将驱动器安装为字母的标准情况。至少在Ubuntu上,默认情况下将东西挂载到/ media。您还可以使用/ etc / mtab在Linux系统上更灵活地解决这个问题(我真的不太使用Windows,所以我不知道另一个)。 - Jeffrey
Linux领域的人似乎建议运行mount并解析输出 - 因为这些工具的输出足够常规,以便shell脚本解析来处理,Java没有太大问题。然而,仍然不是一个非常令人满意的解决方法,而且我又回到了根本没有针对Windows的解决方案。我认为你的意思是常见的情况,而不是标准情况。 “标准”一词很少适用于Windows。 :d - Trejkaz
确实。与此同时,还有黑客攻击。 :d - Trejkaz


答案:


这是一个临时工作,直到找到更好的解决方案:

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系统。

你必须赶上 IOException 在Windows循环中,因为如果CD驱动器中没有CD,则在尝试检索时会抛出异常 FileStore 为了它。在迭代每个根之前可能会发生这种情况。


10
2018-04-09 05:15



这真的适用于Windows吗?好像它会错过我在C:\ Data上挂载的驱动器。我正在使用这个更新的API的重点是它承诺找到我所有的挂载点,而不仅仅是根。 - Trejkaz
还有/媒体?你的意思是/ mnt? - Trejkaz
@Trejkaz我不知道你能做到这一点。它适用于将驱动器安装为字母的标准情况。至少在Ubuntu上,默认情况下将东西挂载到/ media。您还可以使用/ etc / mtab在Linux系统上更灵活地解决这个问题(我真的不太使用Windows,所以我不知道另一个)。 - Jeffrey
Linux领域的人似乎建议运行mount并解析输出 - 因为这些工具的输出足够常规,以便shell脚本解析来处理,Java没有太大问题。然而,仍然不是一个非常令人满意的解决方法,而且我又回到了根本没有针对Windows的解决方案。我认为你的意思是常见的情况,而不是标准情况。 “标准”一词很少适用于Windows。 :d - Trejkaz
确实。与此同时,还有黑客攻击。 :d - Trejkaz


这就是我最终做的事情。这仅限于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);
            }
        }
    }
}

4
2018-05-21 00:48



其他人注意:由于私有字段如有更改,恕不另行通知,这只适用于当前版本的Java,除非另有确认。 - Jeffrey
对。如果使用此功能,请务必添加单元测试以检测行为更改。 - Trejkaz
没有在CentOS(6)上工作。 FileStore是LinuxFileStore,但是在hacksMap中它被添加为UnixFileStore。而Hacks hacks = hacksMap.get(store.getClass());一无所获。更糟糕的是,getDeclaredField(“file”) - 返回原始文件,而不是挂载点,隐藏在UnixMountEntry条目中的容器byte []目录中 - Сергей Никитин
是啊。你必须为LinuxFileStore实现一个新的hack,但是在你实现它之后它会工作。 - Trejkaz


我没有真正探索过java的这个领域,但我发现了 这个,这似乎是相关的。它用 File.listRoots()

似乎也有一些相关问题也存在。


0



File.listRoots 只适用于Windows,我已经审查过这些问题无济于事。在nio2出来之前,他们都被问到了 - Jeffrey
啊> _>如果我找到任何东西,我会继续寻找和编辑这个答案 - Perry Monschau
@Jeffrey它甚至不适用于Windows,因为在Windows上,您可以将驱动器安装在驱动器号上。 - Trejkaz