file.delete () restituisce false anche se file.exists (), file.canRead (), file.canWrite (), file.canExecute () restituiscono tutti true


90

Sto cercando di eliminare un file, dopo averci scritto qualcosa, con FileOutputStream. Questo è il codice che utilizzo per scrivere:

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

Come si vede, svuoto e chiudo il flusso, ma quando provo a eliminare, file.delete()restituisce false.

Ho controllato prima della cancellazione, per vedere se il file esiste, e: file.exists(), file.canRead(), file.canWrite(), file.canExecute()tutto il ritorno vero. Subito dopo aver chiamato questi metodi provo file.delete()e restituisce false.

C'è qualcosa che ho fatto di sbagliato?


Ho dimenticato di dire che non viene catturata alcuna eccezione.
Jenny Smith

Sei sicuro che il file non sia utilizzato da un altro processo? L'hai bloccato? Funziona con deleteOnExit + uscita?
istanza di me il

Su quale sistema operativo stai eseguendo? Puoi eliminare manualmente il file? Qualcosa potrebbe trattenere un handle aperto per il file.
akarnokd

Come faccio a sapere se è utilizzato da un altro processo? Non l'ho bloccato. Non posso usare il metodo deleteOnExit, perché devo eliminare il file e continuare la mia applicazione dopo di che. L'idea è che provo a svuotare una cartella temporanea, che viene utilizzata per memorizzare i file da una cartella diversa per una sessione. Quando premo il pulsante di apertura sulla mia domanda, significa che la cartella deve essere svuotata, per fare spazio ad altri file. Se salto la parte quando scrivo in quel file, va bene e posso cancellarlo.
Jenny Smith,

1
Puoi per favore avvolgere il tuo colore e chiudere in un blocco finale? So che l'hai fatto e non funziona, ma aggiungerà chiarezza alla tua domanda.
bharal

Risposte:


99

Un altro bug in Java. Li trovo di rado, solo il mio secondo in 10 anni di carriera. Questa è la mia soluzione, come altri hanno già detto. Non ho usato niente System.gc(). Ma qui, nel mio caso, è assolutamente cruciale. Strano? SÌ!

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

7
Anche se non l'ho aperto con alcun tipo di flusso (solo facendo a new File(path)), ho riscontrato lo stesso problema e l'aggiunta System.gc()prima delete()che funzionasse!
ixM

1
Qual è l'altro bug?
bogdan.mustiata

non funziona per me. Il file che non riesco a eliminare è un file ZIP di un file scaricatoFromURL. La cosa bella è che il file scaricato verrà eliminato. Qualche idea?
Andrea_86

Ha funzionato per me. E @andreadi non ho mai usato FileChannel.map nel mio codice. Sembra che questo bug sia stato chiuso come irrisolvibile, il che mi rende incredulo perché il System.gctrucco funziona ogni volta.
Adam Burley

Single gc () potrebbe non aiutareSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj

48

Era piuttosto strano il trucco che ha funzionato. Il fatto è che quando ho letto in precedenza il contenuto del file, ho usato BufferedReader. Dopo aver letto, ho chiuso il buffer.

Nel frattempo sono passato e ora sto leggendo il contenuto utilizzando FileInputStream. Inoltre dopo aver finito di leggere chiudo lo stream. E ora funziona.

Il problema è che non ho la spiegazione per questo.

Non lo so BufferedReadere FileOutputStreamdi essere incompatibile.


4
Direi che è un bug quindi: java.sun.com/javase/6/docs/api/java/io/… Dice che il metodo dovrebbe chiudere il flusso e tutte le risorse ad esso associate. Di solito mi piace chiudere tutti i flussi nella sequenza invertita (gli ultimi ad essere aperti sono i primi ad essere chiusi) usando IOUtils.close Tranquillamente, ma tende ad essere eccessivo.
Ravi Wallau

2
Stavo avendo questo problema esatto io e ho pensato che stavo impazzendo. Grazie per la soluzione!
Electrons_Ahoy

14
È il 2011 con JDK 7 e il problema non è ancora risolto. Sono così felice di aver trovato questo thread - semplicemente non riuscivo a capire cosa c'era che non andava ...
David

Ho avuto un problema molto fastidioso con un file che ho letto e chiuso e poi ho provato a cancellare. Niente menzionato qui ha aiutato. Poi dopo un'ora ho scoperto che erano solo i diritti del file, dato che il file era stato creato da un altro utente
MrGreen

Solo una pugnalata nel buio ... Puoi chiamare finalize()per assicurarti la pulizia? O forse ambientato to = null? Vedere Forzare la finalizzazione e la raccolta dei rifiuti .
jww

19

Ho provato questa semplice cosa e sembra funzionare.

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

Per me funziona.

Se questo non funziona prova ad eseguire la tua applicazione Java con sudo se su linux e come amministratore su windows. Solo per assicurarsi che Java abbia i diritti per modificare le proprietà del file.


1
Per questo problema; generalmente si parla di impostare i riferimenti come null o di chiamare system.gc (); ma per me anche dopo il riavvio del server i file non venivano cancellati !! Ma file.setWritable (true); appena funzionato ..
Deepak Singhal

6

Prima di provare a eliminare / rinominare qualsiasi file, è necessario assicurarsi che tutti i lettori o autori (ad esempio: BufferedReader/ InputStreamReader/ BufferedWriter) siano chiusi correttamente.

Quando si tenta di leggere / scrivere i dati da / in un file, il file viene trattenuto dal processo e non viene rilasciato fino al completamento dell'esecuzione del programma. Se si desidera eseguire le operazioni di eliminazione / ridenominazione prima che il programma termini, è necessario utilizzare il close()metodo fornito con le java.io.*classi.


1
Questo è vero solo su Windows. Su qualsiasi sistema operativo reale è possibile eliminare e rinominare i file aperti.
Archie

3

Come ha commentato Jon Skeet, dovresti chiudere il tuo file nel blocco finalmente {...}, per assicurarti che sia sempre chiuso. E, invece di inghiottire le eccezioni con e.printStackTrace, semplicemente non catturare e aggiungere l'eccezione alla firma del metodo. Se non puoi per qualsiasi motivo, fallo almeno:

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

Ora, domanda numero 2:

E se lo fai:

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

Saresti in grado di eliminare il file?

Inoltre, i file vengono scaricati quando vengono chiusi. Uso IOUtils.closeQuietly (...), quindi utilizzo il metodo flush per assicurarmi che il contenuto del file sia presente prima di provare a chiuderlo (IOUtils.closeQuietly non genera eccezioni). Qualcosa come questo:

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

Quindi so che il contenuto del file è lì. Poiché di solito è importante per me che il contenuto del file sia scritto e non se il file possa essere chiuso o meno, non importa se il file è stato chiuso o meno. Nel tuo caso, poiché è importante, consiglierei di chiudere il file da solo e di trattare le eccezioni in base.


Ho provato la prima cosa: chiudere il flusso di output nel blocco finalmente. E non ha funzionato. Quello che è successo se ho provato a leggere dopo quello è che ho ricevuto un messaggio che diceva che lo stream era chiuso. Quando si cambia e si legge il file con FileInputReader, non si è verificata nessuna delle cose "cattive" descritte sopra.
Jenny Smith

2

Non c'è motivo per cui non dovresti essere in grado di eliminare questo file. Vorrei vedere chi ha una presa su questo file. In unix / linux, puoi usare l'utility lsof per controllare quale processo ha un blocco sul file. In Windows, puoi usare Process Explorer.

per lsof, è semplice come dire:

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

per process explorer puoi usare il menu trova e inserire il nome del file per mostrarti l'handle che ti indicherà il processo che blocca il file.

ecco un codice che fa quello che penso tu debba fare:

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

Funziona bene su OS X. Non l'ho testato su Windows ma sospetto che dovrebbe funzionare anche su Windows. Devo anche ammettere di aver visto alcuni comportamenti imprevisti sulla gestione dei file Windows Wrt.


1
Per Windows, puoi scaricare Process Explorer gratuito da MS: technet.microsoft.com/en-us/sysinternals/bb896653.aspx
akarnokd

2

Se stai lavorando in Eclipse IDE, ciò potrebbe significare che non hai chiuso il file nel precedente avvio dell'applicazione. Quando ho ricevuto lo stesso messaggio di errore nel tentativo di eliminare un file, questo è stato il motivo. Sembra che Eclipse IDE non chiuda tutti i file dopo la chiusura di un'applicazione.


1

Si spera che questo possa aiutare. Mi sono imbattuto in un problema simile in cui non riuscivo a eliminare il mio file dopo che il mio codice java ha fatto una copia del contenuto nell'altra cartella. Dopo un'estesa ricerca su Google, ho dichiarato esplicitamente ogni singola variabile relativa alle operazioni sui file e ho chiamato il metodo close () di ogni oggetto delle operazioni sui file e le ho impostate su NULL. Poi, c'è una funzione chiamata System.gc (), che cancellerà la mappatura i / o del file (non sono sicuro, dico solo cosa viene fornito sui siti web).

Ecco il mio codice di esempio:

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

la risposta è quando carichi il file, devi applicare il metodo "close", in qualsiasi riga di codice, funziona per me


0

C'era un problema una volta in ruby ​​in cui i file in Windows necessitavano di un "fsync" per essere effettivamente in grado di girare intorno e rileggere il file dopo averlo scritto e chiuso. Forse questa è una manifestazione simile (e se è così, penso che un bug di Windows, davvero).


0

Nessuna delle soluzioni elencate qui ha funzionato nella mia situazione. La mia soluzione è stata quella di utilizzare un ciclo while, tentando di eliminare il file, con un limite di sicurezza di 5 secondi (configurabile).

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

L'utilizzo del ciclo precedente ha funzionato senza dover eseguire alcuna raccolta manuale dei rifiuti o impostare il flusso su null, ecc.


Che COSÌ usi? Se puoi avere accesso agli STREAM, chiudili, funzionerà. Ma se il file è "già" bloccato, allora hai un problema.
marcolopes

1
-1 eseguire un ciclo nel codice per eliminare un file non è una grande idea. Perché non usare semplicemente l'opzione gc () che funziona?
bharal

@bharal Avviso Dichiaro sopra che nessuna delle altre soluzioni (incluso gc ()) ha funzionato nel mio caso particolare. Sebbene sia d'accordo che l'utilizzo di un ciclo while non sia l'ideale, potrebbe essere una soluzione appropriata in determinate circostanze. Aggiungere un controllo aggiuntivo al codice precedente, come una variabile di timeout o max iterations, sarebbe un buon modo per rendere più sicuro il ciclo while.
etech

@etech se il tuo file non è mai esistito, hai appena creato un ciclo infinito.
bharal

0

Il problema potrebbe essere che il file è ancora visto come aperto e bloccato da un programma; o forse è un componente del programma in cui era stato aperto, quindi devi assicurarti di utilizzare il dispose()metodo per risolvere il problema. cioèJFrame frame; .... frame.dispose();


0

Devi chiudere tutti i flussi o utilizzare il blocco Prova con risorse

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

se file.delete () sta inviando false, nella maggior parte dei casi l'handle di Bufferedreader non verrà chiuso. Chiudi e sembra che funzioni normalmente.


0

Ho avuto lo stesso problema su Windows. Leggevo il file in scala riga per riga con

Source.fromFile(path).getLines()

Ora l'ho letto nel suo insieme con

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

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

che chiude correttamente il file dopo la lettura e ora

new File(path).delete

lavori.


0

PER Eclipse / NetBeans

Riavvia il tuo IDE ed esegui di nuovo il codice, questo è solo un trucco per me dopo un'ora di lotta.

Ecco il mio codice:

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

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

Produzione:

Elimina


0

Un altro caso d'angolo in cui ciò potrebbe accadere: se leggi / scrivi un file JAR tramite un URLe successivamente provi a eliminare lo stesso file all'interno della stessa sessione 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!

Il motivo è che la logica di gestione dei file JAR interna di Java tende a memorizzare nella cache le JarFilevoci:

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

E ciascuno JarFile(piuttosto, la ZipFilestruttura sottostante ) manterrebbe un handle per il file, dal momento della costruzione fino a quando close()viene invocato:

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;

C'è una buona spiegazione su questo problema di NetBeans .


Apparentemente ci sono due modi per "aggiustare" questo:

  • È possibile disabilitare la memorizzazione nella cache dei file JAR - per i messaggi correnti URLConnectiono futuri URLConnection(a livello globale) nella sessione JVM corrente:

    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!] Puoi eliminare manualmente il JarFiledalla cache quando hai finito. Il gestore della cache sun.net.www.protocol.jar.JarFileFactoryè privato del pacchetto, ma un po 'di magia di riflessione può svolgere il lavoro per te:

    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.

Nota: tutto questo è basato su Java 8 codebase ( 1.8.0_144); potrebbero non funzionare con altre / versioni successive.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.