file.delete () gibt false zurück, obwohl file.exists (), file.canRead (), file.canWrite () und file.canExecute () alle true zurückgeben

90

Ich versuche eine Datei zu löschen, nachdem ich etwas darin geschrieben habe, mit FileOutputStream. Dies ist der Code, den ich zum Schreiben verwende:

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

Wie zu sehen ist, spüle und schließe ich den Stream, aber wenn ich versuche zu löschen, wird file.delete()false zurückgegeben.

Ich habe vor dem Löschen zu sehen , ob die Datei vorhanden ist , und: file.exists(), file.canRead(), file.canWrite(), file.canExecute()alle return true. Kurz nachdem ich diese Methoden aufgerufen habe, versuche ich, file.delete()false zurückzugeben.

Gibt es etwas, was ich falsch gemacht habe?

Jenny Smith
quelle
Ich habe vergessen zu erwähnen, dass keine Ausnahme gefangen wird.
Jenny Smith
Sind Sie sicher, dass die Datei nicht von einem anderen Prozess verwendet wird? Hast du es gesperrt? Funktioniert es mit dem Beenden von deleteOnExit +?
Instanz von mir
Auf welchem ​​Betriebssystem laufen Sie? Können Sie die Datei manuell löschen? Etwas könnte ein offenes Handle für die Datei enthalten.
Akarnokd
Wie kann ich herausfinden, ob es von einem anderen Prozess verwendet wird? Ich habe es nicht abgeschlossen. Ich kann die deleteOnExit-Methode nicht verwenden, da ich die Datei löschen und danach meine Anwendung fortsetzen muss. Die Idee ist, dass ich versuche, einen temporären Ordner zu leeren, in dem Dateien aus einem anderen Ordner für eine Sitzung gespeichert werden. Wenn ich bei meiner Bewerbung auf die Schaltfläche "Öffnen" drücke, muss der Ordner geleert werden, um Platz für einige andere Dateien zu schaffen. Wenn ich den Teil beim Schreiben in diese Datei überspringe, ist dies in Ordnung und ich kann ihn löschen.
Jenny Smith
1
Können Sie bitte Ihren Flush & Close in einen Endblock einwickeln? Ich weiß, dass Sie dies getan haben und es nicht funktioniert, aber es wird Ihrer Frage Klarheit verleihen.
Bharal

Antworten:

99

Ein weiterer Fehler in Java. Ich finde sie selten, nur meine zweite in meiner 10-jährigen Karriere. Dies ist meine Lösung, wie andere erwähnt haben. Ich habe Nether benutzt System.gc(). Aber hier ist es in meinem Fall absolut entscheidend. Seltsam? JA!

finally
{
    try
    {
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
        System.gc();
    }
    catch (IOException e)
    {
        logger.error(e.getMessage());
        e.printStackTrace();
    }
}
Da Martin
quelle
7
Obwohl ich es nicht mit irgendeiner Art von Stream geöffnet habe (nur a new File(path)), bin ich auf das gleiche Problem gestoßen und habe es hinzugefügt, System.gc()bevor delete()es funktioniert hat!
ixM
1
Was ist der andere Fehler?
Bogdan.mustiata
wotk nicht für mich. Die Datei, die ich nicht löschen kann, ist eine ZIP-Datei einer heruntergeladenen FromURL-Datei. Das Schöne ist, dass die heruntergeladene Datei gelöscht wird. Irgendeine Idee?
Andrea_86
Hat für mich gearbeitet. Und @andreadi Ich habe FileChannel.map nie in meinem Code verwendet. Scheint, dass dieser Fehler als unlösbar geschlossen wurde, was mich ungläubig macht, weil der System.gcTrick jedes Mal funktioniert.
Adam Burley
Single gc () könnte nicht helfenSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
Notizen-jj
48

Es war ziemlich seltsam, welcher Trick funktionierte. Die Sache ist, wenn ich zuvor den Inhalt der Datei gelesen habe, die ich verwendet habeBufferedReader . Nach dem Lesen schloss ich den Puffer.

Inzwischen habe ich gewechselt und jetzt lese ich den Inhalt mit FileInputStream . Auch nach dem Lesen schließe ich den Stream. Und jetzt funktioniert es.

Das Problem ist, dass ich keine Erklärung dafür habe.

Ich weiß es nicht BufferedReader undFileOutputStream inkompatibel.

Jenny Smith
quelle
4
Ich würde dann argumentieren, dass es ein Fehler ist: java.sun.com/javase/6/docs/api/java/io/… Dort steht, dass die Methode den Stream und alle damit verbundenen Ressourcen schließen sollte. Normalerweise schließe ich gerne alle Streams in der invertierten Reihenfolge (die letzten, die geöffnet sind, sind die ersten, die geschlossen werden) mit IOUtils.closeQuietly, aber es ist in der Regel übertrieben.
Ravi Wallau
2
Ich hatte genau dieses Problem und dachte, ich würde verrückt. Danke für die Lösung!
Electrons_Ahoy
14
Es ist 2011 mit JDK 7 und das Problem ist immer noch nicht behoben. Ich bin so froh, dass ich diesen Thread gefunden habe - ich konnte einfach nicht herausfinden, was los war ...
David
Ich hatte ein sehr nerviges Problem mit einer Datei, die ich gelesen und geschlossen und dann versucht habe zu löschen. Nichts, was hier erwähnt wurde, half. Dann nach einer Stunde stellte ich fest, dass es nur die Dateirechte waren, da die Datei von einem anderen Benutzer erstellt worden war :-D
runholen
Nur ein Stich in die Dunkelheit ... Können Sie anrufen finalize(), um die Reinigung zu gewährleisten? Oder vielleicht eingestellt to = null? Siehe Erzwingen der Finalisierung und Speicherbereinigung .
JWW
19

Ich habe diese einfache Sache ausprobiert und es scheint zu funktionieren.

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

Für mich geht das.

Wenn dies nicht funktioniert, versuchen Sie, Ihre Java-Anwendung unter sudo unter Linux und unter Windows als Administrator auszuführen. Nur um sicherzustellen, dass Java Rechte zum Ändern der Dateieigenschaften hat.

Kislay Sinha
quelle
1
Für dieses Problem; Im Allgemeinen wird davon gesprochen, Referenzen als null festzulegen oder system.gc () aufzurufen. Aber für mich wurden auch nach dem Neustart des Servers keine Dateien gelöscht !! Aber file.setWritable (true); hat gerade funktioniert ..
Deepak Singhal
5

Bevor Sie versuchen , eine Datei zu löschen / umbenennen, müssen Sie sicherstellen , dass alle Leser oder Autoren (zB: BufferedReader/ InputStreamReader/ BufferedWriter) richtig geschlossen.

Wenn Sie versuchen, Ihre Daten aus / in eine Datei zu lesen / schreiben, wird die Datei vom Prozess gehalten und erst freigegeben, wenn die Programmausführung abgeschlossen ist. Wenn Sie die Lösch- / Umbenennungsvorgänge ausführen möchten, bevor das Programm endet, müssen Sie die close()mit den java.io.*Klassen gelieferte Methode verwenden.

Sai Manoj
quelle
Dies gilt nur unter Windows. Auf jedem echten Betriebssystem können Sie geöffnete Dateien löschen und umbenennen.
Archie
3

Wie Jon Skeet kommentierte, sollten Sie Ihre Datei im Block finally ... schließen, um sicherzustellen, dass sie immer geschlossen ist. Und anstatt die Ausnahmen mit e.printStackTrace zu verschlucken, fangen Sie die Ausnahme einfach nicht ab und fügen Sie sie der Methodensignatur hinzu. Wenn Sie aus irgendeinem Grund nicht können, tun Sie zumindest Folgendes:

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

Nun Frage Nr. 2:

Was ist, wenn Sie dies tun:

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

Könnten Sie die Datei löschen?

Außerdem werden Dateien gelöscht, wenn sie geschlossen werden. Ich verwende IOUtils.closeQuietly (...), daher verwende ich die Flush-Methode, um sicherzustellen, dass der Inhalt der Datei vorhanden ist, bevor ich versuche, sie zu schließen (IOUtils.closeQuietly löst keine Ausnahmen aus). Etwas wie das:

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

Ich weiß also, dass der Inhalt der Datei dort ist. Da es mir normalerweise wichtig ist, dass der Inhalt der Datei geschrieben wird und nicht, ob die Datei geschlossen werden kann oder nicht, spielt es keine Rolle, ob die Datei geschlossen wurde oder nicht. In Ihrem Fall würde ich empfehlen, die Datei selbst zu schließen und Ausnahmen entsprechend zu behandeln.

Ravi Wallau
quelle
Ich habe als erstes versucht - den Ausgabestream im finally-Block zu schließen. Und es hat nicht funktioniert. Was passiert ist, wenn ich danach versucht habe zu lesen, ist, dass ich eine Nachricht erhalten habe, dass der Stream geschlossen wurde. Beim Wechseln und Lesen der Datei mit FileInputReader ist keines der oben beschriebenen "schlechten" Ereignisse aufgetreten.
Jenny Smith
2

Es gibt keinen Grund, warum Sie diese Datei nicht löschen können sollten. Ich würde schauen, wer diese Datei im Griff hat. Unter Unix / Linux können Sie mit dem Dienstprogramm lsof überprüfen, welcher Prozess die Datei sperrt. In Windows können Sie den Prozess-Explorer verwenden.

für lsof ist es so einfach wie zu sagen:

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

Für den Prozess-Explorer können Sie das Suchmenü verwenden und den Dateinamen eingeben, um das Handle anzuzeigen, das Sie auf den Prozess verweist, der die Datei sperrt.

Hier ist ein Code, der genau das tut, was Sie meiner Meinung nach tun müssen:

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

Es funktioniert gut unter OS X. Ich habe es nicht unter Windows getestet, aber ich vermute, dass es auch unter Windows funktionieren sollte. Ich gebe auch zu, dass bei der Verarbeitung von Windows-Wrt-Dateien ein unerwartetes Verhalten auftritt.

neesh
quelle
1
Für Windows können Sie den kostenlosen Process Explorer von MS herunterladen: technet.microsoft.com/en-us/sysinternals/bb896653.aspx
akarnokd
2

Wenn Sie in Eclipse IDE arbeiten, kann dies bedeuten, dass Sie die Datei beim vorherigen Start der Anwendung nicht geschlossen haben. Wenn ich beim Versuch, eine Datei zu löschen, dieselbe Fehlermeldung hatte, war dies der Grund. Es scheint, dass Eclipse IDE nach Beendigung einer Anwendung nicht alle Dateien schließt.

Gangnus
quelle
1

Hoffentlich hilft das. Ich stieß auf ein ähnliches Problem, bei dem ich meine Datei nicht löschen konnte, nachdem mein Java-Code eine Kopie des Inhalts in den anderen Ordner erstellt hatte. Nach ausgiebigem googeln habe ich explizit jede einzelne Dateioperationsvariable deklariert und die close () -Methode jedes Dateioperationsobjekts aufgerufen und auf NULL gesetzt. Dann gibt es eine Funktion namens System.gc (), die die Datei-E / A-Zuordnung aufräumt (ich bin nicht sicher, ich sage nur, was auf den Websites angegeben ist).

Hier ist mein Beispielcode:

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

Die Antwort ist, wenn Sie die Datei laden, müssen Sie die "Schließen" -Methode anwenden, in jeder Codezeile, die für mich funktioniert

user3200921
quelle
0

Es gab einmal ein Problem in Ruby, bei dem Dateien in Windows ein "fsync" benötigten, um die Datei nach dem Schreiben und Schließen tatsächlich umdrehen und erneut lesen zu können. Vielleicht ist dies eine ähnliche Manifestation (und wenn ja, denke ich wirklich, ein Windows-Fehler).

Rogerdpack
quelle
0

Keine der hier aufgeführten Lösungen hat in meiner Situation funktioniert. Meine Lösung bestand darin, eine while-Schleife zu verwenden, um die Datei mit einer (konfigurierbaren) 5-Sekunden-Sicherheitsgrenze zu löschen.

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

Die Verwendung der obigen Schleife funktionierte ohne manuelles Sammeln von Müll oder Setzen des Streams auf Null usw.

etech
quelle
Welches SO benutzt du? Wenn Sie Zugriff auf die STREAMS haben, schließen Sie sie, es wird funktionieren. Wenn die Datei jedoch "bereits" gesperrt ist, liegt ein Problem vor.
marcolopes
1
-1 Eine Schleife in Ihrem Code zum Löschen einer Datei ist keine gute Idee. Warum nicht einfach die Option gc () verwenden, die funktioniert?
Bharal
@bharal Hinweis Ich stelle oben fest, dass keine der anderen Lösungen (einschließlich gc ()) in meinem speziellen Fall funktioniert hat. Obwohl ich damit einverstanden bin, dass die Verwendung einer while-Schleife nicht ideal ist, kann sie unter bestimmten Umständen eine geeignete Lösung sein. Das Hinzufügen einer zusätzlichen Prüfung zum obigen Code, z. B. einer Timeout- oder Max-Iterationsvariablen, wäre ein guter Weg, um die while-Schleife sicherer zu machen.
Etech
@etech Wenn Ihre Datei jedoch nie existiert hat, haben Sie gerade eine Endlosschleife erstellt.
Bharal
0

Das Problem könnte sein, dass die Datei immer noch von einem Programm als geöffnet und gesperrt angesehen wird. oder vielleicht ist es eine Komponente aus Ihrem Programm, in der es geöffnet wurde, also müssen Sie sicherstellen, dass Sie die dispose()Methode verwenden, um dieses Problem zu lösen. dhJFrame frame; .... frame.dispose();

Winnifred
quelle
0

Sie müssen alle Streams schließen oder den Try-with-Resource-Block verwenden

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;
}
Feech
quelle
0

Wenn file.delete () false sendet, wird Ihr Bufferedreader-Handle in den meisten Fällen nicht geschlossen. Einfach in der Nähe und es scheint für mich normal zu funktionieren.

Piyush
quelle
0

Ich hatte das gleiche Problem unter Windows. Ich habe die Datei in Scala Zeile für Zeile mit gelesen

Source.fromFile(path).getLines()

Jetzt lese ich es als Ganzes mit

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

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

Dadurch wird die Datei nach dem Lesen und jetzt ordnungsgemäß geschlossen

new File(path).delete

funktioniert.

sbtpr
quelle
0

FÜR Eclipse / NetBeans

Starten Sie Ihre IDE neu und führen Sie Ihren Code erneut aus. Dies ist nur eine Trickarbeit für mich nach einem einstündigen Kampf.

Hier ist mein Code:

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

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

Ausgabe:

Löschen

Omore
quelle
0

Ein weiterer Eckfall, in dem dies passieren könnte: Wenn Sie eine JAR-Datei über eine URLund später lesen / schreiben, versuchen Sie, dieselbe Datei innerhalb derselben JVM-Sitzung zu löschen.

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!

Grund dafür ist, dass die interne JAR-Dateiverarbeitungslogik von Java dazu neigt, JarFileEinträge zwischenzuspeichern:

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

Und jede JarFile(vielmehr die zugrunde liegende ZipFileStruktur) würde vom Zeitpunkt der Erstellung bis zum close()Aufruf ein Handle für die Datei enthalten :

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;

Zu diesem NetBeans-Problem gibt es eine gute Erklärung .


Anscheinend gibt es zwei Möglichkeiten, dies zu "beheben":

  • Sie können das Zwischenspeichern von JAR-Dateien deaktivieren - für die aktuelle URLConnectionoder für alle zukünftigen URLConnections (global) in der aktuellen JVM-Sitzung:

    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);
  • [HACK WARNING!] Sie können das manuell JarFileaus dem Cache löschen, wenn Sie damit fertig sind. Der Cache-Manager sun.net.www.protocol.jar.JarFileFactoryist paketprivat, aber etwas Reflexionszauber kann die Arbeit für Sie erledigen:

    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.

Bitte beachten Sie: All dies basiert auf Java 8 codebase ( 1.8.0_144); Sie funktionieren möglicherweise nicht mit anderen / späteren Versionen.

Janaka Bandara
quelle