查找 FileStore 的目录

Posted

技术标签:

【中文标题】查找 FileStore 的目录【英文标题】:Find the directory for a FileStore 【发布时间】:2012-05-27 13:07:39 【问题描述】:

我正在尝试寻找一种方法来检测闪存驱动器何时插入我的计算机。到目前为止,我找到的解决方案是轮询 FileSystem#getFileStores 以进行更改。这确实告诉我闪存驱动器何时插入,但据我所知,没有办法检索它的位置。 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

【问题讨论】:

【参考方案1】:

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

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

您必须在 Windows 循环中捕获 IOException,因为如果 CD 驱动器中没有 CD,则在您尝试为其检索 FileStore 时会引发异常。这可能发生在您遍历每个根之前。

【讨论】:

这真的适用于 Windows 吗?它似乎会错过我安装在 C:\Data 的驱动器。我使用这个较新的 API 的全部意义在于它承诺会找到所有的挂载点,而不仅仅是根。 另外,/media?你不是说 /mnt 吗? Linux 领域的人似乎建议运行 mount 并解析输出 - 因为这些工具的输出足够规则,可以处理 shell 脚本解析,Java 没有太大问题。然而,仍然不是一个非常令人满意的解决方法,我又回到了对 Windows 根本没有解决方案的状态。另外我认为您的意思是普通情况,而不是标准情况。 “标准”这个词很少适用于 Windows。 :D @Trejkaz 够公平的。我不久前向 Oracle 提交了一份 RFE,希望他们能在这十年内做点什么;) 确实如此。与此同时,也有黑客攻击。 :D【参考方案2】:

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

【讨论】:

其他注意事项:由于私有字段如有更改,恕不另行通知,除非另有确认,否则这仅适用于当前版本的 Java。 是的。如果您使用它,请务必添加单元测试以检测行为变化。 在 CentOS (6) 上不起作用。 FileStore 是 LinuxFileStore,但在 hacksMap 中它被添加为 UnixFileStore。和 Hacks hacks = hacksMap.get(store.getClass());一无所获。更糟糕的是,getDeclaredField("file") - 返回原始文件,而不是挂载点,它隐藏在 UnixMountEntry 条目内的容器字节 [] 目录中 是的。你必须为 LinuxFileStore 实现一个新的 hack,但在你实现一个之后它会起作用。【参考方案3】:

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

那里似乎也链接了许多相关问题。

【讨论】:

File.listRoots 仅适用于 Windows,我已经查看了这些问题,但无济于事。在nio2出来之前他们都被问到了 Ahh >_>如果我找到任何东西,我会继续寻找并编辑这个答案 @Jeffrey 它甚至不适用于 Windows,因为在 Windows 上,您可以将驱动器未安装在驱动器号上。 使用 FileSystems.getDefault().getRootDirectories() 比使用 File.listRoots() 更好。【参考方案4】:

这适用于 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) 过滤,我们排除了所有在未插入光盘时会抛出 IOException 的 CD/DVD 驱动器。

【讨论】:

以上是关于查找 FileStore 的目录的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Agiletoolkit 的 API 中使用 Filestore?

FileStore::OpenErr 在内存位置 0x012FED64

Databricks:将dbfs:/ FileStore文件下载到我的本地计算机?

获取任何给定路径的 FileStore 对象

Ceph单机引擎FileStore简介

Ceph单机引擎FileStore简介