file.exists()、file.canRead()、file.canWrite()、file.canExecute()はすべてtrueを返しますが、file.delete()はfalseを返します


90

ファイルに何かを書き込んだ後、でファイルを削除しようとしていますFileOutputStream。これは私が書くために使用するコードです:

private void writeContent(File file, String fileContent) {
    FileOutputStream to;
    try {
        to = new FileOutputStream(file);
        to.write(fileContent.getBytes());
        to.flush();
        to.close();
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

ご覧のように、ストリームをフラッシュして閉じますが、削除しようとするとfile.delete()falseが返されます。

私は、ファイルが存在するかどうかを確認するために、削除する前にチェックし、: 、file.exists()、、file.canRead() すべての真を返します。これらのメソッドを呼び出した直後に、私は試してfalseを返します。file.canWrite()file.canExecute()file.delete()

私が間違ったことをしたことがありますか?


例外がキャッチされないことを忘れていました。
ジェニースミス

ファイルが別のプロセスで使用されていませんか?ロックしましたか?deleteOnExit + exitで動作しますか?
instanceof me

どのOSを実行していますか?ファイルを手動で削除できますか?何かがファイルへのオープンハンドルを保持している可能性があります。
akarnokd 2009年

別のプロセスで使用されているかどうかを確認するにはどうすればよいですか?ロックはしていません。ファイルを削除してから適用を続行する必要があるため、deleteOnExitメソッドを使用できません。1つのセッションで別のフォルダーからファイルを保存するために使用される一時フォルダーを空にしようとしているという考えです。適用の[開く]ボタンを押すと、他のファイルのためのスペースを確保するために、フォルダーを空にする必要があることを意味します。そのファイルに書き込むときにその部分をスキップすると、問題なく、削除できます。
ジェニースミス

1
あなたのフラッシュ&クローズをfinallyブロックで包んでもらえますか?私はあなたがこれを実行したことを知っています、そしてそれは機能しません、しかしそれはあなたの質問に明快さを追加します。
bharal 2013

回答:


99

Javaのもう1つのバグ。私はめったにそれらを見つけません、私の10年のキャリアの私の2番目だけ。他の人が述べたように、これは私の解決策です。私はネザーを使用しましたSystem.gc()。しかし、ここでは、私の場合、それは絶対に重要です。変?はい!

finally
{
    try
    {
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
        System.gc();
    }
    catch (IOException e)
    {
        logger.error(e.getMessage());
        e.printStackTrace();
    }
}

7
どのような種類のストリームでも(を実行するだけでnew File(path))開いたわけではありませんが、同じ問題が発生し、動作させるSystem.gc()前に追加delete()しました!
ixM 2013年

1
他のバグは何ですか?
bogdan.mustiata 2013年

私にとっては気にしません。削除できないファイルは、downloadedFromURLファイルのZIPファイルです。良い点は、ダウンロードしたファイルが削除されることです。何か案が?
Andrea_86 2015

私のために働いた。そして、@ andreadi私はコードでFileChannel.mapを使用したことはありません。このバグは解決できないものとしてクローズされたようSystem.gcです。トリックが毎回機能するので、信じられないほどです。
Adam Burley、2015年

単一のgc()は役に立たない可能性がありますSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj

48

うまくいったトリックはかなり奇妙でした。問題は、以前にファイルの内容を読んだことがあるときに使用したことBufferedReaderです。読んだ後、バッファを閉じました。

その間私は切り替えましたが、今はを使用してコンテンツを読んでいますFileInputStream。また、読み終わったらストリームを閉じます。そして今それは働いています。

問題は、これについての説明がないことです。

私は知らないBufferedReaderFileOutputStream互換性がないことが。


4
私はそれがバグであると主張します: java.sun.com/javase/6/docs/api/java/io/… それはそこでメソッドがストリームとそれに関連するすべてのリソースを閉じるべきだと言っています。私は通常、IOUtils.closeQuietlyを使用して、すべてのストリームを逆の順序で閉じます(最後に開かれるものは最初に閉じられるものです)が、過剰になりがちです。
Ravi Wallau、

2
私はこの正確な問題を抱えていて、私は頭がおかしくなっていると思いました。解決策をありがとう!
Electrons_Ahoy

14
JDK 7を搭載した2011年ですが、問題はまだ修正されていません。私はこのスレッドを見つけてうれしい-何が問題だったのかわからなかった...
David

読んで閉じてから削除しようとしたファイルに非常に迷惑な問題がありました。ここで言及したことは何の助けにもなりませんでした。それから1時間後、ファイルが別のユーザーによって作成されていたので、それは単なるファイル権限であることが
わかりました

ちょうど暗闇の中で刺す... finalize()クリーンアップを確実にするために呼び出すことができますか?それとも設定to = null強制ファイナライズとガベージコレクションを参照してください。
jww 2014

19

私はこの簡単なことを試しました、そしてそれは働いているようです。

file.setWritable(true);
file.delete();

わたしにはできる。

これが機能しない場合は、Linuxの場合はsudoを使用し、Windowsの場合は管理者としてJavaアプリケーションを実行してみてください。Javaにファイルのプロパティを変更する権限があることを確認するだけです。


1
この問題について。通常、人々は参照をnullに設定するか、system.gc()を呼び出すことについて話します。しかし、サーバーの再起動後もファイルが削除されませんでした!! しかしfile.setWritable(true); ちょうど働いた..
Deepak Singhal

6

ファイルを削除/名前変更する前に、すべてのリーダーまたはライター(例:BufferedReader/ InputStreamReader/ BufferedWriter)が適切に閉じられていることを確認する必要があります。

ファイルとの間でデータを読み書きしようとすると、ファイルはプロセスによって保持され、プログラムの実行が完了するまで解放されません。プログラムが終了する前に削除/名前変更操作を実行する場合close()は、java.io.*クラスに付属するメソッドを使用する必要があります。


1
これはWindowsにのみ当てはまります。実際のO / Sでは、開いているファイルを削除して名前を変更できます。
Archie

3

Jon Skeetがコメントしたように、ファイルが常に閉じていることを確認するには、finally {...}ブロックでファイルを閉じる必要があります。そして、e.printStackTraceで例外を飲み込む代わりに、例外をキャッチしてメソッドシグネチャに追加しないでください。何らかの理由でそれができない場合は、少なくとも次のようにしてください。

catch(IOException ex) {
    throw new RuntimeException("Error processing file XYZ", ex);
}

さて、質問番号#2:

これを行うとどうなりますか?

...
to.close();
System.out.println("Please delete the file and press <enter> afterwards!");
System.in.read();
...

ファイルを削除できますか?

また、ファイルが閉じられるとフラッシュされます。私はIOUtils.closeQuietly(...)を使用しているので、flushメソッドを使用して、ファイルを閉じる前にファイルの内容が確実に存在するようにします(IOUtils.closeQuietlyは例外をスローしません)。このようなもの:

...
try {
    ...
    to.flush();
} catch(IOException ex) {
    throw new CannotProcessFileException("whatever", ex);
} finally {
    IOUtils.closeQuietly(to);
}

そのため、ファイルの内容がそこにあることがわかります。通常、ファイルの内容が書き込まれることが重要であり、ファイルを閉じることができるかどうかは重要ではないので、ファイルが閉じられたかどうかは関係ありません。あなたのケースでは、重要なことですが、自分でファイルを閉じて例外を処理することをお勧めします。


最初に試しました-finallyブロックで出力ストリームを閉じます。そしてそれはうまくいきませんでした。それ以降に読み込もうとすると、ストリームが閉じられたというメッセージが表示されました。FileInputReaderでファイルを切り替えて読み取るときに、上記の「悪い」ことは何も起こりませんでした。
ジェニースミス

2

このファイルを削除できない理由はありません。誰がこのファイルを保持しているのか調べたいと思います。unix / linuxでは、lsofユーティリティを使用して、ファイルをロックしているプロセスを確認できます。ウィンドウでは、プロセスエクスプローラーを使用できます。

lsofの場合、次のように簡単です。

lsof /path/and/name/of/the/file

プロセスエクスプローラの場合、検索メニューを使用してファイル名を入力すると、ファイルをロックしているプロセスを示すハンドルが表示されます。

ここにあなたがする必要があると私が思うことをするいくつかのコードがあります:

FileOutputStream to;

try {
    String file = "/tmp/will_delete.txt";
    to = new FileOutputStream(file );
    to.write(new String("blah blah").getBytes());
    to.flush();
    to.close();
    File f = new File(file);
    System.out.print(f.delete());
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

OS Xでは問題なく動作します。Windowsではテストしていませんが、Windowsでも動作するはずです。Windowsのwrtファイル処理で予期しない動作が発生することも認めます。


1
Windowsでは、MSからフリープロセスExplorerをダウンロードすることができます:technet.microsoft.com/en-us/sysinternals/bb896653.aspxを
akarnokd

2

Eclipse IDEで作業している場合は、アプリケーションの前回の起動時にファイルを閉じていない可能性があります。ファイルを削除しようとしたときに同じエラーメッセージが表示されたのは、そのためです。Eclipse IDEは、アプリケーションの終了後にすべてのファイルを閉じないようです。


1

うまくいけば、これが役立ちます。私のJavaコードが他のフォルダーにコンテンツのコピーを作成した後、ファイルを削除できないという同様の問題に遭遇しました。広範囲にわたるグーグルの後、私はすべての単一ファイル操作関連変数を明示的に宣言し、各ファイル操作オブジェクトのclose()メソッドを呼び出し、それらをNULLに設定しました。次に、System.gc()と呼ばれる関数があり、ファイルI / Oマッピングをクリアします(Webサイトで何が与えられているかはわかりません)。

これが私のコード例です:

public void start() {
    File f = new File(this.archivePath + "\\" + this.currentFile.getName());
    this.Copy(this.currentFile, f);

    if(!this.currentFile.canWrite()){
        System.out.println("Write protected file " +
           this.currentFile.getAbsolutePath());

        return;
    }


    boolean ok = this.currentFile.delete();
    if(ok == false){
        System.out.println("Failed to remove " + this.currentFile.getAbsolutePath());
        return;
    }
}

private void Copy(File source, File dest) throws IOException {
    FileInputStream fin;
    FileOutputStream fout;
    FileChannel cin = null, cout = null;
    try {
        fin = new FileInputStream(source);
        cin = fin.getChannel();
        fout = new FileOutputStream(dest);
        cout = fout.getChannel();

        long size = cin.size();
        MappedByteBuffer buf = cin.map(FileChannel.MapMode.READ_ONLY, 0, size);

        cout.write(buf);
        buf.clear();
        buf = null;

        cin.close();
        cin = null;

        fin.close();
        fin = null;

        cout.close();
        cout = null;

        fout.close();
        fout = null;

        System.gc();

    } catch (Exception e){
        this.message = e.getMessage();
        e.printStackTrace();
    }
}

1

答えは、ファイルをロードするときに、コードのどの行でも「閉じる」メソッドを適用する必要があります。


0

rubyにかつて問題がありましたが、ウィンドウ内のファイルには、ファイルを書き込んで閉じた後に実際に向きを変えて再度読み取ることができるように「fsync」が必要でした。多分これは似たような症状です(もしそうなら、本当にWindowsのバグだと思います)。


0

ここにリストされている解決策はどれも私の状況では機能しませんでした。私の解決策は、安全のために5秒(構成可能)の制限を設けて、whileループを使用してファイルを削除しようとすることでした。

File f = new File("/path/to/file");

int limit = 20; //Only try for 5 seconds, for safety
while(!f.delete() && limit > 0){
    synchronized(this){
        try {
            this.wait(250); //Wait for 250 milliseconds
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    limit--;
}

上記のループの使用は、ガベージコレクションを手動で実行したり、ストリームをnullに設定したりすることなく機能しました。


あなたは何を使っていますか?STREAMSにアクセスできる場合は、それらを閉じてください。しかし、ファイルが「すでに」ロックされている場合は、問題があります。
marcolopes 2013

1
-1コードをループしてファイルを削除するのは、良い考えではありません。機能するgc()オプションを使用しないのはなぜですか?
bharal 2013

@bharal通知私は、他の解決策(gc()を含む)が私の特定のケースで機能しなかったことを上に述べました。whileループの使用は理想的ではありませんが、状況によっては適切な解決策になる場合があります。上記のコードに、タイムアウトや最大反復回数などの追加のチェックを追加することは、whileループをより安全にする良い方法です。
etech 2013

@etechただし、ファイルが存在しない場合は、無限ループが作成されています。
bharal

0

問題は、ファイルがプログラムによって開かれてロックされていると見なされることです。または、それが開かれたプログラムのコンポーネントである可能性があるため、dispose()メソッドを使用してその問題を解決する必要があります。すなわちJFrame frame; .... frame.dispose();


0

すべてのストリームを閉じるか、try-with-resourceブロックを使用する必要があります

static public String head(File file) throws FileNotFoundException, UnsupportedEncodingException, IOException
{
    final String readLine;
    try (FileInputStream fis = new FileInputStream(file);
            InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
            LineNumberReader lnr = new LineNumberReader(isr))
    {
        readLine = lnr.readLine();
    }
    return readLine;
}

0

file.delete()がfalseを送信している場合、ほとんどの場合、Bufferedreaderハンドルは閉じられません。ちょうど近くにあり、それは私のために正常に動作するようです。


0

Windowsでも同じ問題が発生しました。私は1行ずつスカラでファイルを読んでいました

Source.fromFile(path).getLines()

今私はそれを全体として読みます

import org.apache.commons.io.FileUtils._

// encoding is null for platform default
val content=readFileToString(new File(path),null.asInstanceOf[String])

読んだ後、ファイルを正しく閉じます

new File(path).delete

動作します。


0

Eclipse / NetBeansの場合

IDEを再起動してコードを再度実行します。これは、1時間の苦労の後の私にとってはトリックワークです。

これが私のコードです:

File file = new File("file-path");
if(file.exists()){
  if(file.delete()){
     System.out.println("Delete");
  }
  else{

       System.out.println("not delete");
  }
}

出力:

削除する


0

これが発生する可能性のあるもう1つのケース:a URLを介してJARファイルを読み書きし、後で同じJVMセッション内で同じファイルを削除しようとした場合。

File f = new File("/tmp/foo.jar");
URL j = f.toURI().toURL();

URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
URLConnection c = u.openConnection();

// open a Jar entry in auto-closing manner
try (InputStream i = c.getInputStream()) {

    // just read some stuff; for demonstration purposes only
    byte[] first16 = new byte[16];
    i.read(first16);
    System.out.println(new String(first16));
}

// ...

// i is now closed, so we should be good to delete the jar; but...
System.out.println(f.delete());     // says false!

理由は、Javaの内部JARファイル処理ロジックがJarFileエントリをキャッシュする傾向があるためです。

// inner class of `JarURLConnection` that wraps the actual stream returned by `getInputStream()`

class JarURLInputStream extends FilterInputStream {
    JarURLInputStream(InputStream var2) {
        super(var2);
    }

    public void close() throws IOException {
        try {
            super.close();
        } finally {

            // if `getUseCaches()` is set, `jarFile` won't get closed!

            if (!JarURLConnection.this.getUseCaches()) {
                JarURLConnection.this.jarFile.close();
            }
        }
    }
}

そして、それぞれJarFile(むしろ、基礎となるZipFile構造)は、構築時からclose()呼び出されるまで、ファイルへのハンドルを保持します。

public ZipFile(File file, int mode, Charset charset) throws IOException {
    // ...

    jzfile = open(name, mode, file.lastModified(), usemmap);

    // ...
}

// ...

private static native long open(String name, int mode, long lastModified,
                                boolean usemmap) throws IOException;

このNetBeansの問題については、適切な説明があります。


どうやらこれを「修正」する方法は2つあります:

  • 現在のURLConnection、またはURLConnection現在のJVMセッションの(グローバルに)すべてののJARファイルのキャッシュを無効にすることができます。

    URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
    URLConnection c = u.openConnection();
    
    // for only c
    c.setUseCaches(false);
    
    // globally; for some reason this method is not static,
    // so we still need to access it through a URLConnection instance :(
    c.setDefaultUseCaches(false);
  • [ハック警告!]JarFileキャッシュを使い終わったら、キャッシュから手動で削除できます。キャッシュマネージャーsun.net.www.protocol.jar.JarFileFactoryはパッケージプライベートですが、いくつかのリフレクションマジックで作業を完了できます。

    class JarBridge {
    
        static void closeJar(URL url) throws Exception {
    
            // JarFileFactory jarFactory = JarFileFactory.getInstance();
            Class<?> jarFactoryClazz = Class.forName("sun.net.www.protocol.jar.JarFileFactory");
            Method getInstance = jarFactoryClazz.getMethod("getInstance");
            getInstance.setAccessible(true);
            Object jarFactory = getInstance.invoke(jarFactoryClazz);
    
            // JarFile jarFile = jarFactory.get(url);
            Method get = jarFactoryClazz.getMethod("get", URL.class);
            get.setAccessible(true);
            Object jarFile = get.invoke(jarFactory, url);
    
            // jarFactory.close(jarFile);
            Method close = jarFactoryClazz.getMethod("close", JarFile.class);
            close.setAccessible(true);
            //noinspection JavaReflectionInvocation
            close.invoke(jarFactory, jarFile);
    
            // jarFile.close();
            ((JarFile) jarFile).close();
        }
    }
    
    // and in your code:
    
    // i is now closed, so we should be good to delete the jar
    JarBridge.closeJar(j);
    System.out.println(f.delete());     // says true, phew.

注意:これらはすべてJava 8コードベース(1.8.0_144)に基づいています。他の/以降のバージョンでは動作しない場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.