總結刪除檔案或資料夾的7種方法-JAVA IO基礎總結第4篇

字母哥部落格發表於2020-09-01

本文是Java IO總結系列篇的第4篇,前篇的訪問地址如下:

如果您閱讀完成,覺得此文對您有幫助,請給我點個贊,您的支援是我不竭的創作動力。

為了方便大家理解,我特意製作了本文對應的視訊:總結刪除檔案或者資料夾的7種方法

一、刪除檔案或資料夾的四種基礎方法

下面的四個方法都可以刪除檔案或資料夾,它們的共同點是:當資料夾中包含子檔案的時候都會刪除失敗,也就是說這四個方法只能刪除空資料夾

需要注意的是:傳統IO中的File類和NIO中的Path類既可以代表檔案,也可以代表資料夾。

  • File類的delete()
  • File類的deleteOnExit()
  • Files.delete(Path path)
  • Files.deleteIfExists(Path path);

它們之間的差異:

  • 由上面的對比可以看出,傳統IO方法刪除檔案或資料夾,再刪除失敗的時候,最多返回一個false。通過這個false無法發掘刪除失敗的具體原因,是因為檔案本身不存在刪除失敗?還是資料夾不為空導致的刪除失敗?
  • NIO 的方法在這一點上,就做的比較好,刪除成功或失敗都有具體的返回值或者異常資訊,這樣有利於我們在刪除檔案或資料夾的時候更好的做程式的異常處理
  • 需要注意的是傳統IO中的deleteOnExit方法,筆者覺得應該避免使用它。它永遠只返回void,刪除失敗也不會有任何的Exception丟擲,所以我建議不要用,以免在你刪除失敗的時候沒有任何的響應,而你可能誤以為刪除成功了。
//false只能告訴你失敗了 ,但是沒有給出任何失敗的原因
@Test
void testDeleteFileDir1()  {
   File file = new File("D:\\data\\test");
   boolean deleted = file.delete();
   System.out.println(deleted);
}

//void ,刪除失敗沒有任何提示,應避免使用這個方法,就是個坑
@Test
void testDeleteFileDir2()  {
   File file = new File("D:\\data\\test1");
   file.deleteOnExit();
}


//如果檔案不存在,丟擲NoSuchFileException
//如果資料夾裡面包含檔案,丟擲DirectoryNotEmptyException
@Test
void testDeleteFileDir3() throws IOException {
   Path path = Paths.get("D:\\data\\test1");
   Files.delete(path);   //返回值void
}

//如果檔案不存在,返回false,表示刪除失敗(檔案不存在)
//如果資料夾裡面包含檔案,丟擲DirectoryNotEmptyException
@Test
void testDeleteFileDir4() throws IOException {
   Path path = Paths.get("D:\\data\\test1");
   boolean result = Files.deleteIfExists(path);
   System.out.println(result);
}

歸根結底,建議大家使用java NIO的Files.delete(Path path)Files.deleteIfExists(Path path);進行檔案或資料夾的刪除。

二、如何刪除整個目錄或者目錄中的部分檔案

上文已經說了,那四個API刪除資料夾的時候,如果資料夾包含子檔案,就會刪除失敗。那麼,如果我們確實想刪除整個資料夾,該怎麼辦?

前提準備

為了方便我們後面進行試驗,先去建立這樣一個目錄結構,“.log”結尾的是資料檔案,其他的是資料夾

可以使用代面的程式碼進行建立

private  void createMoreFiles() throws IOException {
   Files.createDirectories(Paths.get("D:\\data\\test1\\test2\\test3\\test4\\test5\\"));
   Files.write(Paths.get("D:\\data\\test1\\test2\\test2.log"), "hello".getBytes());
   Files.write(Paths.get("D:\\data\\test1\\test2\\test3\\test3.log"), "hello".getBytes());
}

2.1. walkFileTree與FileVisitor

  • 使用walkFileTree方法遍歷整個檔案目錄樹,使用FileVisitor處理遍歷出來的每一項檔案或資料夾
  • FileVisitor的visitFile方法用來處理遍歷結果中的“檔案”,所以我們可以在這個方法裡面刪除檔案
  • FileVisitor的postVisitDirectory方法,注意方法中的“post”表示“後去做……”的意思,所以用來檔案都處理完成之後再去處理資料夾,所以使用這個方法刪除資料夾就可以有效避免資料夾內容不為空的異常,因為在去刪除資料夾之前,該資料夾裡面的檔案已經被刪除了。
@Test
void testDeleteFileDir5() throws IOException {
   createMoreFiles();
   Path path = Paths.get("D:\\data\\test1\\test2");

   Files.walkFileTree(path,
      new SimpleFileVisitor<Path>() {
         // 先去遍歷刪除檔案
         @Override
         public FileVisitResult visitFile(Path file,
                                  BasicFileAttributes attrs) throws IOException {
            Files.delete(file);
            System.out.printf("檔案被刪除 : %s%n", file);
            return FileVisitResult.CONTINUE;
         }
         // 再去遍歷刪除目錄
         @Override
         public FileVisitResult postVisitDirectory(Path dir,
                                         IOException exc) throws IOException {
            Files.delete(dir);
            System.out.printf("資料夾被刪除: %s%n", dir);
            return FileVisitResult.CONTINUE;
         }

      }
   );

}

下面的輸出體現了檔案的刪除順序

檔案被刪除 : D:\data\test1\test2\test2.log
檔案被刪除 : D:\data\test1\test2\test3\test3.log
資料夾被刪除 : D:\data\test1\test2\test3\test4\test5
資料夾被刪除 : D:\data\test1\test2\test3\test4
資料夾被刪除 : D:\data\test1\test2\test3
資料夾被刪除 : D:\data\test1\test2

我們既然可以遍歷出資料夾或者檔案,我們就可以在處理的過程中進行過濾。比如:

  • 按檔名刪除檔案或資料夾,引數Path裡面含有檔案或資料夾名稱
  • 按檔案建立時間、修改時間、檔案大小等資訊去刪除檔案,引數BasicFileAttributes 裡面包含了這些檔案資訊。

2.2.Files.walk

如果你對Stream流語法不太熟悉的話,這種方法稍微難理解一點,但是說實話也非常簡單。

  • 使用Files.walk遍歷資料夾(包含子資料夾及子其檔案),遍歷結果是一個Stream<Path>
  • 對每一個遍歷出來的結果進行處理,呼叫Files.delete就可以了。
@Test
void testDeleteFileDir6() throws IOException {
   createMoreFiles();
   Path path = Paths.get("D:\\data\\test1\\test2");

   try (Stream<Path> walk = Files.walk(path)) {
      walk.sorted(Comparator.reverseOrder())
         .forEach(DeleteFileDir::deleteDirectoryStream);
   }

}

private static void deleteDirectoryStream(Path path) {
   try {
      Files.delete(path);
      System.out.printf("刪除檔案成功:%s%n",path.toString());
   } catch (IOException e) {
      System.err.printf("無法刪除的路徑 %s%n%s", path, e);
   }
}

問題:怎麼能做到先去刪除檔案,再去刪除資料夾? 。 利用的是字串的排序規則,從字串排序規則上講,“D:\data\test1\test2”一定排在“D:\data\test1\test2\test2.log”的前面。所以我們使用“sorted(Comparator.reverseOrder())”把Stream順序顛倒一下,就達到了先刪除檔案,再刪除資料夾的目的。

下面的輸出,是最終執行結果的刪除順序。

刪除檔案成功:D:\data\test1\test2\test3\test4\test5
刪除檔案成功:D:\data\test1\test2\test3\test4
刪除檔案成功:D:\data\test1\test2\test3\test3.log
刪除檔案成功:D:\data\test1\test2\test3
刪除檔案成功:D:\data\test1\test2\test2.log
刪除檔案成功:D:\data\test1\test2

2.3.傳統IO-遞迴遍歷刪除資料夾

傳統的通過遞迴去刪除檔案或資料夾的方法就比較經典了

//傳統IO遞迴刪除
@Test
void testDeleteFileDir7() throws IOException {
   createMoreFiles();
   File file = new File("D:\\data\\test1\\test2");
   deleteDirectoryLegacyIO(file);

}


private void deleteDirectoryLegacyIO(File file) {

   File[] list = file.listFiles();  //無法做到list多層資料夾資料
   if (list != null) {
      for (File temp : list) {     //先去遞迴刪除子資料夾及子檔案
         deleteDirectoryLegacyIO(temp);   //注意這裡是遞迴呼叫
      }
   }

   if (file.delete()) {     //再刪除自己本身的資料夾
      System.out.printf("刪除成功 : %s%n", file);
   } else {
      System.err.printf("刪除失敗 : %s%n", file);
   }
}

需要注意的是:

  • listFiles()方法只能列出資料夾下面的一層檔案或資料夾,不能列出子資料夾及其子檔案。
  • 先去遞迴刪除子資料夾,再去刪除資料夾自己本身

歡迎關注我的部落格,裡面有很多精品合集

  • 本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

相關文章