subcarpetas - Eliminar directorios recursivamente en Java
leer multiples archivos en java (23)
A continuación el código borra recursivamente todos los contenidos en una carpeta dada.
boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}
¿Hay una manera de eliminar directorios enteros recursivamente en Java?
En el caso normal es posible eliminar un directorio vacío. Sin embargo, cuando se trata de eliminar directorios enteros con contenido, ya no es tan simple.
¿Cómo eliminar directorios enteros con contenidos en Java?
Acabo de ver que mi solución es más o menos la misma que la de erickson, simplemente empaquetada como un método estático. Deje esto en alguna parte, es mucho más liviano que instalar todo Apache Commons para algo que (como puede ver) es bastante simple.
public class FileUtils {
/**
* By default File#delete fails for non-empty directories, it works like "rm".
* We need something a little more brutual - this does the equivalent of "rm -r"
* @param path Root File Path
* @return true iff the file and all sub files/directories have been removed
* @throws FileNotFoundException
*/
public static boolean deleteRecursive(File path) throws FileNotFoundException{
if (!path.exists()) throw new FileNotFoundException(path.getAbsolutePath());
boolean ret = true;
if (path.isDirectory()){
for (File f : path.listFiles()){
ret = ret && deleteRecursive(f);
}
}
return ret && path.delete();
}
}
Aquí hay un método principal básico que acepta un argumento de línea de comando, es posible que deba adjuntar su propio control de errores o moldearlo a su gusto.
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class DeleteFiles {
/**
* @param intitial arguments take in a source to read from and a
* destination to read to
*/
public static void main(String[] args)
throws FileNotFoundException,IOException {
File src = new File(args[0]);
if (!src.exists() ) {
System.out.println("FAILURE!");
}else{
// Gathers files in directory
File[] a = src.listFiles();
for (int i = 0; i < a.length; i++) {
//Sends files to recursive deletion method
fileDelete(a[i]);
}
// Deletes original source folder
src.delete();
System.out.println("Success!");
}
}
/**
* @param srcFile Source file to examine
* @throws FileNotFoundException if File not found
* @throws IOException if File not found
*/
private static void fileDelete(File srcFile)
throws FileNotFoundException, IOException {
// Checks if file is a directory
if (srcFile.isDirectory()) {
//Gathers files in directory
File[] b = srcFile.listFiles();
for (int i = 0; i < b.length; i++) {
//Recursively deletes all files and sub-directories
fileDelete(b[i]);
}
// Deletes original sub-directory file
srcFile.delete();
} else {
srcFile.delete();
}
}
}
¡Espero que eso ayude!
Bueno, asumamos un ejemplo,
import java.io.File;
import java.io.IOException;
public class DeleteDirectory
{
private static final String folder = "D:/project/java";
public static void main(String[] args) throws IOException
{
File fl = new File(folder);
if(!fl.exists()) // checking if directory exists
{
System.out.println("Sorry!! directory doesn''t exist.");
}
else
{
DeleteDirectory dd = new DeleteDirectory();
dd.deleteDirectory(fl);
}
}
public void deleteDirectory(File file) throws IOException
{
if(file.isDirectory())
{
if(file.list().length == 0)
{
deleteEmptyDirectory(file); // here if directory is empty delete we are deleting
}
else
{
File fe[] = file.listFiles();
for(File deleteFile : fe)
{
deleteDirectory(deleteFile); // recursive call
}
if(file.list().length == 0)
{
deleteEmptyDirectory(file);
}
}
}
else
{
file.delete();
System.out.println("File deleted : " + file.getAbsolutePath());
}
}
private void deleteEmptyDirectory(File fi)
{
fi.delete();
System.out.println("Directory deleted : " + fi.getAbsolutePath());
}
}
Para más información consulte los recursos a continuación.
Codifiqué esta rutina que tiene 3 criterios de seguridad para un uso más seguro.
package ch.ethz.idsc.queuey.util;
import java.io.File;
import java.io.IOException;
/** recursive file/directory deletion
*
* safety from erroneous use is enhanced by three criteria
* 1) checking the depth of the directory tree T to be deleted
* against a permitted upper bound "max_depth"
* 2) checking the number of files to be deleted #F
* against a permitted upper bound "max_count"
* 3) if deletion of a file or directory fails, the process aborts */
public final class FileDelete {
/** Example: The command
* FileDelete.of(new File("/user/name/myapp/recordings/log20171024"), 2, 1000);
* deletes given directory with sub directories of depth of at most 2,
* and max number of total files less than 1000. No files are deleted
* if directory tree exceeds 2, or total of files exceed 1000.
*
* abort criteria are described at top of class
*
* @param file
* @param max_depth
* @param max_count
* @return
* @throws Exception if criteria are not met */
public static FileDelete of(File file, int max_depth, int max_count) throws IOException {
return new FileDelete(file, max_depth, max_count);
}
// ---
private final File root;
private final int max_depth;
private int removed = 0;
/** @param root file or a directory. If root is a file, the file will be deleted.
* If root is a directory, the directory tree will be deleted.
* @param max_depth of directory visitor
* @param max_count of files to delete
* @throws IOException */
private FileDelete(final File root, final int max_depth, final int max_count) throws IOException {
this.root = root;
this.max_depth = max_depth;
// ---
final int count = visitRecursively(root, 0, false);
if (count <= max_count) // abort criteria 2)
visitRecursively(root, 0, true);
else
throw new IOException("more files to be deleted than allowed (" + max_count + "<=" + count + ") in " + root);
}
private int visitRecursively(final File file, final int depth, final boolean delete) throws IOException {
if (max_depth < depth) // enforce depth limit, abort criteria 1)
throw new IOException("directory tree exceeds permitted depth");
// ---
int count = 0;
if (file.isDirectory()) // if file is a directory, recur
for (File entry : file.listFiles())
count += visitRecursively(entry, depth + 1, delete);
++count; // count file as visited
if (delete) {
final boolean deleted = file.delete();
if (!deleted) // abort criteria 3)
throw new IOException("cannot delete " + file.getAbsolutePath());
++removed;
}
return count;
}
public int deletedCount() {
return removed;
}
public void printNotification() {
int count = deletedCount();
if (0 < count)
System.out.println("deleted " + count + " file(s) in " + root);
}
}
Con Java 7, finalmente podemos hacer esto con la detección confiable de enlaces simbólicos. (No considero que el commons-io de Apache tenga una detección confiable de enlaces simbólicos en este momento, ya que no maneja enlaces en Windows creados con mklink
).
Por el bien de la historia, aquí hay una respuesta previa a Java 7, que sigue los enlaces simbólicos.
void delete(File f) throws IOException {
if (f.isDirectory()) {
for (File c : f.listFiles())
delete(c);
}
if (!f.delete())
throw new FileNotFoundException("Failed to delete file: " + f);
}
Deberías revisar los commons-io de Apache . Tiene una clase de FileUtils que hará lo que quieras.
FileUtils.deleteDirectory(new File("directory"));
Dos formas de fallar con los enlaces simbólicos y el código anterior ... y no conocer la solución.
Camino # 1
Ejecuta esto para crear una prueba:
echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete
Aquí puedes ver tu archivo de prueba y directorio de prueba:
$ ls testfile dirtodelete
testfile
dirtodelete:
linktodelete
Luego ejecute su commons-io deleteDirectory (). Se bloquea diciendo que el archivo no se encuentra. No estoy seguro de lo que hacen los otros ejemplos aquí. El comando rm de Linux simplemente eliminaría el enlace, y rm -r en el directorio también lo haría.
Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete
Camino # 2
Ejecuta esto para crear una prueba:
mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete
Aquí puedes ver tu archivo de prueba y directorio de prueba:
$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete
testdir:
testfile
Luego ejecute su commons-io deleteDirectory () o el código de ejemplo que publicaron las personas. Elimina no solo el directorio, sino también el archivo de prueba que se encuentra fuera del directorio que se está eliminando. (Dereferencia implícita del directorio y borra los contenidos). rm -r borraría el enlace solamente. Debe usar algo como esto para eliminar los archivos anulados: "find -L dirtodelete -type f -exec rm {} /;".
$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:
En Java 7+ puedes usar la clase Files
. El código es muy simple:
Path directory = Paths.get("/tmp");
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
En proyectos heredados, necesito crear código nativo de Java. Creo este código similar al código Paulitex. Mira eso:
public class FileHelper {
public static boolean delete(File fileOrFolder) {
boolean result = true;
if(fileOrFolder.isDirectory()) {
for (File file : fileOrFolder.listFiles()) {
result = result && delete(file);
}
}
result = result && fileOrFolder.delete();
return result;
}
}
Y la prueba unitaria:
public class FileHelperTest {
@Before
public void setup() throws IOException {
new File("FOLDER_TO_DELETE/SUBFOLDER").mkdirs();
new File("FOLDER_TO_DELETE/SUBFOLDER_TWO").mkdirs();
new File("FOLDER_TO_DELETE/SUBFOLDER_TWO/TEST_FILE.txt").createNewFile();
}
@Test
public void deleteFolderWithFiles() {
File folderToDelete = new File("FOLDER_TO_DELETE");
Assert.assertTrue(FileHelper.delete(folderToDelete));
Assert.assertFalse(new File("FOLDER_TO_DELETE").exists());
}
}
Java 7 agregó soporte para directorios móviles con manejo de enlaces simbólicos:
import java.nio.file.*;
public static void removeRecursive(Path path) throws IOException
{
Files.walkFileTree(path, new SimpleFileVisitor<Path>()
{
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
{
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
{
// try to delete the file anyway, even if its attributes
// could not be read, since delete-only access is
// theoretically possible
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException
{
if (exc == null)
{
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
else
{
// directory iteration failed; propagate exception
throw exc;
}
}
});
}
Utilizo esto como una alternativa de los métodos específicos de la plataforma (en este código no probado ):
public static void removeDirectory(Path directory) throws IOException
{
// does nothing if non-existent
if (Files.exists(directory))
{
try
{
// prefer OS-dependent directory removal tool
if (SystemUtils.IS_OS_WINDOWS)
Processes.execute("%ComSpec%", "/C", "RD /S /Q /"" + directory + ''"'');
else if (SystemUtils.IS_OS_UNIX)
Processes.execute("/bin/rm", "-rf", directory.toString());
}
catch (ProcessExecutionException | InterruptedException e)
{
// fallback to internal implementation on error
}
if (Files.exists(directory))
removeRecursive(directory);
}
}
(SystemUtils es de Apache Commons Lang . Los procesos son privados, pero su comportamiento debería ser obvio).
Mientras que los archivos se pueden eliminar fácilmente usando file.delete (), se requiere que los directorios estén vacíos para ser eliminados. Usa la recursividad para hacer esto fácilmente. Por ejemplo:
public static void clearFolders(String[] args) {
for(String st : args){
File folder = new File(st);
if (folder.isDirectory()) {
File[] files = folder.listFiles();
if(files!=null) {
for(File f: files) {
if (f.isDirectory()){
clearFolders(new String[]{f.getAbsolutePath()});
f.delete();
} else {
f.delete();
}
}
}
}
}
}
Podrías usar:
org.apache.commons.io.FileUtils.deleteQuietly(destFile);
Borra un archivo, nunca lanza una excepción. Si el archivo es un directorio, elimínelo y todos los subdirectorios. La diferencia entre File.delete () y este método es: un directorio que se va a eliminar no tiene que estar vacío. No se lanzan excepciones cuando un archivo o directorio no puede ser eliminado.
Si tiene Spring, puede usar FileSystemUtils.deleteRecursively :
import org.springframework.util.FileSystemUtils;
boolean success = FileSystemUtils.deleteRecursively(new File("directory"));
Sin Commons IO y <Java SE 7
public static void deleteRecursive(File path){
path.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
pathname.listFiles(this);
pathname.delete();
} else {
pathname.delete();
}
return false;
}
});
path.delete();
}
Tal vez una solución para este problema podría ser volver a implementar el método de eliminación de la clase File utilizando el código de la respuesta de erickson:
public class MyFile extends File {
... <- copy constructor
public boolean delete() {
if (f.isDirectory()) {
for (File c : f.listFiles()) {
return new MyFile(c).delete();
}
} else {
return f.delete();
}
}
}
Una solución óptima que maneje la excepción de manera consistente con el enfoque de que una excepción lanzada desde un método siempre debe describir lo que el método intentaba (y falló) hacer:
private void deleteRecursive(File f) throws Exception {
try {
if (f.isDirectory()) {
for (File c : f.listFiles()) {
deleteRecursive(c);
}
}
if (!f.delete()) {
throw new Exception("Delete command returned false for file: " + f);
}
}
catch (Exception e) {
throw new Exception("Failed to delete the folder: " + f, e);
}
}
Una solución con una pila y sin métodos recursivos:
File dir = new File("/path/to/dir");
File[] currList;
Stack<File> stack = new Stack<File>();
stack.push(dir);
while (! stack.isEmpty()) {
if (stack.lastElement().isDirectory()) {
currList = stack.lastElement().listFiles();
if (currList.length > 0) {
for (File curr: currList) {
stack.push(curr);
}
} else {
stack.pop().delete();
}
} else {
stack.pop().delete();
}
}
Guava tenía Files.deleteRecursively(File)
admitido hasta Guava 9 .
De guayaba 10 :
Obsoleto. Este método sufre de una mala detección de enlaces simbólicos y condiciones de carrera. Esta funcionalidad se puede admitir adecuadamente solo mediante el envío de un comando del sistema operativo como
rm -rf
odel /s
. Este método está programado para ser eliminado de Guava en la versión 11.0 de Guava.
Por lo tanto, no existe tal método en Guava 11 .
Solución de una sola línea (Java8) para eliminar todos los archivos y directorios de forma recursiva, incluido el directorio de inicio:
Files.walk(Paths.get("c:/dir_to_delete/"))
.map(Path::toFile)
.sorted((o1, o2) -> -o1.compareTo(o2))
.forEach(File::delete);
Utilizamos un comparador para el orden inverso, de lo contrario, File :: delete no podrá eliminar un directorio que posiblemente no esté vacío. Por lo tanto, si desea mantener los directorios y solo eliminar archivos, simplemente elimine el comparador en ordenado () o elimine la clasificación por completo y agregue el filtro de archivos:
Files.walk(Paths.get("c:/dir_to_delete/"))
.filter(Files::isRegularFile)
.map(Path::toFile)
.forEach(File::delete);
for(Path p : Files.walk(directoryToDelete).
sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
toArray(Path[]::new))
{
Files.delete(p);
}
O si quieres manejar la IOException
:
Files.walk(directoryToDelete).
sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
forEach(p -> {
try { Files.delete(p); }
catch(IOException e) { /* ... */ }
});
public void deleteRecursive(File path){
File[] c = path.listFiles();
System.out.println("Cleaning out folder:" + path.toString());
for (File file : c){
if (file.isDirectory()){
System.out.println("Deleting file:" + file.toString());
deleteRecursive(file);
file.delete();
} else {
file.delete();
}
}
path.delete();
}
static public void deleteDirectory(File path)
{
if (path == null)
return;
if (path.exists())
{
for(File f : path.listFiles())
{
if(f.isDirectory())
{
deleteDirectory(f);
f.delete();
}
else
{
f.delete();
}
}
path.delete();
}
}