from files create java file file-io directory temporary-directory

files - java create directory path



¿Cómo crear un directorio/carpeta temporal en Java? (17)

A partir de Java 1.7, createTempDirectory(prefix, attrs) y createTempDirectory(dir, prefix, attrs) se incluyen en java.nio.file.Files

Ejemplo: File tempDir = Files.createTempDirectory("foobar").toFile();

¿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 existe una solución estándar en una de las bibliotecas habituales (Apache Commons, etc.)


Antes de Java 7 también puedes:

File folder = File.createTempFile("testFileUtils", ""); // no suffix folder.delete(); folder.mkdirs(); folder.deleteOnExit();


Bueno, "createTempFile" en realidad crea el archivo. Entonces, ¿por qué no eliminarlo primero y luego hacer mkdir en él?


Como puede ver en las otras respuestas, no ha surgido un enfoque estándar. Por lo tanto, ya mencionó Apache Commons, propongo el siguiente enfoque usando FileUtils de Apache Commons IO :

/** * Creates a temporary subdirectory in the standard temporary directory. * This will be automatically deleted upon exit. * * @param prefix * the prefix used to create the directory, completed by a * current timestamp. Use for instance your application''s name * @return the directory */ public static File createTempDirectory(String prefix) { final File tmp = new File(FileUtils.getTempDirectory().getAbsolutePath() + "/" + prefix + System.currentTimeMillis()); tmp.mkdir(); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { FileUtils.deleteDirectory(tmp); } catch (IOException e) { e.printStackTrace(); } } }); return tmp; }

Esto se prefiere ya que apache usa la biblioteca que se acerca más al "estándar" solicitado y funciona con JDK 7 y versiones anteriores. Esto también devuelve una instancia de archivo "antigua" (que se basa en la transmisión) y no una instancia de ruta "nueva" (que se basa en el búfer y sería el resultado del método getTemporaryDirectory () de JDK7) -> Por lo tanto, devuelve lo que la mayoría de la gente necesita cuando Ellos quieren crear un directorio temporal.


Como se explica en esta RFE y sus comentarios, primero puede llamar a tempDir.delete() . O puede usar System.getProperty("java.io.tmpdir") y crear un directorio allí. De cualquier manera, debes recordar llamar a tempDir.deleteOnExit() , o el archivo no se eliminará una vez que hayas terminado.


El código escrito de forma ingenua para resolver este problema sufre condiciones de carrera, incluidas varias de las respuestas aquí. Históricamente, podría pensar detenidamente sobre las condiciones de la raza y escribirlo usted mismo, o podría usar una biblioteca de terceros como la Guava de Google (como sugería la respuesta de Spina). O bien, podría escribir un código con errores.

Pero a partir del JDK 7, ¡hay buenas noticias! La biblioteca estándar de Java en sí misma ahora proporciona una solución que funciona correctamente (no irreal) para este problema. Desea java.nio.file.Files#createTempDirectory() . De la java.nio.file.Files#createTempDirectory() :

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 de manera efectiva el informe de errores vergonzosamente antiguo en el rastreador de errores de Sun que solicitó tal función.


El uso de File#createTempFile y delete para crear un nombre único para el directorio parece correcto. Debe agregar un ShutdownHook para eliminar el directorio (recursivamente) en el cierre de JVM.


Este código debería funcionar razonablemente bien:

public static File createTempDir() { final String baseTempPath = System.getProperty("java.io.tmpdir"); Random rand = new Random(); int randomInt = 1 + rand.nextInt(); File tempDir = new File(baseTempPath + File.separator + "tempDir" + randomInt); if (tempDir.exists() == false) { tempDir.mkdir(); } tempDir.deleteOnExit(); return tempDir; }


Este es el código fuente de Files.createTempDir () de la biblioteca de Guava. No es en ninguna parte 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í


Esto es lo que decidí hacer por mi propio código:

/** * Create a new temporary directory. Use something like * {@link #recursiveDelete(File)} to clean this directory up since it isn''t * deleted automatically * @return the new directory * @throws IOException if there is an error creating the temporary directory */ public static File createTempDir() throws IOException { final File sysTempDir = new File(System.getProperty("java.io.tmpdir")); File newTempDir; final int maxAttempts = 9; int attemptCount = 0; do { attemptCount++; if(attemptCount > maxAttempts) { throw new IOException( "The highly improbable has occurred! Failed to " + "create a unique temporary directory after " + maxAttempts + " attempts."); } String dirName = UUID.randomUUID().toString(); newTempDir = new File(sysTempDir, dirName); } while(newTempDir.exists()); if(newTempDir.mkdirs()) { return newTempDir; } else { throw new IOException( "Failed to create temp dir named " + newTempDir.getAbsolutePath()); } } /** * Recursively delete file or directory * @param fileOrDir * the file or dir to delete * @return * true iff all files are successfully deleted */ public static boolean recursiveDelete(File fileOrDir) { if(fileOrDir.isDirectory()) { // recursively delete contents for(File innerFile: fileOrDir.listFiles()) { if(!FileUtilities.recursiveDelete(innerFile)) { return false; } } } return fileOrDir.delete(); }


La biblioteca de Google Guava tiene un montón de utilidades útiles. Una de las notas aquí es la clase de 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 here , verá que la adaptación propuesta de File.createTempFile("install", "dir") normalmente introduce vulnerabilidades de seguridad.


Me gustan los intentos múltiples de crear un nombre único, pero incluso esta solución no descarta una condición de carrera. Otro proceso puede fallar después de la prueba de exists() y la invocación del método if(newTempDir.mkdirs()) . No tengo idea de cómo hacer esto completamente seguro sin recurrir al código nativo, que supongo que es lo que está File.createTempFile() dentro de File.createTempFile() .


No use deleteOnExit() incluso si lo elimina explícitamente más tarde.

Google ''deleteonexit is evil'' para obtener más información, pero la esencia del problema es:

  1. deleteOnExit() solo elimina las paradas de JVM normales, no se bloquea ni se detiene el proceso de JVM.

  2. deleteOnExit() solo elimina en el cierre de JVM, lo que no es bueno para procesos de servidor de larga ejecución porque:

  3. La más perversa de todas: deleteOnExit() consume memoria para cada entrada de archivo temporal. Si su proceso se ejecuta durante meses, o crea una gran cantidad de archivos temporales en poco tiempo, usted consume memoria y nunca la libera hasta que se apaga la JVM.


Sólo para completar, este es el código de la biblioteca de google guava. No es mi código, pero creo que es valioso mostrarlo aquí en este hilo.

/** Maximum loop count when creating temp directories. */ private static final int TEMP_DIR_ATTEMPTS = 10000; /** * Atomically creates a new directory somewhere beneath the system''s temporary directory (as * defined by the {@code java.io.tmpdir} system property), and returns its name. * * <p>Use this method instead of {@link File#createTempFile(String, String)} when you wish to * create a directory, not a regular file. A common pitfall is to call {@code createTempFile}, * delete the file and create a directory in its place, but this leads a race condition which can * be exploited to create security vulnerabilities, especially when executable files are to be * written into the directory. * * <p>This method assumes that the temporary volume is writable, has free inodes and free blocks, * and that it will not be called thousands of times per second. * * @return the newly-created directory * @throws IllegalStateException if the directory could not be created */ 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) + '')''); }


Si está utilizando JDK 7, use la nueva clase Files.createTempDirectory para crear el directorio temporal.

Path tempDirWithPrefix = Files.createTempDirectory(prefix);

Antes de JDK 7 esto debería hacerlo:

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ía hacer mejores excepciones (subclase IOException) si lo desea.


Si necesita un directorio temporal para realizar pruebas y está utilizando jUnit, @Rule junto con TemporaryFolder resuelve su problema:

@Rule public TemporaryFolder folder = new TemporaryFolder();

De la documentation :

La regla de la 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 usar JUnit Pioneer, que es el JUnit 5 Extension Pack.

Copiado de la documentación del proyecto :

Por ejemplo, la siguiente prueba registra la extensión para un solo 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'' }

Maven

<dependency> <groupId>org.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> <version>0.1.2</version> <scope>test</test> </dependency>


Tengo el mismo problema, así que esta es solo otra respuesta para aquellos que están interesados, y es similar a uno de los anteriores:

public static final String tempDir = System.getProperty("java.io.tmpdir")+"tmp"+System.nanoTime(); static { File f = new File(tempDir); if(!f.exists()) f.mkdir(); }

Y para mi aplicación, decidí agregar una opción para borrar la temperatura al salir, así que agregué un gancho de apagado:

Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { //stackless deletion String root = MainWindow.tempDir; Stack<String> dirStack = new Stack<String>(); dirStack.push(root); while(!dirStack.empty()) { String dir = dirStack.pop(); File f = new File(dir); if(f.listFiles().length==0) f.delete(); else { dirStack.push(dir); for(File ff: f.listFiles()) { if(ff.isFile()) ff.delete(); else if(ff.isDirectory()) dirStack.push(ff.getPath()); } } } } });

El método elimina todos los archivos y archivos antes de eliminar la temperatura , sin usar la pila de llamadas (que es totalmente opcional y podría hacerlo con recursión en este punto), pero quiero estar en el lado seguro.