file.delete () zwraca false, mimo że file.exists (), file.canRead (), file.canWrite (), file.canExecute () wszystkie zwracają true


90

Próbuję usunąć plik po zapisaniu w nim czegoś za pomocą FileOutputStream. Oto kod, którego używam do pisania:

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

Jak widać, opróżniam i zamykam strumień, ale kiedy próbuję usunąć, file.delete()zwraca fałsz.

Sprawdziłem przed usunięciem, aby sprawdzić, czy plik istnieje i: file.exists(), file.canRead(), file.canWrite(), file.canExecute()wszystko return true. Zaraz po wywołaniu tych metod próbuję file.delete()zwrócić wartość false.

Czy jest coś, co zrobiłem źle?


Zapomniałem wspomnieć, że żaden wyjątek nie został złapany.
Jenny Smith

Czy na pewno plik nie jest używany przez inny proces? Zablokowałeś to? Czy to działa z wyjściem deleteOnExit +?
instancja mnie

Na jakim systemie operacyjnym używasz? Czy możesz ręcznie usunąć plik? Coś może mieć otwarty uchwyt do pliku.
akarnokd

Jak mogę się dowiedzieć, czy jest używany przez inny proces? Nie zamknąłem go. Nie mogę użyć metody deleteOnExit, ponieważ muszę usunąć plik, a następnie kontynuować aplikację. Pomysł polega na tym, że próbuję opróżnić folder tymczasowy, który służy do przechowywania plików z innego folderu na jedną sesję. Kiedy naciskam przycisk otwierania w mojej aplikacji, oznacza to, że folder musi zostać opróżniony, aby zrobić miejsce na inne pliki. Jeśli pominę część, kiedy piszę w tym pliku, wszystko jest w porządku i mogę to usunąć.
Jenny Smith

1
Czy możesz zawinąć swój kolor i zamknąć w ostatnim bloku? Wiem, że to zrobiłeś i nie działa, ale doda to jasności Twojemu pytaniu.
bharal

Odpowiedzi:


100

Kolejny błąd w Javie. Rzadko je znajduję, tylko drugi w mojej 10-letniej karierze. To jest moje rozwiązanie, jak wspominali inni. Użyłem nether System.gc(). Ale tutaj, w moim przypadku, jest to absolutnie kluczowe. Dziwne? TAK!

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

7
Chociaż nie otworzyłem go żadnym strumieniem (po prostu robiąc a new File(path)), napotkałem ten sam problem i dodałem, System.gc()zanim delete()zadziałało!
ixM

1
Jaki jest inny błąd?
bogdan.mustiata

nie działa dla mnie. Plik, którego nie mogę usunąć, jest plikiem ZIP pobranego pliku z adresu URL. Fajną rzeczą jest to, że pobrany plik zostanie usunięty. Dowolny pomysł?
Andrea_86

Pracował dla mnie. I @andreadi nigdy nie użyłem FileChannel.map w moim kodzie. Wygląda na to, że ten błąd został zamknięty jako nierozwiązywalny, co sprawia, że ​​jestem niedowierzający, ponieważ ta System.gcsztuczka działa za każdym razem.
Adam Burley

Pojedyncza gc () może nie pomócSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj

48

To była dość dziwna sztuczka, która zadziałała. Chodzi o to, że kiedy wcześniej przeczytałem zawartość pliku, użyłem BufferedReader. Po przeczytaniu zamknąłem bufor.

W międzyczasie przełączyłem się i teraz czytam treść za pomocą FileInputStream. Również po skończeniu czytania zamykam strumień. A teraz to działa.

Problem w tym, że nie mam na to wytłumaczenia.

Nie wiem BufferedReaderi FileOutputStreambyć niekompatybilnym.


4
Twierdzę wtedy, że to błąd: java.sun.com/javase/6/docs/api/java/io/… Jest tam napisane, że metoda powinna zamknąć strumień i wszelkie powiązane z nim zasoby. Zwykle lubię zamykać wszystkie strumienie w odwróconej kolejności (ostatnie otwarte są pierwszymi do zamknięcia) za pomocą IOUtils.close Quietly, ale zwykle jest to przesada.
Ravi Wallau

2
Miałem dokładnie ten problem, ja i myślałem, że zwariowałem. Dzięki za rozwiązanie!
Electrons_Ahoy

14
Jest rok 2011 z JDK 7 i problem nadal nie został rozwiązany. Tak się cieszę, że znalazłem ten wątek - po prostu nie mogłem dowiedzieć się, co jest nie tak ...
David

Miałem bardzo irytujący problem z plikiem, który przeczytałem i zamknąłem, a następnie próbowałem usunąć. Nic, co tu wymieniono, nie pomogło. Potem po godzinie odkryłem, że to tylko prawa do pliku, ponieważ plik został utworzony przez innego użytkownika :-D
runholen

Tylko dźgnięcie w ciemność ... Czy możesz zadzwonić, finalize()żeby posprzątać? A może zestaw to = null? Zobacz Wymuszanie finalizacji i wyrzucanie elementów bezużytecznych .
jww

19

Wypróbowałem tę prostą rzecz i wydaje się, że działa.

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

Mi to pasuje.

Jeśli to nie zadziała, spróbuj uruchomić aplikację Java z sudo w systemie Linux i jako administrator w systemie Windows. Aby upewnić się, że Java ma uprawnienia do zmiany właściwości pliku.


1
W przypadku tego problemu; generalnie ludzie mówią o ustawianiu referencji na null lub wywoływaniu system.gc (); ale dla mnie nawet po restarcie serwera pliki nie były usuwane !! Ale file.setWritable (true); właśnie pracowałem ...
Deepak Singhal

6

Przed próbą usunięcia / zmiany nazwy dowolnego pliku należy upewnić się, że wszystkie czytniki lub osoby piszące (na przykład: BufferedReader/ InputStreamReader/ BufferedWriter) są prawidłowo zamknięte.

Kiedy próbujesz odczytać / zapisać dane z / do pliku, plik jest zatrzymywany przez proces i nie jest zwalniany do czasu zakończenia wykonywania programu. Jeśli chcesz wykonać operacje usuwania / zmiany nazwy przed zakończeniem programu, musisz użyć close()metody dołączonej do java.io.*klas.


1
Dotyczy to tylko systemu Windows. W każdym prawdziwym systemie operacyjnym możesz usuwać i zmieniać nazwy otwartych plików.
Archie

3

Jak skomentował Jon Skeet, powinieneś zamknąć swój plik w ostatnim bloku {...}, aby mieć pewność, że jest zawsze zamknięty. I zamiast połykać wyjątki za pomocą e.printStackTrace, po prostu nie przechwytuj i nie dodawaj wyjątku do sygnatury metody. Jeśli z jakiegoś powodu nie możesz, przynajmniej zrób to:

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

Teraz pytanie numer 2:

Co jeśli zrobisz to:

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

Czy byłbyś w stanie usunąć plik?

Ponadto pliki są opróżniane po zamknięciu. Używam IOUtils.closeQuietly (...), więc używam metody flush, aby upewnić się, że zawartość pliku jest tam, zanim spróbuję go zamknąć (IOUtils.closeQuietly nie zgłasza wyjątków). Coś takiego:

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

Wiem więc, że jest tam zawartość pliku. Ponieważ zwykle ma dla mnie znaczenie, że zawartość pliku jest zapisana, a nie, czy plik można zamknąć, czy nie, tak naprawdę nie ma znaczenia, czy plik został zamknięty, czy nie. W Twoim przypadku, ponieważ ma to znaczenie, polecam samodzielne zamknięcie pliku i odpowiednie traktowanie wyjątków.


Spróbowałem pierwszej rzeczy - zamknięcia strumienia wyjściowego w ostatnim bloku. I to nie zadziałało. To, co się stało, gdy próbowałem później przeczytać, to komunikat, że strumień został zamknięty. Podczas przełączania i odczytywania pliku za pomocą FileInputReader, żadna z "złych" rzeczy opisanych powyżej nie wydarzyła się.
Jenny Smith

2

Nie ma powodu, dla którego nie powinieneś być w stanie usunąć tego pliku. Chciałbym sprawdzić, kto ma kontrolę nad tym plikiem. W systemach unix / linux możesz użyć narzędzia lsof, aby sprawdzić, który proces ma blokadę pliku. W systemie Windows możesz użyć eksploratora procesów.

w przypadku lsof wystarczy powiedzieć:

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

w eksploratorze procesów możesz użyć menu wyszukiwania i wprowadzić nazwę pliku, aby pokazać uchwyt, który wskaże proces blokujący plik.

tutaj jest kod, który robi to, co myślę, że musisz zrobić:

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

Działa dobrze na OS X. Nie testowałem tego w systemie Windows, ale podejrzewam, że powinien działać również w systemie Windows. Przyznam się również, że zauważyłem nieoczekiwane zachowanie podczas obsługi plików wrt w systemie Windows.


1
W przypadku systemu Windows można pobrać darmowy Process Explorer z witryny
akarnokd

2

Jeśli pracujesz w Eclipse IDE, może to oznaczać, że nie zamknąłeś pliku podczas poprzedniego uruchomienia aplikacji. Kiedy miałem ten sam komunikat o błędzie podczas próby usunięcia pliku, to był powód. Wygląda na to, że Eclipse IDE nie zamyka wszystkich plików po zakończeniu działania aplikacji.


1

Mam nadzieję, że to pomoże. Natknąłem się na podobny problem, w którym nie mogłem usunąć mojego pliku po tym, jak mój kod java skopiował zawartość do innego folderu. Po obszernym wyszukiwaniu w Google jawnie zadeklarowałem wszystkie zmienne związane z operacjami na plikach i wywołałem metodę close () każdego obiektu operacji na plikach i ustawiłem je na NULL. Następnie jest funkcja o nazwie System.gc (), która wyczyści mapowanie we / wy pliku (nie jestem pewien, po prostu podam, co jest podane na stronach internetowych).

Oto mój przykładowy kod:

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

odpowiedź brzmi: kiedy ładujesz plik, musisz zastosować metodę „close”, w każdym wierszu kodu, dla mnie działa


0

Raz w Ruby pojawił się problem polegający na tym, że pliki w systemie Windows wymagały „fsync”, aby faktycznie móc odwrócić się i ponownie odczytać plik po zapisaniu go i zamknięciu. Może to jest podobna manifestacja (a jeśli tak, myślę, że naprawdę jest to błąd systemu Windows).


0

Żadne z wymienionych tutaj rozwiązań nie zadziałało w mojej sytuacji. Moim rozwiązaniem było użycie pętli while, próbując usunąć plik, z 5-sekundowym (konfigurowalnym) limitem bezpieczeństwa.

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

Korzystanie z powyższej pętli działało bez konieczności ręcznego zbierania śmieci lub ustawiania strumienia na zero itp.


Jakiego SO używasz? Jeśli masz dostęp do STRUMIENI, zamknij je, to zadziała. Ale jeśli plik jest „już” zablokowany, masz problem.
marcolopes

1
-1 zapętlenie kodu w celu usunięcia pliku nie jest dobrym pomysłem. Dlaczego po prostu nie użyć opcji gc (), która działa?
bharal

@bharal Uwaga Powyżej stwierdzam, że żadne z pozostałych rozwiązań (w tym gc ()) nie działało w moim przypadku. Chociaż zgadzam się, że użycie pętli while nie jest idealne, w pewnych okolicznościach może to być odpowiednie rozwiązanie. Dodanie dodatkowego sprawdzenia do powyższego kodu, takiego jak zmienna limitu czasu lub maksymalnej liczby iteracji, byłoby dobrym sposobem na zwiększenie bezpieczeństwa pętli while.
etech

@etech, jeśli twój plik nigdy nie istniał, właśnie utworzyłeś nieskończoną pętlę.
bharal

0

Problem może polegać na tym, że plik jest nadal postrzegany jako otwarty i zablokowany przez program; a może jest to komponent twojego programu, w którym został otwarty, więc musisz upewnić się, że używasz dispose()metody do rozwiązania tego problemu. to znaczyJFrame frame; .... frame.dispose();


0

Musisz zamknąć wszystkie strumienie lub użyć bloku 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

jeśli file.delete () wysyła false, to w większości przypadków uchwyt Bufferedreader nie zostanie zamknięty. Po prostu blisko i wydaje mi się, że działa normalnie.


0

Miałem ten sam problem w systemie Windows. Kiedyś czytałem plik w scali wiersz po wierszu z

Source.fromFile(path).getLines()

Teraz czytam to w całości za pomocą

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

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

który zamyka plik poprawnie po przeczytaniu i teraz

new File(path).delete

Pracuje.


0

DLA Eclipse / NetBeans

Zrestartuj IDE i uruchom kod jeszcze raz, to dla mnie tylko sztuczka po godzinnej walce.

Oto mój kod:

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

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

Wynik:

Usunąć


0

Kolejny przypadek, w którym może się to zdarzyć: jeśli odczytujesz / zapisujesz plik JAR za pomocą a, URLa później spróbujesz usunąć ten sam plik w tej samej sesji 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!

Powodem jest to, że wewnętrzna logika obsługi plików JAR w Javie ma tendencję do buforowania JarFilewpisów:

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

I każda JarFile(a raczej podstawowa ZipFilestruktura) trzymałaby uchwyt do pliku, od momentu konstrukcji aż do close()wywołania:

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;

Istnieje dobre wyjaśnienie tego problemu z NetBeans .


Najwyraźniej istnieją dwa sposoby, aby to „naprawić”:

  • Możesz wyłączyć buforowanie plików JAR - dla bieżącej URLConnectionlub dla wszystkich przyszłych URLConnections (globalnie) w bieżącej sesji JVM:

    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);
    
  • [OSTRZEŻENIE HACK!] Możesz ręcznie wyczyścić JarFilepamięć podręczną, gdy skończysz z tym. Menedżer pamięci podręcznej sun.net.www.protocol.jar.JarFileFactoryjest prywatnym pakietem, ale pewna magia refleksji może wykonać zadanie za Ciebie:

    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.
    

Uwaga: wszystko to jest oparte na bazie kodu Java 8 ( 1.8.0_144); mogą nie działać z innymi / późniejszymi wersjami.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.