1. Repaso: File vs Path vs Files
El veterano File
Sí… hay bastante código heredado (legacy) en los proyectos Java. Así que volvamos a recordar al veterano de Java, la clase File. Apareció en el lenguaje ya en la primera versión y permite trabajar con archivos y carpetas: saber si un archivo existe, su nombre, tamaño, fecha de modificación; crear y eliminar archivos y carpetas. Como ya sabemos, está lleno de inconvenientes: algunos métodos no son intuitivos, no todas las operaciones están soportadas de forma multiplataforma y algunos métodos no lanzan excepciones, sino que simplemente devuelven false (lo que no siempre es cómodo para depurar).
import java.io.File;
File file = new File("example.txt");
System.out.println("¿Existe el archivo? " + file.exists());
System.out.println("¿Es un archivo? " + file.isFile());
System.out.println("¿Es una carpeta? " + file.isDirectory());
Path y Files: visión moderna (Java 7+)
Con la llegada de Java 7 apareció el paquete java.nio.file, que ofrece herramientas más potentes y flexibles. Dos protagonistas clave:
- Path — una interfaz que describe la ruta a un archivo o carpeta. Se podría decir que es «una cadena con inteligencia»: sabe descomponer, unir, analizar y comparar rutas.
- Files — una clase utilitaria con un montón de métodos estáticos para trabajar con archivos y directorios: crear, eliminar, copiar, leer, escribir y obtener información.
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("example.txt");
System.out.println("Ruta: " + path);
Dato curioso: la clase Path no es solo una cadena. Conoce los separadores de directorios, sabe manejar rutas relativas y absolutas y funciona igual de bien en Windows y Linux.
Tabla comparativa de File y Path/Files
| Criterio | File (API antiguo) | Path + Files (API moderno) |
|---|---|---|
| Sintaxis | Estilo POO | Combinación de Path + métodos estáticos de Files |
| Compatibilidad multiplataforma | Parcial | Excelente; tiene en cuenta el sistema operativo |
| Excepciones | No siempre | Casi siempre |
| Nuevas funcionalidades | No | Sí (atributos, permisos, enlaces, etc.) |
| Recomendado | Más bien no | Sí |
2. Obtención de información sobre un archivo/directorio
Veamos cómo averiguar lo más importante sobre un archivo o una carpeta: si el objeto existe, su tipo (archivo o directorio), tamaño, nombre, ruta y directorio padre.
Comprobación de existencia
Con File
File file = new File("example.txt");
if (file.exists()) {
System.out.println("¡El archivo existe!");
} else {
System.out.println("Archivo no encontrado.");
}
Con Path y Files
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("example.txt");
if (Files.exists(path)) {
System.out.println("¡El archivo existe!");
} else {
System.out.println("Archivo no encontrado.");
}
Consejo: Para proyectos nuevos, utiliza siempre Path y Files. Es más seguro, más cómodo y multiplataforma.
Comprobación del tipo: archivo o directorio
File
if (file.isFile()) {
System.out.println("Es un archivo.");
} else if (file.isDirectory()) {
System.out.println("Es una carpeta.");
}
Path + Files
if (Files.isRegularFile(path)) {
System.out.println("Es un archivo.");
} else if (Files.isDirectory(path)) {
System.out.println("Es una carpeta.");
}
Obtención del tamaño de un archivo
File
long size = file.length();
System.out.println("Tamaño del archivo: " + size + " bytes");
Path + Files
try {
long size = Files.size(path);
System.out.println("Tamaño del archivo: " + size + " bytes");
} catch (IOException e) {
System.out.println("Error al obtener el tamaño del archivo: " + e.getMessage());
}
Atención: Para una carpeta, el método length() siempre devolverá 0, y Files.size(path) lanzará IOException. El tamaño de una carpeta es la suma de los tamaños de todos los archivos que contiene (más detalles en las siguientes lecciones).
Obtención de la ruta absoluta y relativa
File
System.out.println("Ruta relativa: " + file.getPath());
System.out.println("Ruta absoluta: " + file.getAbsolutePath());
Path
System.out.println("Ruta relativa: " + path);
System.out.println("Ruta absoluta: " + path.toAbsolutePath());
Obtención del nombre de archivo y el directorio padre
File
System.out.println("Nombre del archivo: " + file.getName());
System.out.println("Directorio padre: " + file.getParent());
Path
System.out.println("Nombre del archivo: " + path.getFileName());
System.out.println("Directorio padre: " + path.getParent());
Truco: Path puede trabajar no solo con archivos, sino también con carpetas; con rutas relativas y absolutas, y además descompone una ruta en partes con facilidad.
3. Obtención de atributos de archivo
A veces queremos saber no solo el tamaño y el nombre de un archivo, sino también algo más interesante: fecha de creación, fecha de la última modificación y permisos de acceso.
Fecha de creación y última modificación
File (limitado)
long lastModified = file.lastModified();
System.out.println("Fecha de la última modificación: " + new java.util.Date(lastModified));
Inconveniente: ¡No es posible obtener la fecha de creación del archivo a través de File!
Path + Files
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
try {
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
FileTime creationTime = attrs.creationTime();
FileTime lastModifiedTime = attrs.lastModifiedTime();
System.out.println("Fecha de creación: " + creationTime);
System.out.println("Fecha de la última modificación: " + lastModifiedTime);
} catch (IOException e) {
System.out.println("Error al obtener atributos: " + e.getMessage());
}
Comprobación de permisos de acceso
File
System.out.println("¿Se puede leer? " + file.canRead());
System.out.println("¿Se puede escribir? " + file.canWrite());
System.out.println("¿Se puede ejecutar? " + file.canExecute());
Path + Files
System.out.println("¿Se puede leer? " + Files.isReadable(path));
System.out.println("¿Se puede escribir? " + Files.isWritable(path));
System.out.println("¿Se puede ejecutar? " + Files.isExecutable(path));
Dato curioso: En Windows los permisos de acceso son limitados, mientras que en sistemas Unix se pueden obtener y cambiar muchos más (por ejemplo, mediante atributos POSIX).
4. Errores comunes al obtener información sobre archivos
Error n.º 1: Usar File cuando se necesita Path.
Muchos principiantes siguen utilizando File porque les resulta más sencillo o «porque así es en todo Internet». Pero con Path y Files se puede hacer lo mismo, de forma más fiable y moderna.
Error n.º 2: Comprobar el tipo de objeto solo con File.
Si solo utilizas file.isFile() y file.isDirectory(), puedes pasar por alto archivos «especiales» (enlaces simbólicos, dispositivos). En el API moderno existen Files.isRegularFile(path), Files.isDirectory(path) y otras comprobaciones.
Error n.º 3: Ignorar las excepciones.
Los métodos Files.size(path) y Files.readAttributes(...) lanzan IOException. Si no se gestionan las excepciones, el programa se caerá ante el primer error de acceso o si el archivo desaparece durante la ejecución.
Error n.º 4: Esperar que el tamaño de una carpeta se pueda obtener directamente.
El método Files.size(path), para una carpeta, lanzará una excepción. Para conocer el tamaño de una carpeta, hay que sumar de forma recursiva los tamaños de todos los archivos que contiene.
Error n.º 5: Comprobación insuficiente de los permisos de acceso.
Incluso si el archivo existe, es posible que no tengas permisos para leerlo o escribirlo. Comprueba siempre los permisos antes de realizar operaciones: Files.isReadable(path), Files.isWritable(path), Files.isExecutable(path).
GO TO FULL VERSION