file.delete () renvoie false même si file.exists (), file.canRead (), file.canWrite (), file.canExecute () retournent tous true

90

J'essaie de supprimer un fichier, après y avoir écrit quelque chose, avec FileOutputStream. Voici le code que j'utilise pour écrire:

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

Comme on le voit, je vide et ferme le flux, mais lorsque j'essaye de supprimer, file.delete()renvoie false.

J'ai vérifié avant la suppression pour voir si le fichier existe et: file.exists(), file.canRead(), file.canWrite(), file.canExecute()tout retour vrai. Juste après avoir appelé ces méthodes, j'essaye file.delete()et renvoie false.

Y a-t-il quelque chose que j'ai fait de mal?

Jenny Smith
la source
J'ai oublié de mentionner qu'aucune exception n'est interceptée.
Jenny Smith
Êtes-vous sûr que le fichier n'est pas utilisé par un autre processus? L'avez-vous verrouillé? Fonctionne-t-il avec la sortie de deleteOnExit +?
instanceof me
Sur quel OS utilisez-vous? Pouvez-vous supprimer manuellement le fichier? Quelque chose peut contenir une poignée ouverte sur le fichier.
akarnokd
Comment puis-je savoir s'il est utilisé par un autre processus? Je ne l'ai pas verrouillé. Je ne peux pas utiliser la méthode deleteOnExit, car je dois supprimer le fichier et continuer mon application après cela.L'idée est que j'essaie de vider un dossier temporaire, qui est utilisé pour stocker des fichiers d'un dossier différent pour une session. Lorsque j'appuie sur le bouton d'ouverture de mon application, cela signifie que le dossier doit être vidé, pour faire de la place pour d'autres fichiers. Si je saute la partie lorsque j'écris dans ce fichier, tout va bien et je peux la supprimer.
Jenny Smith
1
Pouvez-vous s'il vous plaît envelopper votre flush & close dans un bloc final? Je sais que vous avez fait cela et que cela ne fonctionne pas, mais cela clarifiera votre question.
bharal

Réponses:

99

Un autre bogue en Java. Je les trouve rarement, seulement ma deuxième en 10 ans de carrière. C'est ma solution, comme d'autres l'ont mentionné. Je n'ai jamais utilisé System.gc(). Mais ici, dans mon cas, c'est absolument crucial. Bizarre? OUI!

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
la source
7
Bien que je ne l'ai pas ouvert avec aucun type de flux (juste en faisant un new File(path)), j'ai rencontré le même problème et l'ajout System.gc()avant que delete()cela fonctionne!
ixM
1
Quel est l'autre bug?
bogdan.mustiata
ne me parle pas. Le fichier que je ne peux pas supprimer est un fichier ZIP d'un fichier téléchargéFromURL. La bonne chose est que le fichier téléchargé sera supprimé. Une idée?
Andrea_86
A travaillé pour moi. Et @andreadi je n'ai jamais utilisé FileChannel.map dans mon code. Il semble que ce bug a été fermé comme impossible à résoudre, ce qui me rend incrédule car l' System.gcastuce fonctionne à chaque fois.
Adam Burley
Seul gc () pourrait ne pas aiderSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj
48

C'était assez étrange le truc qui a fonctionné. Le problème, c'est que lorsque j'ai lu le contenu du fichier, je l'ai utilisé BufferedReader. Après lecture, j'ai fermé le tampon.

Pendant ce temps, j'ai changé et maintenant je lis le contenu en utilisant FileInputStream. Aussi après avoir fini de lire, je ferme le flux. Et maintenant ça marche.

Le problème est que je n'ai pas d'explication à cela.

Je ne sais pas BufferedReaderet FileOutputStreamêtre incompatible.

Jenny Smith
la source
4
Je dirais que c'est un bogue alors: java.sun.com/javase/6/docs/api/java/io / ... Il est dit là-dedans que la méthode devrait fermer le flux et toutes les ressources associées. J'aime généralement fermer tous les flux dans la séquence inversée (les derniers à être ouverts sont les premiers à être fermés) en utilisant IOUtils.closeQuietly, mais cela a tendance à être excessif.
Ravi Wallau
2
J'avais exactement ce problème moi et je pensais que je devenais fou. Merci pour la solution!
Electrons_Ahoy
14
Nous sommes en 2011 avec JDK 7 et le problème n'est toujours pas résolu. Je suis tellement content d'avoir trouvé ce fil - je ne pouvais tout simplement pas comprendre ce qui n'allait pas ...
David
J'ai eu un problème très ennuyeux avec un fichier que j'ai lu et fermé, puis essayé de supprimer. Rien de mentionné ici n'a aidé. Puis, après une heure, j'ai découvert que c'était juste les droits de fichier, puisque le fichier avait été créé par un autre utilisateur :-D
runholen
Juste un coup de couteau dans le noir ... Pouvez-vous appeler finalize()pour assurer le nettoyage? Ou peut-être ensemble to = null? Voir Forcer la finalisation et la récupération de place .
jww
19

J'ai essayé cette chose simple et cela semble fonctionner.

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

Ça marche pour moi.

Si cela ne fonctionne pas, essayez d'exécuter votre application Java avec sudo sous Linux et en tant qu'administrateur sous Windows. Juste pour être sûr que Java a le droit de modifier les propriétés du fichier.

Kislay Sinha
la source
1
Pour ce problème; généralement, les gens parlent de définir des références comme nulles ou d'appeler system.gc (); mais pour moi, même après le redémarrage du serveur, les fichiers n'étaient pas supprimés !! Mais file.setWritable (true); vient de travailler ..
Deepak Singhal
6

Avant d'essayer de supprimer / renommer un fichier, vous devez vous assurer que tous les lecteurs ou rédacteurs (par exemple: BufferedReader/ InputStreamReader/ BufferedWriter) sont correctement fermés.

Lorsque vous essayez de lire / écrire vos données depuis / vers un fichier, le fichier est conservé par le processus et n'est pas libéré tant que l'exécution du programme n'est pas terminée. Si vous souhaitez effectuer les opérations de suppression / changement de nom avant la fin du programme, vous devez utiliser la close()méthode fournie avec les java.io.*classes.

Sai Manoj
la source
1
Cela n'est vrai que sous Windows. Sur n'importe quel véritable O / S, vous pouvez supprimer et renommer les fichiers ouverts.
Archie
3

Comme Jon Skeet l'a commenté, vous devez fermer votre fichier dans le bloc finally {...}, pour vous assurer qu'il est toujours fermé. Et, au lieu d'avaler les exceptions avec e.printStackTrace, n'attrapez simplement pas et n'ajoutez pas l'exception à la signature de la méthode. Si vous ne pouvez pas pour une raison quelconque, faites au moins ceci:

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

Maintenant, question numéro 2:

Et si vous faites ceci:

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

Seriez-vous capable de supprimer le fichier?

De plus, les fichiers sont vidés lorsqu'ils sont fermés. J'utilise IOUtils.closeQuietly (...), donc j'utilise la méthode flush pour m'assurer que le contenu du fichier est là avant d'essayer de le fermer (IOUtils.closeQuietly ne lève pas d'exceptions). Quelque chose comme ça:

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

Je sais donc que le contenu du fichier s'y trouve. Comme il m'importe généralement que le contenu du fichier soit écrit et non si le fichier peut être fermé ou non, peu importe si le fichier a été fermé ou non. Dans votre cas, comme cela compte, je recommanderais de fermer le dossier vous-même et de traiter les exceptions en conséquence.

Ravi Wallau
la source
J'ai essayé la première chose - fermer le flux de sortie dans le bloc finally. Et ça n'a pas marché. Ce qui s'est passé si j'ai essayé de lire après cela, c'est que j'ai reçu un message disant que le flux était fermé. Lors de la commutation et de la lecture du fichier avec FileInputReader, aucune des «mauvaises» choses décrites ci-dessus ne s'est produite.
Jenny Smith le
2

Il n'y a aucune raison pour laquelle vous ne devriez pas pouvoir supprimer ce fichier. Je chercherais à voir qui détient ce dossier. Sous unix / linux, vous pouvez utiliser l'utilitaire lsof pour vérifier quel processus a un verrou sur le fichier. Dans Windows, vous pouvez utiliser l'explorateur de processus.

pour lsof, c'est aussi simple que de dire:

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

pour l'explorateur de processus, vous pouvez utiliser le menu de recherche et entrer le nom du fichier pour vous montrer la poignée qui vous dirigera vers le processus de verrouillage du fichier.

voici un code qui fait ce que je pense que vous devez faire:

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

Cela fonctionne bien sur OS X. Je ne l'ai pas testé sur Windows mais je pense que cela devrait également fonctionner sur Windows. J'admettrai également avoir vu un comportement inattendu sur la gestion des fichiers Windows Wrt.

neesh
la source
1
Pour Windows, vous pouvez télécharger gratuitement Process Explorer à partir de MS: technet.microsoft.com/en-us/sysinternals/bb896653.aspx
akarnokd
2

Si vous travaillez dans Eclipse IDE, cela peut signifier que vous n'avez pas fermé le fichier lors du lancement précédent de l'application. Lorsque j'ai eu le même message d'erreur en essayant de supprimer un fichier, c'était la raison. Il semble qu'Eclipse IDE ne ferme pas tous les fichiers après l'arrêt d'une application.

Gangnus
la source
1

J'espère que cela aidera. Je suis tombé sur un problème similaire où je ne pouvais pas supprimer mon fichier après que mon code java ait fait une copie du contenu dans l'autre dossier. Après une recherche approfondie sur Google, j'ai explicitement déclaré toutes les variables liées aux opérations de fichier et appelé la méthode close () de chaque objet d'opération de fichier, et les ai définies sur NULL. Ensuite, il y a une fonction appelée System.gc (), qui effacera le mappage des entrées / sorties de fichiers (je ne suis pas sûr, je dis juste ce qui est donné sur les sites Web).

Voici mon exemple de code:

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
la source
1

la réponse est que lorsque vous chargez le fichier, vous devez appliquer la méthode "close", dans n'importe quelle ligne de code, fonctionne pour moi

user3200921
la source
0

Il y avait un problème une fois dans ruby ​​où les fichiers dans Windows avaient besoin d'un "fsync" pour pouvoir réellement se retourner et relire le fichier après l'avoir écrit et fermé. Peut-être que c'est une manifestation similaire (et si c'est le cas, je pense qu'un bug de Windows, vraiment).

rogerdpack
la source
0

Aucune des solutions énumérées ici n'a fonctionné dans ma situation. Ma solution était d'utiliser une boucle while, en essayant de supprimer le fichier, avec une limite de 5 secondes (configurable) pour la sécurité.

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'utilisation de la boucle ci-dessus a fonctionné sans avoir à faire de ramasse-miettes manuel ou à définir le flux sur null, etc.

etech
la source
Quel SO utilisez-vous? Si vous pouvez avoir accès aux STREAMS, fermez-les, cela fonctionnera. Mais si le fichier est "déjà" verrouillé, vous avez un problème.
marcolopes
1
-1 boucler votre code pour supprimer un fichier n'est pas une bonne idée. Pourquoi ne pas simplement utiliser l'option gc () qui fonctionne?
bharal
@bharal Remarquez que je déclare ci-dessus qu'aucune des autres solutions (y compris gc ()) n'a fonctionné dans mon cas particulier. Bien que je convienne que l'utilisation d'une boucle while n'est pas idéale, cela peut être une solution appropriée dans certaines circonstances. Ajouter une vérification supplémentaire au code ci-dessus, comme une variable timeout ou max iterations, serait un bon moyen de rendre la boucle while plus sûre.
etech
@etech si votre fichier n'a jamais existé, vous venez de créer une boucle infinie.
bharal du
0

Le problème peut être que le fichier est toujours considéré comme ouvert et verrouillé par un programme; ou peut-être est-ce un composant de votre programme dans lequel il a été ouvert, vous devez donc vous assurer d'utiliser la dispose()méthode pour résoudre ce problème. c'est à direJFrame frame; .... frame.dispose();

Winnifred
la source
0

Vous devez fermer tous les flux ou utiliser le bloc 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;
}
discours
la source
0

si file.delete () envoie false, dans la plupart des cas, votre handle de Bufferedreader ne sera pas fermé. Juste fermer et cela semble fonctionner pour moi normalement.

Piyush
la source
0

J'ai eu le même problème sous Windows. J'avais l'habitude de lire le fichier en scala ligne par ligne avec

Source.fromFile(path).getLines()

Maintenant je le lis dans son ensemble avec

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

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

qui ferme le fichier correctement après la lecture et maintenant

new File(path).delete

travaux.

sbtpr
la source
0

POUR Eclipse / NetBeans

Redémarrez votre IDE et exécutez à nouveau votre code, ce n'est qu'un truc pour moi après une heure de lutte.

Voici mon code:

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

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

Production:

Supprimer

Omore
la source
0

Un autre cas de coin où cela pourrait se produire: si vous lisez / écrivez un fichier JAR via un URLet plus tard, essayez de supprimer le même fichier dans la même session 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!

La raison en est que la logique de gestion des fichiers JAR internes de Java a tendance à mettre en cache les JarFileentrées:

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

Et chacun JarFile(plutôt, la ZipFilestructure sous-jacente ) détiendrait une poignée pour le fichier, dès le moment de la construction jusqu'àclose() son invocation:

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;

Il y a une bonne explication sur ce problème NetBeans .


Apparemment, il existe deux façons de "résoudre" ce problème:

  • Vous pouvez désactiver la mise en cache du fichier JAR - pour la session actuelle URLConnectionou pour tous les futurs URLConnection(globalement) dans la session JVM actuelle:

    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);
  • [AVERTISSEMENT DE HACK!] Vous pouvez purger manuellement le JarFiledu cache lorsque vous avez terminé. Le gestionnaire de cache sun.net.www.protocol.jar.JarFileFactoryest privé de package, mais une certaine magie de réflexion peut faire le travail pour vous:

    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.

Remarque: tout ceci est basé sur la base de code Java 8 ( 1.8.0_144); ils peuvent ne pas fonctionner avec d'autres versions / versions ultérieures.

Janaka Bandara
la source