file.delete () retorna falso, embora file.exists (), file.canRead (), file.canWrite (), file.canExecute () retornem verdadeiro

90

Estou tentando excluir um arquivo, depois de escrever algo nele, com FileOutputStream. Este é o código que uso para escrever:

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 pode ser visto, libero e fecho o stream, mas quando tento deletar, file.delete()retorna falso.

Eu verifiquei antes da eliminação para ver se existe o arquivo e: file.exists(), file.canRead(), file.canWrite(), file.canExecute()todo o retorno verdadeiro. Logo após chamar esses métodos, tento file.delete()e retorna falso.

Há algo que eu fiz de errado?

Jenny Smith
fonte
Esqueci de mencionar que nenhuma exceção foi detectada.
Jenny Smith
Tem certeza de que o arquivo não é usado por outro processo? Você trancou? Funciona com deleteOnExit + saindo?
instanceof me
Qual sistema operacional você está usando? Você pode excluir o arquivo manualmente? Algo pode conter uma alça aberta para o arquivo.
akarnokd
Como posso saber se ele é usado por outro processo? Eu não tranquei. Não posso usar o método deleteOnExit, porque preciso deletar o arquivo e continuar minha aplicação depois disso. A ideia é tentar esvaziar uma pasta temporária, que é usada para armazenar arquivos de uma pasta diferente para uma sessão. Quando pressiono o botão abrir na minha aplicação, significa que a pasta deve ser esvaziada, para dar lugar a alguns outros arquivos. Se eu pular a parte ao escrever nesse arquivo, tudo bem e posso excluí-lo.
Jenny Smith
1
Você pode, por favor, embrulhar seu flush & close em um bloco finally? Sei que você fez isso e não funciona, mas esclarecerá sua pergunta.
bharal

Respostas:

99

Outro bug em Java. Eu raramente os encontro, apenas meu segundo em minha carreira de 10 anos. Esta é a minha solução, como outros mencionaram. Eu tenho nether usado System.gc(). Mas aqui, no meu caso, é absolutamente crucial. Esquisito? SIM!

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
fonte
7
Apesar de não ter aberto com nenhum tipo de stream (apenas fazendo um new File(path)), encontrei o mesmo problema e adicionando System.gc()antes de delete()fazer funcionar!
ixM
1
Qual é o outro bug?
bogdan.mustiata
não funciona para mim. O arquivo que não consigo excluir é um arquivo ZIP de um arquivo baixadoFromURL. O bom é que o arquivo baixado será excluído. Qualquer ideia?
Andrea_86
Funcionou para mim. E @andreadi nunca usei FileChannel.map em meu código. Parece que esse bug foi encerrado como sem solução, o que me deixa incrédulo porque o System.gctruque funciona sempre.
Adam Burley
Um único gc () pode não ajudarSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj
48

Foi muito estranho o truque que funcionou. A coisa é quando eu li anteriormente o conteúdo do arquivo, eu usei BufferedReader. Depois de ler, fechei o buffer.

Enquanto isso troquei e agora estou lendo o conteúdo usando FileInputStream. Também depois de terminar de ler, fecho o riacho. E agora está funcionando.

O problema é que não tenho explicação para isso.

Não sei BufferedReadere FileOutputStreamser incompatível.

Jenny Smith
fonte
4
Eu diria que é um bug então: java.sun.com/javase/6/docs/api/java/io/… Aqui diz que o método deve fechar o fluxo e quaisquer recursos associados a ele. Normalmente gosto de fechar todos os streams na sequência invertida (os últimos a serem abertos são os primeiros a serem fechados) usando IOUtils.closeQuietly, mas tende a ser um exagero.
Ravi Wallau
2
Eu estava tendo exatamente esse problema, eu e eu pensávamos que estava ficando louco. Obrigado pela solução!
Electrons_Ahoy
14
Estamos em 2011 com o JDK 7 e o problema ainda não foi resolvido. Estou tão feliz por ter encontrado este tópico - eu simplesmente não conseguia descobrir o que estava errado ...
David
Tive um problema muito chato com um arquivo que li e fechei e tentei excluir. Nada mencionado aqui ajudou. Então, depois de uma hora, descobri que eram apenas os direitos do arquivo, já que o arquivo havia sido criado por outro usuário :-D
runholen
Apenas uma facada no escuro ... Você pode ligar finalize()para garantir a limpeza? Ou talvez definido to = null? Consulte Forçando a finalização e a coleta de lixo .
jww
19

Tentei uma coisa simples e parece estar funcionando.

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

Funciona para mim.

Se isso não funcionar, tente executar seu aplicativo Java com sudo se estiver no Linux e como administrador no Windows. Apenas para ter certeza de que o Java tem direitos para alterar as propriedades do arquivo.

Kislay Sinha
fonte
1
Para este problema; geralmente as pessoas falam sobre definir referências como null ou chamar system.gc (); mas para mim, mesmo após a reinicialização do servidor, os arquivos não estavam sendo excluídos !! Mas file.setWritable (true); apenas funcionou ..
Deepak Singhal
5

Antes de tentar excluir / renomear qualquer arquivo, você deve garantir que todos os leitores ou gravadores (por exemplo: BufferedReader/ InputStreamReader/ BufferedWriter) estão devidamente fechados.

Quando você tenta ler / gravar seus dados de / para um arquivo, o arquivo é mantido pelo processo e não liberado até que a execução do programa seja concluída. Se você quiser realizar as operações de exclusão / renomeação antes do término do programa, deverá usar o close()método fornecido com as java.io.*classes.

Sai Manoj
fonte
Isso só é verdade no Windows. Em qualquer O / S real, você pode excluir e renomear arquivos abertos.
Archie
3

Como Jon Skeet comentou, você deve fechar seu arquivo no bloco finally {...}, para garantir que ele esteja sempre fechado. E, em vez de engolir as exceções com o e.printStackTrace, simplesmente não capture e adicione a exceção à assinatura do método. Se você não puder por algum motivo, pelo menos faça o seguinte:

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

Agora, pergunta número 2:

E se você fizer isso:

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

Você conseguiria excluir o arquivo?

Além disso, os arquivos são liberados quando são fechados. Eu uso IOUtils.closeQuietly (...), portanto, uso o método flush para garantir que o conteúdo do arquivo esteja lá antes de tentar fechá-lo (IOUtils.closeQuietly não lança exceções). Algo assim:

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

Portanto, sei que o conteúdo do arquivo está lá. Como geralmente é importante para mim que o conteúdo do arquivo seja escrito e não se o arquivo pode ser fechado ou não, realmente não importa se o arquivo foi fechado ou não. No seu caso, se for importante, eu recomendaria fechar o arquivo você mesmo e tratar todas as exceções de acordo.

Ravi Wallau
fonte
Tentei a primeira coisa - fechar o fluxo de saída no bloco finally. E não funcionou. O que aconteceu se eu tentasse ler depois disso é que recebi uma mensagem dizendo que o stream estava fechado. Ao alternar e ler o arquivo com FileInputReader, nada de "ruim" descrito acima aconteceu.
Jenny Smith
2

Não há nenhuma razão pela qual você não possa excluir este arquivo. Gostaria de ver quem tem controle sobre este arquivo. No unix / linux, você pode usar o utilitário lsof para verificar qual processo está bloqueado no arquivo. No Windows, você pode usar o explorador de processos.

para lsof, é tão simples quanto dizer:

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

para o explorador de processos, você pode usar o menu localizar e inserir o nome do arquivo para mostrar o identificador que irá apontar para o processo de bloqueio do arquivo.

aqui está um código que faz o que acho que você precisa fazer:

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 bem no OS X. Não testei no Windows, mas suspeito que deve funcionar no Windows também. Eu também admitirei ter visto algum comportamento inesperado no manuseio de arquivos wrt do Windows.

neesh
fonte
1
Para Windows, você pode baixar o Process Explorer gratuito no MS: technet.microsoft.com/en-us/sysinternals/bb896653.aspx
akarnokd
2

Se você estiver trabalhando no Eclipse IDE, isso pode significar que você não fechou o arquivo na inicialização anterior do aplicativo. Quando recebi a mesma mensagem de erro ao tentar excluir um arquivo, esse foi o motivo. Parece que o Eclipse IDE não fecha todos os arquivos após o término de um aplicativo.

Gangnus
fonte
1

Espero que isso ajude. Eu me deparei com um problema semelhante em que não consegui excluir meu arquivo depois que meu código Java fez uma cópia do conteúdo para a outra pasta. Após uma extensa pesquisa no Google, declarei explicitamente todas as variáveis ​​relacionadas à operação de arquivo e chamei o método close () de cada objeto de operação de arquivo e as defini como NULL. Em seguida, existe uma função chamada System.gc (), que limpará o mapeamento de i / o do arquivo (não tenho certeza, apenas digo o que é fornecido nos sites).

Aqui está meu código de exemplo:

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

a resposta é quando você carrega o arquivo, você precisa aplicar o método "close", em qualquer linha de código, funciona para mim

user3200921
fonte
0

Houve um problema uma vez no Ruby, onde os arquivos do Windows precisavam de um "fsync" para realmente poder voltar e reler o arquivo depois de gravá-lo e fechá-lo. Talvez seja uma manifestação semelhante (e se for, acho que é um bug do Windows, na verdade).

rogerdpack
fonte
0

Nenhuma das soluções listadas aqui funcionou na minha situação. Minha solução foi usar um loop while, tentando excluir o arquivo, com um limite de 5 segundos (configurável) para segurança.

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

Usar o loop acima funcionou sem ter que fazer nenhuma coleta de lixo manual ou definir o fluxo como nulo, etc.

etech
fonte
Qual SO você usa? Se você pode ter acesso aos STREAMS, feche-os, vai funcionar. Mas se o arquivo "já" estiver bloqueado, você tem um problema.
marcolopes de
1
-1 fazer um loop em seu código para excluir um arquivo não é uma boa ideia. Por que não apenas usar a opção gc () que funciona?
bharal
@bharal Aviso Eu declarei acima que nenhuma das outras soluções (incluindo gc ()) funcionou em meu caso particular. Embora eu concorde que usar um loop while não seja o ideal, pode ser uma solução apropriada em certas circunstâncias. Adicionar uma verificação adicional ao código acima, como uma variável de tempo limite ou iterações máximas, seria uma boa maneira de tornar o loop while mais seguro.
etech
@etech se o seu arquivo nunca existiu, você acabou de criar um loop infinito.
bharal
0

O problema pode ser que o arquivo ainda é visto como aberto e bloqueado por um programa; ou talvez seja um componente do seu programa no qual ele foi aberto, portanto, você deve garantir o uso do dispose()método para resolver esse problema. ieJFrame frame; .... frame.dispose();

Winnifred
fonte
0

Você deve fechar todos os streams ou usar o bloco 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
fonte
0

se file.delete () está enviando false, então na maioria dos casos o identificador do Bufferedreader não será fechado. Basta fechar e parece funcionar normalmente para mim.

Piyush
fonte
0

Eu tive o mesmo problema no Windows. Eu costumava ler o arquivo em scala linha por linha com

Source.fromFile(path).getLines()

Agora eu leio como um todo com

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

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

que fecha o arquivo corretamente após a leitura e agora

new File(path).delete

trabalho.

sbtpr
fonte
0

PARA Eclipse / NetBeans

Reinicie seu IDE e execute seu código novamente, este é apenas um trabalho de truque para mim depois de uma luta de uma hora.

Aqui está o meu código:

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

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

Resultado:

Excluir

Omore
fonte
0

Outro caso secundário em que isso pode acontecer: se você ler / gravar um arquivo JAR por meio de um URLe depois tentar excluir o mesmo arquivo dentro da mesma sessão 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!

A razão é que a lógica de manipulação de arquivo JAR interno do Java tende a armazenar JarFileentradas em cache :

// 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 cada um JarFile(em vez disso, a ZipFileestrutura subjacente ) teria um identificador para o arquivo, desde o momento da construção até close()ser invocado:

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;

Há uma boa explicação sobre esse problema do NetBeans .


Aparentemente, existem duas maneiras de "consertar" isso:

  • Você pode desativar o armazenamento em cache do arquivo JAR - para o atual URLConnectionou para todos os URLConnections futuros (globalmente) na sessão JVM atual:

    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!] Você pode limpar manualmente o JarFiledo cache quando terminar de usá-lo. O gerenciador de cache sun.net.www.protocol.jar.JarFileFactoryé um pacote privado, mas alguma mágica de reflexão pode fazer o trabalho para você:

    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.

Observação: tudo isso é baseado no codebase Java 8 ( 1.8.0_144); eles podem não funcionar com outras versões / posteriores.

Janaka Bandara
fonte