มีวิธีการลบไดเรกทอรีทั้งหมดซ้ำใน Java?
ในกรณีปกติเป็นไปได้ที่จะลบไดเรกทอรีว่าง อย่างไรก็ตามเมื่อมันมาถึงการลบไดเรกทอรีทั้งหมดที่มีเนื้อหามันไม่ง่ายอีกต่อไป
คุณจะลบไดเรกทอรีทั้งหมดที่มีเนื้อหาใน Java ได้อย่างไร
มีวิธีการลบไดเรกทอรีทั้งหมดซ้ำใน Java?
ในกรณีปกติเป็นไปได้ที่จะลบไดเรกทอรีว่าง อย่างไรก็ตามเมื่อมันมาถึงการลบไดเรกทอรีทั้งหมดที่มีเนื้อหามันไม่ง่ายอีกต่อไป
คุณจะลบไดเรกทอรีทั้งหมดที่มีเนื้อหาใน Java ได้อย่างไร
คำตอบ:
คุณควรตรวจสอบของ Apache Commons-io มีคลาสFileUtilsที่จะทำสิ่งที่คุณต้องการ
FileUtils.deleteDirectory(new File("directory"));
ด้วย 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);
}
foo
มีลิงก์foo/link
เช่นนั้นการlink->/
โทรdelete(new File(foo))
จะลบระบบไฟล์ของคุณมากเท่ากับที่ผู้ใช้ของคุณอนุญาต !!
ใน 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;
}
});
super.postVisitDirectory(dir, exc);
ของคุณpostVisitDirectory
เพื่อระเบิดถ้าการเดินไม่สามารถแสดงรายการไดเรกทอรี
โซลูชัน 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);
.sorted(Comparator.reverseOrder())
ข้อเสนอแนะComparator::reverseOrder
ไม่ได้ทำงาน ดู: stackoverflow.com/questions/43036611/…
.sorted((f1, f2) -> f2.compareTo(f1))
เปรียบเทียบf2
กับf1
แทนด้วยf1
f2
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กระบวนการเป็นแบบส่วนตัว แต่พฤติกรรมของมันควรจะชัดเจน)
เพิ่งเห็นวิธีการแก้ปัญหาของฉันจะมากหรือน้อยเหมือน 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();
}
}
โซลูชันที่มีสแต็กและไม่มีเมธอดแบบเรียกซ้ำ:
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();
}
}
list*
java.io.File
จาก Javadocs: "ส่งคืนค่า null หากชื่อพา ธ นามธรรมนี้ไม่แสดงถึงไดเรกทอรีหรือหากเกิดข้อผิดพลาด I / O" ดังนั้น: if (currList.length > 0) {
กลายเป็นif (null != currList && currList.length > 0) {
หากคุณมี Spring คุณสามารถใช้FileSystemUtils.deleteRecursively :
import org.springframework.util.FileSystemUtils;
boolean success = FileSystemUtils.deleteRecursively(new File("directory"));
ฝรั่งได้Files.deleteRecursively(File)
รับการสนับสนุนจนฝรั่ง 9
จากGuava 10 :
เลิก วิธีนี้ทนทุกข์ทรมานจากการตรวจจับ symlink ที่ไม่ดีและสภาวะการแข่งขัน ฟังก์ชั่นนี้ได้รับการสนับสนุนอย่างเหมาะสมโดยเฉพาะปลอกกระสุนออกไปยังคำสั่งระบบปฏิบัติการเช่นหรือ
rm -rf
วิธีการนี้มีกำหนดจะถูกลบออกจาก Guava ใน Guava รีลีส 11.0del /s
ดังนั้นไม่มีวิธีการดังกล่าวในฝรั่ง 11
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) { /* ... */ }
});
Files.walk(path).iterator().toSeq.reverse.foreach(Files.delete)
walk
วิธีแล้วรับประกันการข้ามผ่านความลึกแรก
Collections.reverseOrder()
ดังนั้นรหัสของคุณจะ for (Path p : Files.walk(directoryToDelete).sorted(reverseOrder()).toArray(Path[]::new))
ถือว่าเป็นการนำเข้าแบบคงที่
Comparator.reverseOrder
อะไร? Files.walk(dir) .sorted(Comparator.reverseOrder()) .toArray(Path[]::new))
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();
}
}
f.delete()
ล่างdeleteDirectory(f)
จะพ่น NoSuchFileException เนื่องจากdeleteDirectory(f)
ไฟล์นั้นลบไปแล้ว ไดเรกทอรีทุกคนจะกลายเป็นเส้นทางเมื่อผ่านและถูกลบโดยdeleteDirectory(f)
path.delete()
ดังนั้นเราไม่ต้องการf.delete()
ในif f.isDerectory
ส่วน ดังนั้นเพียงแค่ลบf.delete();
ภายใต้ deleteDirectory (f) และมันจะทำงาน
สองวิธีล้มเหลวด้วย symlinks และโค้ดด้านบน ... และไม่ทราบวิธีแก้ปัญหา
เรียกใช้สิ่งนี้เพื่อสร้างการทดสอบ:
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
เรียกใช้สิ่งนี้เพื่อสร้างการทดสอบ:
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:
คุณสามารถใช้:
org.apache.commons.io.FileUtils.deleteQuietly(destFile);
ลบไฟล์ห้ามทิ้งข้อยกเว้น หากไฟล์เป็นไดเรกทอรีให้ลบมันและไดเรกทอรีย่อยทั้งหมด ความแตกต่างระหว่าง File.delete () และวิธีนี้คือ: ไดเรกทอรีที่จะลบไม่จำเป็นต้องว่างเปล่า จะไม่มีข้อยกเว้นเกิดขึ้นเมื่อไฟล์หรือไดเรกทอรีไม่สามารถลบได้
ทางออกที่ดีที่สุดที่จัดการข้อยกเว้นอย่างสอดคล้องกับวิธีการที่ข้อยกเว้นที่เกิดขึ้นจากวิธีการควรอธิบายถึงวิธีการที่พยายาม (และล้มเหลว) ในการทำ:
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);
}
}
ในโครงการดั้งเดิมฉันต้องสร้างรหัส 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());
}
}
รหัสด้านล่างซ้ำลบเนื้อหาทั้งหมดในโฟลเดอร์ที่กำหนด
boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}
นี่คือวิธีการหลักของกระดูกเปลือยที่ยอมรับอาร์กิวเมนต์ของบรรทัดคำสั่งคุณอาจต้องผนวกการตรวจสอบข้อผิดพลาดของคุณเองหรือทำการขึ้นรูปตามที่คุณเห็นว่าเหมาะสม
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();
}
}
}
ฉันหวังว่าจะช่วย!
บางทีวิธีแก้ปัญหาสำหรับปัญหานี้อาจเป็นการปรับใช้วิธีลบของคลาสไฟล์โดยใช้รหัสจากคำตอบของ 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();
}
}
}
ไม่มีคอมมอนส์ 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();
}
ในขณะที่ไฟล์สามารถลบได้อย่างง่ายดายโดยใช้ 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();
}
}
}
}
}
}
ฉันเขียนกิจวัตรประจำวันนี้ที่มี 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);
}
}
สมมติว่าเป็นตัวอย่าง
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());
}
}
สำหรับข้อมูลเพิ่มเติมอ้างอิงทรัพยากรด้านล่าง
rm -rf
ถูกมากมากขึ้น performant FileUtils.deleteDirectory
กว่าหลังจากการเปรียบเทียบที่กว้างขวางเราพบว่าการใช้ถูกหลายครั้งเร็วกว่าการใช้rm -rf
FileUtils.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;
}
น่าลองถ้าคุณจัดการกับไดเรกทอรีขนาดใหญ่หรือซับซ้อน
ฝรั่งMoreFiles.deleteRecursively()
ให้หนึ่งซับ:
ซึ่งแตกต่างจากตัวอย่างจำนวนมากที่ใช้ร่วมกันมันทำหน้าที่เป็นลิงก์สัญลักษณ์และจะไม่ลบไฟล์ออกนอกเส้นทางที่ระบุ