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?

Jenny Smith
źródło
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();
    }
}
Da Martin
źródło
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.

Jenny Smith
źródło
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.

Kislay Sinha
źródło
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.

Sai Manoj
źródło
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.

Ravi Wallau
źródło
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.

neesh
źródło
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.

Gangnus
źródło
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();
    }
}
poh
źródło
1

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

user3200921
źródło
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).

rogerdpack
źródło
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.

etech
źródło
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();

Winnifred
źródło
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;
}
feech
źródło
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.

Piyush
źródło
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.

sbtpr
źródło
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ąć

Omore
źródło
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.

Janaka Bandara
źródło