file.delete () devuelve falso aunque file.exists (), file.canRead (), file.canWrite (), file.canExecute () todos devuelven verdadero

90

Estoy tratando de eliminar un archivo, después de escribir algo en él, con FileOutputStream. Este es el código que utilizo para escribir:

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

Como se ve, lavo y cierro el flujo, pero cuando trato de eliminar, file.delete() devuelve falso.

He comprobado antes de la eliminación para ver si el archivo existe, y: file.exists(), file.canRead(), file.canWrite(), file.canExecute()todos vuelven realidad. Justo después de llamar a estos métodos, intento file.delete()devolver falso.

¿Hay algo que haya hecho mal?

Jenny Smith
fuente
Olvidé mencionar que no se captura ninguna excepción.
Jenny Smith
¿Está seguro de que el archivo no está siendo utilizado por otro proceso? ¿Lo cerraste? ¿Funciona con deleteOnSalir + salir?
instancia de mí
¿En qué sistema operativo estás ejecutando? ¿Puede eliminar manualmente el archivo? Algo podría sostener un asa abierta para el archivo.
akarnokd
¿Cómo puedo saber si otro proceso lo utiliza? No lo cerré. No puedo usar el método deleteOnSalir, porque necesito eliminar el archivo y continuar con mi aplicación después de eso. La idea es que trato de vaciar una carpeta temporal, que se usa para almacenar archivos de una carpeta diferente para una sesión. Cuando presiono el botón Abrir en mi aplicación, significa que la carpeta debe vaciarse para dejar espacio para algunos otros archivos. Si me salto la parte cuando escribo en ese archivo, está bien y puedo eliminarlo.
Jenny Smith
1
¿Puede envolver su descarga y cerrar en un bloque finalmente? Sé que ha hecho esto y no funciona, pero agregará claridad a su pregunta.
bharal

Respuestas:

99

Otro error en Java. Rara vez los encuentro, solo el segundo en mis 10 años de carrera. Esta es mi solución, como han mencionado otros. Yo no he usado System.gc(). Pero aquí, en mi caso, es absolutamente crucial. ¿Extraño? ¡SI!

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
fuente
7
Aunque no lo abrí con ningún tipo de flujo (solo haciendo a new File(path)), encontré el mismo problema y agregué System.gc()antes de delete()que funcionara.
ixM
1
¿Cuál es el otro error?
bogdan.mustiata
no funciona para mí. El archivo que no puedo eliminar es un archivo ZIP de un archivo DownloadFromURL. Lo bueno es que el archivo descargado se eliminará. ¿Alguna idea?
Andrea_86
Trabajó para mi. Y @andreadi nunca usé FileChannel.map en mi código. Parece que este error se cerró como irresoluble, lo que me hace incrédulo porque el System.gctruco funciona siempre.
Adam Burley
Un solo gc () podría no ayudarSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj
48

Fue bastante extraño el truco que funcionó. La cosa es que cuando he leído previamente el contenido del archivo, usé BufferedReader. Después de leer, cerré el búfer.

Mientras tanto cambié y ahora estoy leyendo el contenido usando FileInputStream. También después de terminar de leer cierro el arroyo. Y ahora está funcionando.

El problema es que no tengo la explicación para esto.

No lo sé BufferedReadery FileOutputStreamser incompatible.

Jenny Smith
fuente
4
Entonces, diría que es un error: java.sun.com/javase/6/docs/api/java/io/… Allí dice que el método debería cerrar el flujo y cualquier recurso asociado con él. Por lo general, me gusta cerrar todos los flujos en la secuencia invertida (los últimos en estar abiertos son los primeros en cerrarse) usando IOUtils.closeQuietly, pero tiende a ser excesivo.
Ravi Wallau
2
Estaba teniendo este problema exacto yo y pensé que me estaba volviendo loco. ¡Gracias por la solución!
Electrons_Ahoy
14
Es 2011 con JDK 7 y el problema aún no se ha solucionado. Estoy tan contento de haber encontrado este hilo, simplemente no pude averiguar qué estaba mal ...
David
Tuve un problema muy molesto con un archivo que leí y cerré y luego intenté eliminar. Nada de lo mencionado aquí ayudó. Luego, después de una hora, descubrí que eran solo los derechos del archivo, ya que el archivo había sido creado por otro usuario :-D
runholen
Solo una puñalada en la oscuridad ... ¿Puedes llamar finalize()para asegurar la limpieza? ¿O quizás listo to = null? Consulte Forzar la finalización y la recolección de basura .
jww
19

Probé esta cosa simple y parece estar funcionando.

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

Esto funciona para mi.

Si esto no funciona, intente ejecutar su aplicación Java con sudo si está en Linux y como administrador cuando está en Windows. Solo para asegurarse de que Java tenga derechos para cambiar las propiedades del archivo.

Kislay Sinha
fuente
1
Por este problema; en general, la gente habla de establecer referencias como nulas o de llamar a system.gc (); ¡Pero para mí, incluso después de reiniciar el servidor, los archivos no se eliminaron! Pero file.setWritable (true); acaba de trabajar ..
Deepak Singhal
6

Antes de intentar eliminar / cambiar el nombre de cualquier archivo, debe asegurarse de que todos los lectores o escritores (por ejemplo: BufferedReader/ InputStreamReader/ BufferedWriter) estén correctamente cerrados.

Cuando intenta leer / escribir sus datos desde / hacia un archivo, el proceso lo retiene y no se libera hasta que se completa la ejecución del programa. Si desea realizar las operaciones de eliminación / cambio de nombre antes de que finalice el programa, debe utilizar el close()método que viene con las java.io.*clases.

Sai Manoj
fuente
1
Esto solo es cierto en Windows. En cualquier sistema operativo real, puede eliminar y cambiar el nombre de los archivos abiertos.
Archie
3

Como comentó Jon Skeet, debe cerrar su archivo en el bloque finalmente {...}, para asegurarse de que siempre esté cerrado. Y, en lugar de tragar las excepciones con e.printStackTrace, simplemente no capte y agregue la excepción a la firma del método. Si no puede por alguna razón, al menos haga esto:

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

Ahora, pregunta número 2:

¿Qué pasa si haces esto?

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

¿Podrías borrar el archivo?

Además, los archivos se vacían cuando se cierran. Yo uso IOUtils.closeQuietly (...), así que uso el método flush para asegurarme de que el contenido del archivo esté ahí antes de intentar cerrarlo (IOUtils.closeQuietly no arroja excepciones). Algo como esto:

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

Entonces sé que el contenido del archivo está ahí. Como generalmente me importa que el contenido del archivo esté escrito y no si el archivo podría cerrarse o no, realmente no importa si el archivo se cerró o no. En su caso, como importa, le recomendaría cerrar el expediente usted mismo y tratar las excepciones de acuerdo.

Ravi Wallau
fuente
Probé lo primero: cerrar el flujo de salida en el bloque finalmente. Y no funcionó. Lo que sucedió si intenté leer después de eso es que recibí un mensaje que decía que la transmisión estaba cerrada. Al cambiar y leer el archivo con FileInputReader, no sucedió nada de lo "malo" descrito anteriormente.
Jenny Smith
2

No hay ninguna razón por la que no pueda eliminar este archivo. Miraría para ver quién tiene un control sobre este archivo. En unix / linux, puede usar la utilidad lsof para verificar qué proceso tiene un bloqueo en el archivo. En Windows, puede utilizar el explorador de procesos.

para lsof, es tan simple como decir:

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

para el explorador de procesos, puede usar el menú de búsqueda e ingresar el nombre del archivo para mostrarle el identificador que le indicará el proceso que bloquea el archivo.

aquí hay un código que hace lo que creo que debes hacer:

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

Funciona bien en OS X. No lo he probado en Windows, pero sospecho que también debería funcionar en Windows. También admitiré haber visto un comportamiento inesperado en el manejo de archivos wrt de Windows.

neesh
fuente
1
Para Windows, puede descargar el Explorador de procesos gratuito de MS: technet.microsoft.com/en-us/sysinternals/bb896653.aspx
akarnokd
2

Si está trabajando en Eclipse IDE, eso podría significar que no ha cerrado el archivo en el lanzamiento anterior de la aplicación. Cuando tuve el mismo mensaje de error al intentar eliminar un archivo, esa fue la razón. Parece que Eclipse IDE no cierra todos los archivos después de la terminación de una aplicación.

Gangnus
fuente
1

Ojalá esto ayude. Me encontré con un problema similar en el que no pude eliminar mi archivo después de que mi código Java hiciera una copia del contenido en la otra carpeta. Después de una extensa búsqueda en Google, declaré explícitamente todas las variables relacionadas con la operación de archivo y llamé al método close () de cada objeto de operación de archivo, y los configuré en NULL. Luego, hay una función llamada System.gc (), que borrará el mapeo de E / S del archivo (no estoy seguro, solo digo lo que se da en los sitios web).

Aquí está mi código de ejemplo:

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
fuente
1

la respuesta es que cuando cargas el archivo, necesitas aplicar el método "cerrar", en cualquier línea de código, me funciona

usuario3200921
fuente
0

Una vez hubo un problema en ruby ​​donde los archivos en Windows necesitaban un "fsync" para poder volverse y volver a leer el archivo después de escribirlo y cerrarlo. Tal vez esta sea una manifestación similar (y si es así, creo que un error de Windows, en realidad).

Rogerdpack
fuente
0

Ninguna de las soluciones enumeradas aquí funcionó en mi situación. Mi solución fue usar un bucle while, intentando eliminar el archivo, con un límite de 5 segundos (configurable) por seguridad.

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

El uso del bucle anterior funcionó sin tener que hacer ninguna recolección manual de basura o configurar la transmisión en nulo, etc.

etech
fuente
¿Qué SO usas? Si puede tener acceso a los STREAMS, ciérrelos, funcionará. Pero si el archivo "ya" está bloqueado, entonces, tiene un problema.
marcolopes
1
-1 hacer un bucle en su código para eliminar un archivo no es una gran idea. ¿Por qué no usar la opción gc () que funciona?
bharal
@bharal Aviso Declaro anteriormente que ninguna de las otras soluciones (incluido gc ()) funcionó en mi caso particular. Si bien estoy de acuerdo en que usar un bucle while no es ideal, puede ser una solución adecuada en determinadas circunstancias. Agregar una verificación adicional al código anterior, como un tiempo de espera o una variable de iteraciones máximas, sería una buena manera de hacer que el ciclo while sea más seguro.
etech
@etech, sin embargo, si su archivo nunca existió, acaba de crear un bucle infinito.
bharal
0

El problema podría ser que el archivo todavía se vea como abierto y bloqueado por un programa; o tal vez es un componente de su programa en el que se ha abierto, por lo que debe asegurarse de utilizar el dispose()método para resolver ese problema. es decirJFrame frame; .... frame.dispose();

Winnifred
fuente
0

Tienes que cerrar todas las transmisiones o usar el bloque 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
fuente
0

si file.delete () envía falso, en la mayoría de los casos, el identificador de Bufferedreader no se cerrará. Solo cerca y parece funcionar para mí normalmente.

Piyush
fuente
0

Tuve el mismo problema en Windows. Solía ​​leer el archivo en scala línea por línea con

Source.fromFile(path).getLines()

Ahora lo leo completo con

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

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

que cierra el archivo correctamente después de leer y ahora

new File(path).delete

trabajos.

sbtpr
fuente
0

PARA Eclipse / NetBeans

Reinicie su IDE y ejecute su código nuevamente, esto es solo un truco para mí después de una hora de lucha.

Aquí está mi código:

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

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

Salida:

Eliminar

Omore
fuente
0

Otro caso de esquina en el que esto podría suceder: si lee / escribe un archivo JAR a través de a URLy luego intenta eliminar el mismo archivo dentro de la misma sesión de 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 razón es que la lógica de manejo de archivos JAR interno de Java tiende a almacenar en caché las JarFileentradas:

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

Y cada uno JarFile(más bien, la ZipFileestructura subyacente ) mantendría un identificador para el archivo, desde el momento de la construcción hasta que close()se invoca:

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;

Hay una buena explicación sobre este problema de NetBeans .


Aparentemente, hay dos formas de "arreglar" esto:

  • Puede deshabilitar el almacenamiento en caché de archivos JAR, para el actual URLConnectiono para todos los correos URLConnectionelectrónicos futuros (globalmente) en la sesión actual de 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);
  • [ADVERTENCIA DE HACK!] Puede purgar manualmente el JarFilede la caché cuando haya terminado con él. El administrador de caché sun.net.www.protocol.jar.JarFileFactoryes un paquete privado, pero algo de magia de reflexión puede hacer el trabajo por usted:

    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.

Tenga en cuenta: Todo esto se basa en el código base de Java 8 ( 1.8.0_144); es posible que no funcionen con otras versiones o versiones posteriores.

Janaka Bandara
fuente