ลบไดเรกทอรีซ้ำใน Java


382

มีวิธีการลบไดเรกทอรีทั้งหมดซ้ำใน Java?

ในกรณีปกติเป็นไปได้ที่จะลบไดเรกทอรีว่าง อย่างไรก็ตามเมื่อมันมาถึงการลบไดเรกทอรีทั้งหมดที่มีเนื้อหามันไม่ง่ายอีกต่อไป

คุณจะลบไดเรกทอรีทั้งหมดที่มีเนื้อหาใน Java ได้อย่างไร


4
File.delete () ควรกลับเท็จเมื่อเรียกมันด้วยไดเรกทอรีที่ไม่ว่างเปล่า
Ben S

หากคุณใช้ Java 8 ให้ดูที่คำตอบของ @ RoK
Robin

คำตอบ:


462

คุณควรตรวจสอบของ Apache Commons-io มีคลาสFileUtilsที่จะทำสิ่งที่คุณต้องการ

FileUtils.deleteDirectory(new File("directory"));

3
ฟังก์ชั่นนี้อาจหุ้มโค้ดที่ erickson ให้ไว้ในคำตอบของเขา
paweloque

14
มันละเอียดกว่านี้นิดหน่อย มันจัดการสิ่งต่าง ๆ เช่นลิงค์สัญลักษณ์อย่างถูกต้องบน Linux / Unix svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/…
Steve K

1
@Steve K, URL อยู่ในขณะนี้: svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/…
Richard EB

@RichardEB url อยู่ในขณะนี้: github.com/apache/commons-io/blob/master/src/main/java/org/…
swe

เพิ่มการพึ่งพาอีกทำไมเมื่อ Java มีสิ่งอำนวยความสะดวกออกมา? ดูคำตอบจาก RoK ในหน้านี้หรือ stackoverflow.com/questions/35988192/…
foo

190

ด้วย Java 7 ในที่สุดเราก็สามารถทำได้ด้วยการตรวจจับ symlink ที่เชื่อถือได้ (ฉันไม่คิดว่าคอมมอนส์ - io ของ Apache จะมีการตรวจจับ symlink ที่เชื่อถือได้ในขณะนี้เนื่องจากไม่รองรับการเชื่อมโยงบน Windows ที่สร้างขึ้นด้วยmklink)

เพื่อประโยชน์ของประวัติศาสตร์นี่คือคำตอบล่วงหน้าของ Java 7 ซึ่งตามมาด้วยการเชื่อมโยง

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

11
File.delete () ไม่มีฟังก์ชันการทำงานนั้น
Ben S

14
@Erickson: FileNotFoundException ไม่ใช่ข้อยกเว้นที่ไม่ดีสำหรับความล้มเหลวในการลบใช่หรือไม่ หากไฟล์นั้นไม่มีอยู่จริงอีกต่อไปไฟล์นั้นจะต้องถูกลบไปแล้วซึ่งหมายความว่าการลบไม่ได้ล้มเหลว - โดยไม่มีความหมาย และถ้ามันล้มเหลวด้วยเหตุผลอื่นมันไม่ใช่เพราะไม่พบไฟล์
Lawrence Dol

46
จงระวังให้ดี การทำเช่นนี้จะเป็นการยกเลิกการเชื่อมโยง หากคุณใช้งานอยู่เช่น linux และมีโฟลเดอร์ที่fooมีลิงก์foo/linkเช่นนั้นการlink->/โทรdelete(new File(foo)) จะลบระบบไฟล์ของคุณมากเท่ากับที่ผู้ใช้ของคุณอนุญาต !!
Miquel

4
@Miquel นั่นไม่สมเหตุสมผล - ทำไมเราถึงต้องระวัง? แน่นอนจุดของรหัสที่ให้ไว้คือการลบไดเรกทอรีทั้งหมดซึ่งเป็นสิ่งที่ดูเหมือนจะทำ ฉันไม่เข้าใจว่าสิ่งที่เป็นอันตรายอยู่ที่นี่
Joehot200

12
@ Joehot200 ถูกต้องการโทรลบบน symlink ไดเรกทอรีจะไม่ลบไดเรกทอรีเพียง symlink นั้นเอง การลบไดเรกทอรีจริงจะต้องมีต่อไป symlink อย่างชัดเจนโดยใช้ReadSymbolicLink ความผิดฉันเอง! ดีด่าง
Miquel

148

ใน Java 7+ คุณสามารถใช้Filesคลาส รหัสง่ายมาก:

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

2
วิธีการแก้ปัญหานี้ดูดีมากและไม่มีตรรกะการสำรวจเส้นทางไดเรกทอรีเลย!
Zero3

1
เพื่อค้นหาไข่มุกดำน้ำที่ลึกลงไปในมหาสมุทร. นี่เป็นวิธีการแก้ปัญหาที่ประณีตที่สุดที่ฉันพบ ต้องดำน้ำลึกเพื่อค้นหามัน ยอดเยี่ยม!
Basil Musa

20
"รหัสคือ" ไม่ "ง่ายมาก" เพียงแค่ลบ dir :-) แต่เดี๋ยวก่อนนั่นเป็นทางออกที่ดีที่สุดในจาวาบริสุทธิ์ฉันคิดว่า
Mat

1
โปรดทราบว่า walkFileTree โอเวอร์โหลดที่ใช้ที่นี่ " ไม่ได้ติดตามลิงก์สัญลักษณ์ " (Javadoc: docs.oracle.com/javase/7/docs/api/java/nio/file/… )
Stephan

1
คุณอาจจะต้องโทรตามวิธีการsuper.postVisitDirectory(dir, exc);ของคุณpostVisitDirectoryเพื่อระเบิดถ้าการเดินไม่สามารถแสดงรายการไดเรกทอรี
Tom Anderson

68

โซลูชัน One-liner (Java8)เพื่อลบไฟล์และไดเรกทอรีทั้งหมดซ้ำรวมถึงไดเรกทอรีเริ่มต้น:

Files.walk(Paths.get("c:/dir_to_delete/"))
                .map(Path::toFile)
                .sorted((o1, o2) -> -o1.compareTo(o2))
                .forEach(File::delete);

เราใช้เครื่องมือเปรียบเทียบสำหรับคำสั่งที่กลับรายการมิฉะนั้น File :: delete จะไม่สามารถลบไดเรกทอรีที่อาจไม่ว่างเปล่าได้ ดังนั้นหากคุณต้องการเก็บไดเรกทอรีและลบไฟล์เพียงแค่ลบตัวเปรียบเทียบในเรียง () หรือลบการเรียงลำดับอย่างสมบูรณ์และเพิ่มตัวกรองไฟล์:

Files.walk(Paths.get("c:/dir_to_delete/"))
                .filter(Files::isRegularFile)
                .map(Path::toFile)
                .forEach(File::delete);

1
คุณต้องเปลี่ยนการเรียงลำดับในรายการแรกเป็น. sorted (Comparator :: reverseOrder)เพื่อลบไดเรกทอรีทั้งหมด มิฉะนั้นจะสั่งไดเรกทอรีหลักก่อนที่เด็กและดังนั้นจะไม่ลบเพราะมันไม่ว่างเปล่า คำตอบที่ยอดเยี่ยมสำหรับผู้ที่ใช้ Java 8!
Robin

1
วิธีที่ถูกต้องคือ.sorted(Comparator.reverseOrder())ข้อเสนอแนะComparator::reverseOrderไม่ได้ทำงาน ดู: stackoverflow.com/questions/43036611/…
1156544

4
Robin สนใจที่เครื่องหมายลบใน "-o1.compareTo (o2)" มันเป็นเช่นเดียวกับ. สารพัน (Comparator.reverseOrder)
RoK

Files.walk เป็นลำดับหรือไม่ หรือคำตอบนี้จำเป็นสำหรับแต่ละคำสั่งแทนที่จะ forEach เพื่อหลีกเลี่ยงการพยายามลบไดเรกทอรีที่ไม่ว่างเปล่าหรือไม่?
Silwing

เพียงแค่ใช้: .sorted((f1, f2) -> f2.compareTo(f1))เปรียบเทียบf2กับf1แทนด้วยf1 f2
Beto Neto

67

Java 7 เพิ่มการรองรับไดเรกทอรีการเดินด้วยการจัดการ symlink:

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

ฉันใช้สิ่งนี้เป็นทางเลือกจากวิธีเฉพาะแพลตฟอร์ม (ในรหัสที่ยังไม่ทดลอง ):

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 มาจากApache Commons Langกระบวนการเป็นแบบส่วนตัว แต่พฤติกรรมของมันควรจะชัดเจน)


ฉันพบปัญหาหนึ่งกับ Files.walkFileTree - มันไม่เพียงพอสำหรับการใช้การลบแบบเรียกซ้ำที่คุณเก็บไฟล์ไว้จนกว่าคุณจะหมดตัวเลือก มันเพียงพอสำหรับรุ่นที่ล้มเหลวอย่างรวดเร็ว แต่ความล้มเหลวอย่างรวดเร็วนั้นไม่ใช่สิ่งที่คุณต้องการเสมอ (เช่นหากคุณกำลังล้างไฟล์ temp คุณต้องการลบตอนนี้ไม่ล้มเหลวอย่างรวดเร็ว)
Trejkaz

ฉันไม่เห็นสาเหตุที่เป็นจริง คุณสามารถจัดการข้อผิดพลาดได้ตามที่คุณต้องการไม่จำเป็นต้องล้มเหลวอย่างรวดเร็ว ปัญหาเดียวที่ฉันสามารถคาดการณ์ได้คือมันอาจไม่จัดการกับไฟล์ใหม่ที่ถูกสร้างขึ้นระหว่างการเดินของไดเรกทอรีปัจจุบัน แต่นั่นเป็นสถานการณ์ที่ไม่ซ้ำกันซึ่งเหมาะกับโซลูชันแบบกำหนดเอง
เทรเวอร์โรบินสัน

1
หากคุณระงับข้อผิดพลาดจาก visitFile และโทร walkFileTree ในไฟล์เดียวซึ่งล้มเหลวคุณจะไม่ได้รับข้อผิดพลาด (ดังนั้น VisitFile จะต้องเผยแพร่ข้อผิดพลาดใด ๆ ที่เกิดขึ้น) หากคุณกำลังลบไดเรกทอรีและไม่สามารถลบหนึ่งไฟล์ คือ postVisitDirectory นั่นคือจะไม่เยี่ยมชมไฟล์อื่น ๆ ในไดเรกทอรีหากคุณพบข้อผิดพลาดในการเข้าชมไฟล์เดียว นี่คือสิ่งที่ฉันหมายถึง ฉันแน่ใจว่าอาจมีวิธีการแก้ไขบางอย่าง แต่ตามเวลาที่เรามาถึงจุดนี้เราได้เขียนโค้ดเพิ่มเติมมากกว่ารูทีนการลบแบบเรียกซ้ำแบบเดิมดังนั้นเราจึงตัดสินใจใช้มัน
Trejkaz

ขอบคุณสำหรับรหัสที่ 1 ของคุณมันมีประโยชน์กับฉัน แต่ฉันต้องเปลี่ยนเพราะมันไม่ได้ทำ deltree ง่าย ๆ : ฉันต้องละเว้นข้อยกเว้นใน "postVisitDirectory" และส่งกลับ CONTINUE โดยไม่คำนึงถึงเพราะต้นไม้ที่เรียบง่ายต่อไปนี้อาจไม่สมบูรณ์ ถูกลบ: ไดเรกทอรีภายในซึ่งมีไดเรกทอรีอื่นอยู่ภายในซึ่งเป็นหนึ่งไฟล์ ทั้งหมดนี้เป็นเรื่องง่าย / ปกติตามที่ได้รับบน Windows
ประธาน Dreamspace

ทุกอย่างเริ่มต้นจาก java.nio.file.DirectoryNotEmptyException ที่ฉันได้รับ ฉันพบกรณีที่มีการใช้การเยี่ยมชม FileFailed หากโครงสร้างไดเรกทอรีของคุณมีลิงค์ประเภทจังก์ชันใน windows สิ่งนี้อาจทำให้คุณมีปัญหา 2 ข้อ: *) Files.walkFileTree ตามลิงก์ไปยังทางแยกและลบทุกอย่างที่นั่น *) หากไดเร็กทอรีเป้าหมายทางแยกถูกลบไปแล้วให้ทำการแยกวิเคราะห์ลิงก์โดย Files.walkFileTree ล้มเหลวด้วย NoSuchFileException ซึ่งถูกดักจับใน visitFileFailed
Andres Luuk

34

เพิ่งเห็นวิธีการแก้ปัญหาของฉันจะมากหรือน้อยเหมือน erickson เพียงแค่บรรจุเป็นวิธีคงที่ วางที่ใดที่หนึ่งซึ่งมีน้ำหนักเบากว่าการติดตั้ง Apache Commons ทั้งหมดสำหรับบางสิ่งที่ (เท่าที่คุณเห็น) ค่อนข้างง่าย

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

20

โซลูชันที่มีสแต็กและไม่มีเมธอดแบบเรียกซ้ำ:

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

2
+1 สำหรับการใช้สแต็ก สิ่งนี้จะทำงานกับไดเรกทอรีที่มีไดเรกทอรีย่อยที่ซ้อนกันระดับลึกในขณะที่วิธีการตามสแต็กอื่น ๆ จะล้มเหลว
นาธานออสมัน

4
การเห็นว่าคุณมักจะไม่มีปัญหาในการเรียกใช้เมธอดสองสามร้อยครั้งฉันคิดว่าคุณน่าจะพบข้อ จำกัด ของระบบไฟล์ได้เร็วกว่ามาก
Bombe

2
ระมัดระวังกับวิธีการสำหรับการเรียนlist* java.io.Fileจาก Javadocs: "ส่งคืนค่า null หากชื่อพา ธ นามธรรมนี้ไม่แสดงถึงไดเรกทอรีหรือหากเกิดข้อผิดพลาด I / O" ดังนั้น: if (currList.length > 0) {กลายเป็นif (null != currList && currList.length > 0) {
kevinarpe

1
ฉันใช้ ArrayDeque แทน Stack ซึ่งเร็วกว่าเล็กน้อย (ไม่
ซิงโครไนซ์


15

ฝรั่งได้Files.deleteRecursively(File)รับการสนับสนุนจนฝรั่ง 9

จากGuava 10 :

เลิก วิธีนี้ทนทุกข์ทรมานจากการตรวจจับ symlink ที่ไม่ดีและสภาวะการแข่งขัน ฟังก์ชั่นนี้ได้รับการสนับสนุนอย่างเหมาะสมโดยเฉพาะปลอกกระสุนออกไปยังคำสั่งระบบปฏิบัติการเช่นหรือrm -rf วิธีการนี้มีกำหนดจะถูกลบออกจาก Guava ใน Guava รีลีส 11.0del /s

ดังนั้นไม่มีวิธีการดังกล่าวในฝรั่ง 11


6
เลวร้ายเกินไป. การปอกเปลือกออกดูค่อนข้างหยาบและไม่พกพาได้ หาก Apache Commons เวอร์ชันทำงานอย่างถูกต้องแสดงว่าเป็นไปไม่ได้ที่จะนำไปใช้
Andrew McKinlay

6
@andrew การใช้งาน Apache Commons ควรมีปัญหาคล้ายกันกับที่ทำให้ Guava ลบการติดตั้งโปรดดูcode.google.com/p/guava-l
ไลบรารี/issues/detail?id=365

apache คอมมอนส์รุ่นตรวจจับการเชื่อมโยงและก็ไม่ได้สำรวจลูก ๆ ของไฟล์
อาแจ็กซ์

5
ฝรั่ง 21.0 เพิ่มนี้เป็นMoreFiles.deleteRecursively ()
Robert Fleming

12
for(Path p : Files.walk(directoryToDelete).
        sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
        toArray(Path[]::new))
{
    Files.delete(p);
}

หรือถ้าคุณต้องการจัดการกับIOException:

Files.walk(directoryToDelete).
    sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
    forEach(p -> {
        try { Files.delete(p); }
        catch(IOException e) { /* ... */ }
      });

2
สิ่งนี้ช่วยฉันในการสร้างเวอร์ชั่น Scala:Files.walk(path).iterator().toSeq.reverse.foreach(Files.delete)
James Ward

การเรียงลำดับจำเป็นจริงๆหรือ? walkวิธีแล้วรับประกันการข้ามผ่านความลึกแรก
VGR

ตัวเปรียบเทียบสามารถนำกลับมาใช้ใหม่ได้Collections.reverseOrder()ดังนั้นรหัสของคุณจะ for (Path p : Files.walk(directoryToDelete).sorted(reverseOrder()).toArray(Path[]::new))ถือว่าเป็นการนำเข้าแบบคงที่
namero999

@ namero999 คุณหมายถึงComparator.reverseOrderอะไร? Files.walk(dir) .sorted(Comparator.reverseOrder()) .toArray(Path[]::new))
Jeff

@ Jeff ค่อนข้างแน่ใจว่าคุณมีสิทธิ์ส่วนใหญ่ไปด้วยหน่วยความจำมี :)
namero999

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

5
รุ่นที่ปรับปรุงแล้วพร้อมค่าส่งคืนบูลีนและไม่มีการทำซ้ำ: pastebin.com/PqJyzQUx
Erik Kaplun

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

รหัสดี แต่มีข้อผิดพลาดหนึ่งเมื่อแก้ไขมันทำงาน บรรทัดด้านf.delete()ล่างdeleteDirectory(f)จะพ่น NoSuchFileException เนื่องจากdeleteDirectory(f)ไฟล์นั้นลบไปแล้ว ไดเรกทอรีทุกคนจะกลายเป็นเส้นทางเมื่อผ่านและถูกลบโดยdeleteDirectory(f) path.delete()ดังนั้นเราไม่ต้องการf.delete()ในif f.isDerectoryส่วน ดังนั้นเพียงแค่ลบf.delete();ภายใต้ deleteDirectory (f) และมันจะทำงาน
Trieu Nguyen

5

สองวิธีล้มเหลวด้วย symlinks และโค้ดด้านบน ... และไม่ทราบวิธีแก้ปัญหา

วิธี # 1

เรียกใช้สิ่งนี้เพื่อสร้างการทดสอบ:

echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete

ที่นี่คุณจะเห็นไฟล์ทดสอบและไดเรกทอรีทดสอบ:

$ ls testfile dirtodelete
testfile

dirtodelete:
linktodelete

จากนั้นเรียกใช้ commons-io deleteDirectory () ของคุณ มันล้มเหลวบอกว่าไม่พบไฟล์ ไม่แน่ใจว่าตัวอย่างอื่นทำอะไรที่นี่ คำสั่ง Linux rm จะลบการเชื่อมโยงและ rm -r ในไดเรกทอรีก็จะ

Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete

วิธี # 2

เรียกใช้สิ่งนี้เพื่อสร้างการทดสอบ:

mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete

ที่นี่คุณจะเห็นไฟล์ทดสอบและไดเรกทอรีทดสอบ:

$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete

testdir:
testfile

จากนั้นเรียกใช้ commons-io deleteDirectory () หรือตัวอย่างโค้ดที่ผู้คนโพสต์ มันจะลบไม่เพียง แต่ไดเรกทอรี แต่ testfile ของคุณซึ่งอยู่นอกไดเรกทอรีที่จะถูกลบ (จะยกเลิกการกำหนดไดเรกทอรีโดยปริยายและลบเนื้อหา) rm -r จะลบลิงค์เท่านั้น คุณต้องใช้สิ่งนี้ลบไฟล์ที่ถูกอ้างถึง: "find -L dirtodelete -type f -exec rm {} \;"

$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:

4

คุณสามารถใช้:

org.apache.commons.io.FileUtils.deleteQuietly(destFile);

ลบไฟล์ห้ามทิ้งข้อยกเว้น หากไฟล์เป็นไดเรกทอรีให้ลบมันและไดเรกทอรีย่อยทั้งหมด ความแตกต่างระหว่าง File.delete () และวิธีนี้คือ: ไดเรกทอรีที่จะลบไม่จำเป็นต้องว่างเปล่า จะไม่มีข้อยกเว้นเกิดขึ้นเมื่อไฟล์หรือไดเรกทอรีไม่สามารถลบได้


4

ทางออกที่ดีที่สุดที่จัดการข้อยกเว้นอย่างสอดคล้องกับวิธีการที่ข้อยกเว้นที่เกิดขึ้นจากวิธีการควรอธิบายถึงวิธีการที่พยายาม (และล้มเหลว) ในการทำ:

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

3

ในโครงการดั้งเดิมฉันต้องสร้างรหัส Java ดั้งเดิม ฉันสร้างรหัสนี้คล้ายกับรหัส Paulitex ดูว่า:

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

และการทดสอบหน่วย:

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

}

3

รหัสด้านล่างซ้ำลบเนื้อหาทั้งหมดในโฟลเดอร์ที่กำหนด

boolean deleteDirectory(File directoryToBeDeleted) {
    File[] allContents = directoryToBeDeleted.listFiles();
    if (allContents != null) {
        for (File file : allContents) {
            deleteDirectory(file);
        }
    }
    return directoryToBeDeleted.delete();
}

2

นี่คือวิธีการหลักของกระดูกเปลือยที่ยอมรับอาร์กิวเมนต์ของบรรทัดคำสั่งคุณอาจต้องผนวกการตรวจสอบข้อผิดพลาดของคุณเองหรือทำการขึ้นรูปตามที่คุณเห็นว่าเหมาะสม

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

ฉันหวังว่าจะช่วย!


1

บางทีวิธีแก้ปัญหาสำหรับปัญหานี้อาจเป็นการปรับใช้วิธีลบของคลาสไฟล์โดยใช้รหัสจากคำตอบของ 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();
    }
  }
}

1
ฉันคิดว่ามันใช้งานเพราะมันเลียนแบบพฤติกรรมของอรรถประโยชน์เชลล์คำสั่งส่วนใหญ่เช่น "rm", "rmdir" และ "del" ในสองทางเลือกการใช้งานในปัจจุบันลดความประหลาดใจโดยรวม (และความโกรธ) ที่อาจเกิดขึ้นได้อย่างแน่นอน มันจะไม่เปลี่ยนแปลง
erickson

4
โดยทั่วไปแพ็คเกจ Java JRE เดียวที่ฉันเห็นมีมาจาก Swing โดยปกติแล้วการขยายคลาสอื่น ๆ เช่น java.io.File เป็นความคิดที่ไม่ดีเนื่องจากมีความเป็นไปได้ที่จะทำให้สิ่งต่าง ๆ ดำเนินไปในลักษณะที่ไม่คาดคิด
Eddie

1

ไม่มีคอมมอนส์ IO และ <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();
        }

0

ในขณะที่ไฟล์สามารถลบได้อย่างง่ายดายโดยใช้ file.delete () ไดเรกทอรีจะต้องว่างเปล่าที่จะลบ ใช้การเรียกซ้ำเพื่อทำสิ่งนี้ได้อย่างง่ายดาย ตัวอย่างเช่น:

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

0

ฉันเขียนกิจวัตรประจำวันนี้ที่มี 3 เกณฑ์ความปลอดภัยเพื่อการใช้งานที่ปลอดภัยยิ่งขึ้น

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

0

สมมติว่าเป็นตัวอย่าง

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

สำหรับข้อมูลเพิ่มเติมอ้างอิงทรัพยากรด้านล่าง

ลบไดเรกทอรี


0

rm -rfถูกมากมากขึ้น performant FileUtils.deleteDirectoryกว่า

หลังจากการเปรียบเทียบที่กว้างขวางเราพบว่าการใช้ถูกหลายครั้งเร็วกว่าการใช้rm -rfFileUtils.deleteDirectory

แน่นอนถ้าคุณมีไดเรกทอรีขนาดเล็กหรือเรียบง่ายมันจะไม่สำคัญ แต่ในกรณีของเราเรามีหลายกิกะไบต์และไดเรกทอรีย่อยที่ซ้อนกันอย่างลึกซึ้งซึ่งจะใช้เวลามากกว่า 10 นาทีFileUtils.deleteDirectoryและใช้เวลาเพียง 1 นาทีrm -rfเท่านั้น

ต่อไปนี้เป็นการนำ Java มาใช้อย่างคร่าวๆเพื่อทำสิ่งนี้:

// Delete directory given and all subdirectories and files (i.e. recursively).
//
static public boolean deleteDirectory( File file ) throws IOException, InterruptedException {

    if ( file.exists() ) {

        String deleteCommand = "rm -rf " + file.getAbsolutePath();
        Runtime runtime = Runtime.getRuntime();

        Process process = runtime.exec( deleteCommand );
        process.waitFor();

        return true;
    }

    return false;

}

น่าลองถ้าคุณจัดการกับไดเรกทอรีขนาดใหญ่หรือซับซ้อน


มันทำงานข้ามแพลตฟอร์ม?
OneCricketeer

@ cricket_007 แพลตฟอร์มใดบ้าง
Joshua Pinter

Windows? OpenWrt? BSD?
OneCricketeer

1
@ cricket_007 ไม่ใช่ Windows แน่นอน สิ่งนี้ได้รับการทดสอบและใช้งานบน Android และ macOS
Joshua Pinter

0

ฝรั่งMoreFiles.deleteRecursively()ให้หนึ่งซับ:

ซึ่งแตกต่างจากตัวอย่างจำนวนมากที่ใช้ร่วมกันมันทำหน้าที่เป็นลิงก์สัญลักษณ์และจะไม่ลบไฟล์ออกนอกเส้นทางที่ระบุ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.