¿Cómo crear un directorio/carpeta temporal en Java?
¿Existe una forma estándar y confiable de crear un directorio temporal dentro de una aplicación Java? Hay una entrada en la base de datos de problemas de Java , que tiene un poco de código en los comentarios, pero me pregunto si se puede encontrar una solución estándar en una de las bibliotecas habituales (Apache Commons, etc.).
Si está utilizando JDK 7, utilice la nueva clase Files.createTempDirectory para crear el directorio temporal.
Path tempDirWithPrefix = Files.createTempDirectory(prefix);
Antes de JDK 7 esto debería funcionar:
public static File createTempDirectory()
throws IOException
{
final File temp;
temp = File.createTempFile("temp", Long.toString(System.nanoTime()));
if(!(temp.delete()))
{
throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
}
if(!(temp.mkdir()))
{
throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
}
return (temp);
}
Podrías hacer mejores excepciones (subclase IOException) si lo deseas.
La biblioteca de Google Guava tiene un montón de utilidades útiles. Una cosa a tener en cuenta aquí es la clase Archivos . Tiene un montón de métodos útiles que incluyen:
File myTempDir = Files.createTempDir();
Esto hace exactamente lo que pediste en una línea. Si lee la documentación aquí, verá que la adaptación propuesta File.createTempFile("install", "dir")
normalmente introduce vulnerabilidades de seguridad.
Si necesita un directorio temporal para realizar pruebas y está utilizando jUnit, @Rule
esto TemporaryFolder
resuelve su problema:
@Rule
public TemporaryFolder folder = new TemporaryFolder();
De la documentación :
La regla de carpeta temporal permite la creación de archivos y carpetas que se garantiza que se eliminarán cuando finalice el método de prueba (ya sea que pase o falle).
Actualizar:
Si está utilizando JUnit Jupiter (versión 5.1.1 o superior), tiene la opción de utilizar JUnit Pioneer, que es el paquete de extensión JUnit 5.
Copiado de la documentación del proyecto :
Por ejemplo, la siguiente prueba registra la extensión para un único método de prueba, crea y escribe un archivo en el directorio temporal y verifica su contenido.
@Test
@ExtendWith(TempDirectory.class)
void test(@TempDir Path tempDir) {
Path file = tempDir.resolve("test.txt");
writeFile(file);
assertExpectedFileContent(file);
}
Más información en el JavaDoc y el JavaDoc de TempDirectory
Gradle:
dependencies {
testImplementation 'org.junit-pioneer:junit-pioneer:0.1.2'
}
experto:
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>0.1.2</version>
<scope>test</scope>
</dependency>
Actualización 2:
La anotación @TempDir se agregó a la versión JUnit Jupiter 5.4.0 como característica experimental. Ejemplo copiado de la Guía del usuario de JUnit 5 :
@Test
void writeItemsToFile(@TempDir Path tempDir) throws IOException {
Path file = tempDir.resolve("test.txt");
new ListWriter(file).write("a", "b", "c");
assertEquals(singletonList("a,b,c"), Files.readAllLines(file));
}
El código escrito ingenuamente para resolver este problema sufre condiciones de carrera, incluidas varias de las respuestas aquí. Históricamente, podría pensar detenidamente sobre las condiciones de carrera y escribirlas usted mismo, o podría usar una biblioteca de terceros como Guava de Google (como sugirió la respuesta de Spina). O podría escribir código con errores.
Pero a partir del JDK 7, ¡hay buenas noticias! La propia biblioteca estándar de Java ahora proporciona una solución que funciona correctamente (no es atractiva) para este problema. Quieres java.nio.file.Files#createTempDirectory() . De la documentación :
public static Path createTempDirectory(Path dir,
String prefix,
FileAttribute<?>... attrs)
throws IOException
Crea un nuevo directorio en el directorio especificado, utilizando el prefijo dado para generar su nombre. La ruta resultante está asociada con el mismo sistema de archivos que el directorio dado.
Los detalles sobre cómo se construye el nombre del directorio dependen de la implementación y, por lo tanto, no se especifican. Siempre que sea posible, el prefijo se utiliza para construir nombres de candidatos.
Esto resuelve efectivamente el vergonzosamente antiguo informe de error en el rastreador de errores de Sun que solicitaba dicha función.
Este es el código fuente de Files.createTempDir() de la biblioteca Guava. No es tan complejo como podría pensar:
public static File createTempDir() {
File baseDir = new File(System.getProperty("java.io.tmpdir"));
String baseName = System.currentTimeMillis() + "-";
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
throw new IllegalStateException("Failed to create directory within "
+ TEMP_DIR_ATTEMPTS + " attempts (tried "
+ baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
}
Por defecto:
private static final int TEMP_DIR_ATTEMPTS = 10000;
Mira aquí