web-dev-qa-db-ja.com

java.nio.file.Files.delete(Path path)-SimpleFileVisitorを使用してディレクトリを再帰的に削除できない場合があります

Javaでディレクトリを再帰的に削除する から取得した再帰的削除メソッドで時々_Java.nio.file.DirectoryNotEmptyException_のトラブルシューティングを試みています

コード(@ TrevorRobinsonへのクレジット):

_static void removeRecursive(Path path) throws IOException {
    Files.walkFileTree(path, new SimpleFileVisitor<Path>() {

        final Logger logger = LoggerFactory.getLogger(this.getClass());
        @Override
        public FileVisitResult visitFile(Path file,
                BasicFileAttributes attrs) throws IOException {
            logger.warn("Deleting " + file.getFileName());
            Files.delete(file);
            logger.warn("DELETED " + file.getFileName());
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) {
            // try to delete the file anyway, even if its attributes could
            // not be read, since delete-only access is theoretically possible
            // I NEVER SEE THIS
            logger.warn("Delete file " + file + " failed", exc);
            try {
                Files.delete(file);
            } catch (IOException e) {
                logger.warn(
                    "Delete file " + file + " failed again", exc);
            }
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                throws IOException {
            if (exc == null) {
                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }
            // directory iteration failed; propagate exception
            throw exc;
        }
    });
}
_

Call

_try {
    removeRecursive(Paths.get(unzipDirPath));
} catch (IOException e) {
    String msg = "Failed to delete folder " + unzipDirPath;
    if (e instanceof Java.nio.file.DirectoryNotEmptyException) {
        msg += ". Still contains : ";
        final File[] listFiles = Paths.get(unzipDirPath).toFile().listFiles();
        if (listFiles != null) for (File file : listFiles) {
            msg += file.getAbsolutePath() + "\n";
        }
    }
    log.error(msg, e);
}
_

Prints(20/40回の反復に1回):

_22:03:34.190 [http-bio-8080-exec-47] WARN  g.u.d.m.server.servlets.Controller$1 - Deleting batt
22:03:34.192 [http-bio-8080-exec-47] WARN  g.u.d.m.server.servlets.Controller$1 - DELETED batt
22:03:34.192 [http-bio-8080-exec-47] WARN  g.u.d.m.server.servlets.Controller$1 - Deleting wifi
22:03:34.193 [http-bio-8080-exec-47] WARN  g.u.d.m.server.servlets.Controller$1 - DELETED wifi
22:03:34.196 [http-bio-8080-exec-47] ERROR g.u.d.m.s.s.DataCollectionServlet - Failed to delete folder C:\yada\. Still contains : C:\yada\dir\wifi

Java.nio.file.DirectoryNotEmptyException: C:\yada\dir
    at Sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.Java:265) ~[na:1.7.0_45]
    at Sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.Java:103) ~[na:1.7.0_45]
    at Java.nio.file.Files.delete(Files.Java:1077) ~[na:1.7.0_45]
    at gr.uoa.di.monitoring.server.servlets.Controller$1.postVisitDirectory(Controller.Java:128) ~[Controller$1.class:na]
    at gr.uoa.di.monitoring.server.servlets.Controller$1.postVisitDirectory(Controller.Java:1) ~[Controller$1.class:na]
    at Java.nio.file.FileTreeWalker.walk(FileTreeWalker.Java:224) ~[na:1.7.0_45]
    at Java.nio.file.FileTreeWalker.walk(FileTreeWalker.Java:199) ~[na:1.7.0_45]
    at Java.nio.file.FileTreeWalker.walk(FileTreeWalker.Java:69) ~[na:1.7.0_45]
    at Java.nio.file.Files.walkFileTree(Files.Java:2600) ~[na:1.7.0_45]
    at Java.nio.file.Files.walkFileTree(Files.Java:2633) ~[na:1.7.0_45]
    at gr.uoa.di.monitoring.server.servlets.Controller.removeRecursive(Controller.Java:96) ~[Controller.class:na]
    at gr.uoa.di.monitoring.server.servlets.DataCollectionServlet.doPost(DataCollectionServlet.Java:153) ~[DataCollectionServlet.class:na]
    at javax.servlet.http.HttpServlet.service(HttpServlet.Java:641) [servlet-api.jar:na]
    at javax.servlet.http.HttpServlet.service(HttpServlet.Java:722) [servlet-api.jar:na]
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:305) [catalina.jar:7.0.32]
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:210) [catalina.jar:7.0.32]
    at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:222) [catalina.jar:7.0.32]
    at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:123) [catalina.jar:7.0.32]
    at org.Apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.Java:472) [catalina.jar:7.0.32]
    at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:168) [catalina.jar:7.0.32]
    at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:99) [catalina.jar:7.0.32]
    at org.Apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.Java:929) [catalina.jar:7.0.32]
    at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:118) [catalina.jar:7.0.32]
    at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:407) [catalina.jar:7.0.32]
    at org.Apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.Java:1002) [Tomcat-coyote.jar:7.0.32]
    at org.Apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.Java:585) [Tomcat-coyote.jar:7.0.32]
    at org.Apache.Tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.Java:310) [Tomcat-coyote.jar:7.0.32]
    at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1145) [na:1.7.0_45]
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:615) [na:1.7.0_45]
    at Java.lang.Thread.run(Thread.Java:744) [na:1.7.0_45]
_

wifiが削除されたと報告されていることに注意してください。さらに奇妙なのは、次のようになることです。

フォルダC:\ yadaの削除に失敗しました。まだ含まれています:C:\ yada\dir

Java.nio.file.DirectoryNotEmptyException:C:\ yada\dir

私は時々削除に時間がかかりすぎるという結論に向かう傾向があります-言い換えれば、問題はJava.nio.file.Files.delete(Path path)がブロックされないことです(したがって、C:\ yada\dirには、時が来てもファイルが含まれていることがあり、削除されることもあります私がそれを述べる時までに)。では、これをどのように回避するのですか?

また:スローするにはJava.nio.file.Files.delete(Path path)が必要ですか? docs 状態:

一部のオペレーティングシステムでは、ファイルが開いていて、このJava仮想マシンまたは他のプログラムによって使用されている場合、ファイルを削除できない場合があります。

この場合、例外をスローする必要はないようです。 投げるにはJava.nio.file.Files.delete(Path path)が必要ですか

16
Mr_and_Mrs_D

私は同じ問題を抱えていました、そしてそれは私がものを削除していた同じディレクトリのコードのどこかに閉じられていないディレクトリファイルストリームによって引き起こされたことが判明しました。返されるストリームオブジェクト:

Files.list(Path)

閉じる必要があるため、そのメソッドを使用する場合は、コードでtry-with-resources構造を使用してください。

ですから、削除に時間がかかりすぎるとは思いません。運が悪かったので、ディレクトリの削除を再試行する前に、しばらく待ってみました。自分のプログラムでそのリソースがロックされている可能性があります。その結果、正常に戻っても削除呼び出しは完了しません(Windowsは、独自のプログラムがファイルを解放すると、最終的にファイルを削除するようです)が、もちろん、含まれているディレクトリはまだ削除されていないため、削除できません。空の。

13
user3485962

これは非常に古いスレッドであることはわかっていますが、同じ問題が発生し、修正するのにかなりの時間がかかりました。この誤動作はタイミングの問題(Windowsでのみ発生するように見えます)が原因であると思うので、postVisitDirectoryメソッドを一時停止します。それはうまくいきました、そしてこれは私が最終的に思いついたものです:

DirectoryNotEmptyExceptionをスローせずに削除を行うメソッド:

private boolean isDeleted(Path dir) throws IOException {
    boolean deleted = false;
    try {
        Files.delete(dir);
        deleted = true;
    } catch (DirectoryNotEmptyException e) {
    // happens sometimes if Windows is too slow to remove children of a directory
    deleted = false;
    }
    return deleted;
}

ループでの使用:

public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
    if (e == null) {
        int maxTries = 5;
        int count = 0;
        boolean deleted = false;
        do {
            if ((deleted = this.isDeleted(dir))) {
                break;
            } else {
                // wait a bit and try again
                count++;
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e1) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        } while (count < maxTries);
        // gone?
        if (!deleted) {
            throw new DirectoryNotEmptyException(dir.toString());
        }
        // go ahead
        return FileVisitResult.CONTINUE;
    }
    throw e;
}

アンディ

3
andy

@ user3485962の回答にコメントしたと思いますが、十分なポイントがありません。

リストの作成後にストリームを閉じるリソースでtryを使用する例を次に示します。

    /**  
     * @param dir The directory to list.
     * @return A list of files and directories in dir.
     * @throws IOException If encountered.
     */
    public static List<Path> getList(Path dir) throws IOException {
        try (Stream<Path> s = Files.list(dir)) {
            return s.collect(Collectors.toList());
        }
    }
0
Andy Turner

現在反復しているファイルの名前を一時的に変数に格納し、DirectoryNotEmptyExceptionでtry-catchを実行できます。この例外が発生した場合は、それをキャッチして、ファイルを指定する独自の例外をスローします。

_try {
 Files.delete(file);
} catch (DirectoryNotEmptyException  e) {
 throw new MySpecificException(file.getFileName());
}

class MySpecificException extends Exception {
 public MySpecificException() { }

 public MySpecificException(string filename) {
  super(filename);
 }  
}
_

ファイル名の取得はe.getMessage();で行うことができます

Files.delete()は、削除できないファイルが見つかった場合でも、ディレクトリ内のファイルを削除し続けると思います。この場合でも、私の方法は機能します。ディレクトリのファイル名ではなく、ディレクトリ内のすべてのファイルのリストを返すだけです。削除できないファイルのみが含まれている必要があり、それでもソリューションはあります。

0
Jeroen Vannevel