2012-05-21 17 views
10

Estoy tratando de encontrar una manera de detectar cuando una unidad flash ha sido conectada a mi computadora. Hasta el momento, la solución que encontré fue sondear FileSystem#getFileStores por cambios. Esto realmente me dice cuándo se ha insertado la unidad flash, pero hasta donde sé, no hay forma de recuperar la ubicación. FileStore#type y FileStore#name ambos parecen muy poco confiables ya que su valor de retorno es específico de la implementación, pero parecen ser los únicos métodos que pueden devolver información relevante que pueda ayudar a encontrar el directorio para el FileStore.Busque el directorio de un FileStore

Con esto en mente, el siguiente código:

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

me dio este resultado:

/ (/dev/sda5) 
    /dev/sda5 
    ext4 

/* snip */ 

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

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

Como resultado, FileStore#type devuelve el formato de la unidad y FileStore#name devuelve la ubicación del el archivo de dispositivo para la unidad. Por lo que puedo decir, el único método que tiene la ubicación de la unidad es el método toString, pero extraer el nombre de la ruta parece peligroso porque no estoy seguro de qué tan bien esa solución en particular se mantendría en otros sistemas operativos y versiones futuras de Java.

¿Hay algo que me falta aquí o simplemente no es posible simplemente con Java?

información del sistema:

$ 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 

Respuesta

10

Aquí hay un trabajo temporal alrededor hasta que se encuentre una solución mejor:

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; 
} 

Por lo que yo sé, esta solución sólo funcionará para los sistemas Windows y Linux.

Tiene que capturar el IOException en el bucle de Windows porque si no hay un CD en la unidad de CD una excepción se produce cuando intenta recuperar el FileStore para ello. Esto puede suceder antes de iterar sobre cada raíz.

+0

¿Esto realmente funciona para Windows? Parece que extrañaría un disco que monté en C: \ Data. Todo el punto que estoy usando esta nueva API es que prometió encontrarme todos los puntos de montaje y no solo las raíces. – Trejkaz

+0

Además, ¿/ media? ¿No quieres decir/mnt? – Trejkaz

+0

@Trejkaz No sabía que pudieras hacer eso. Funciona para el caso estándar donde las unidades se montan como letras. Al menos en Ubuntu, las cosas están montadas por defecto en/media. También puede usar/etc/mtab para un trabajo más flexible en los sistemas Linux (realmente no uso tanto Windows, así que no sé de otro). – Jeffrey

0

Realmente no he explorado esta área de java, pero he encontrado this, que parece estar relacionado. Utiliza File.listRoots()

También parece haber una serie de preguntas relacionadas vinculadas allí también.

+1

'File.listRoots' sólo funcionará para Windows, y he revisado las preguntas en vano.Todos fueron preguntados antes de que saliera nio2 – Jeffrey

+0

Ahh> _> Seguiré buscando y editando esta respuesta si encuentro algo –

+0

@Jeffrey ni siquiera funcionará para Windows porque en Windows, puede tener unidades no montadas en una unidad carta. – Trejkaz

4

Esto es lo que he terminado haciendo. Esto está limitado a Windows + UNIX, pero evita el uso de herramientas externas o llamadas a la biblioteca adicionales. Roba la información que Java ya tiene en los objetos FileStore

LinuxFileStore definitivamente se extiende UnixFileStore, por lo que funcionará. El mismo trato para Solaris. Como Mac OS X es UNIX, probablemente funcione allí, pero no estoy seguro porque no pude ver su subclase en el lugar que estaba buscando.

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

Nota para otros: dado que los campos privados están sujetos a cambios sin previo aviso, esto solo funcionará para la versión actual de Java hasta que se confirme lo contrario. – Jeffrey

+1

Sí. Definitivamente agregue pruebas unitarias para detectar el cambio de comportamiento, si usa esto. – Trejkaz

-1

uso más fácil manera Files.getFileStore(Paths.get("/home/...")

+2

Eso es lo contrario de lo que estoy buscando. Estás tomando un 'Path' y obteniendo un' FileStore', pero quiero tomar un 'FileStore' y obtener' Path'. – Jeffrey

Cuestiones relacionadas