file.delete()返回false,即使file.exists(),file.canRead(),file.canWrite(),file.canExecute()都返回true

问题描述 投票:86回答:17

我正在尝试使用FileOutputStream删除文件。这是我用来编写的代码:

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

如图所示,我刷新并关闭流,但是当我尝试删除时,file.delete()返回false。

我在删除前检查了文件是否存在,并且:file.exists()file.canRead()file.canWrite()file.canExecute()都返回true。在调用这些方法后,我尝试file.delete()并返回false。

有什么我做错了吗?

java file fileoutputstream
17个回答
95
投票

Java中的另一个错误。我很少找到他们,只是我10年职业生涯中的第二个。正如其他人所提到的,这是我的解决方案。我有幽冥使用System.gc()。但就我而言,这绝对是至关重要的。奇怪的?是!

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

0
投票

在ruby中有一个问题,Windows中的文件需要一个“fsync”来实际上能够转换并在写入并关闭它之后重新读取该文件。也许这是一个类似的表现(如果是这样,我认为一个Windows bug,真的)。


0
投票

这里列出的解决方案都不适用于我的情况。我的解决方案是使用while循环,尝试删除文件,安全性限制为5秒(可配置)。

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

使用上面的循环工作,无需进行任何手动垃圾收集或将流设置为null等。


0
投票

问题可能是文件仍被视为打开并被程序锁定;或者它可能是程序中已打开的组件,因此您必须确保使用dispose()方法来解决该问题。即JFrame frame; .... frame.dispose();


0
投票

您必须关闭所有流或使用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
投票

如果file.delete()发送false,那么在大多数情况下,您的Bufferedreader句柄将不会被关闭。刚关闭,它似乎通常对我有用。


0
投票

我在Windows上遇到了同样的问题。我曾经逐行读取scala中的文件

Source.fromFile(path).getLines()

现在我把它作为一个整体阅读

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

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

在阅读后现在正确关闭文件

new File(path).delete

作品。


0
投票

对于Eclipse / NetBeans

重新启动你的IDE并再次运行你的代码这只是经过一个小时的努力后才对我有用。

这是我的代码:

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

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

输出:

删除


0
投票

另一种可能发生的极端情况:如果您通过URL读取/写入JAR文件,稍后尝试在同一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!

原因是Java的内部JAR文件处理逻辑,倾向于缓存JarFile条目:

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

每个JarFile(相反,底层的ZipFile结构)将保持文件的句柄,从构建时直到close()被调用:

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;

this NetBeans issue有一个很好的解释。


显然有两种方法来“修复”这个:

  • 您可以在当前JVM会话中禁用JAR文件缓存 - 对于当前的URLConnection或所有将来的URLConnections(全局): 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!]完成后,您可以从缓存中手动清除JarFile。缓存管理器sun.net.www.protocol.jar.JarFileFactory是包私有的,但是一些反射魔法可以为你完成工作: 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.

请注意:所有这些都基于Java 8代码库(1.8.0_144);它们可能无法与其他/更高版本一起使用。


46
投票

这个诀窍很奇怪。事情是,当我以前读过文件的内容时,我使用了BufferedReader。读完后,我关闭了缓冲区。

与此同时,我改变了,现在我正在使用FileInputStream阅读内容。完成阅读后,我关闭了流。现在它正在发挥作用。

问题是我对此没有解释。

我不知道BufferedReaderFileOutputStream是不相容的。


17
投票

我尝试过这个简单的事情似乎很有效。

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

这个对我有用。

如果这不起作用,请尝试使用sudo运行Java应用程序(如果在Linux上)和管理员运行Windows时。只是为了确保Java有权更改文件属性。


5
投票

在尝试删除/重命名任何文件之前,必须确保所有读者或编写者(例如:BufferedReader / InputStreamReader / BufferedWriter)已正确关闭。

当您尝试从/向文件读取/写入数据时,该文件由进程保留,并且在程序执行完成之前不会释放。如果要在程序结束之前执行删除/重命名操作,则必须使用close()类附带的java.io.*方法。


3
投票

正如Jon Skeet评论的那样,您应该在finally {...}块中关闭文件,以确保它始终关闭。并且,不要使用e.printStackTrace吞下异常,而只是不捕获并将异常添加到方法签名中。如果你出于任何原因不能,至少这样做:

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

现在,问题编号#2:

如果你这样做怎么办:

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

你能删除这个文件吗?

此外,文件在关闭时会刷新。我使用IOUtils.closeQuietly(...),所以我使用flush方法确保在我尝试关闭它之前文件的内容在那里(IOUtils.closeQuietly不会抛出异常)。像这样的东西:

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

所以我知道文件的内容在那里。因为通常对我来说文件的内容是写的而不是文件是否可以关闭,所以文件是否关闭并不重要。在您的情况下,重要的是,我建议您自己关闭文件并根据情况处理任何异常。


2
投票

您没有理由不能删除此文件。我会看看谁对这个文件有所保留。在unix / linux中,您可以使用lsof实用程序来检查哪个进程对文件具有锁定。在Windows中,您可以使用进程资源管理器。

对于lsof来说,就像说:

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

对于进程资源管理器,您可以使用查找菜单并输入文件名来显示句柄,该句柄将指向锁定文件的进程。

这里有一些代码可以完成我认为你需要做的事情:

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

它在OS X上工作正常。我没有在Windows上测试它,但我怀疑它也适用于Windows。我也承认在Windows w.r.t上看到了一些意想不到的行为。文件处理。


2
投票

如果您在Eclipse IDE中工作,则可能意味着您尚未在先前启动的应用程序中关闭该文件。当我在尝试删除文件时遇到相同的错误消息时,原因就在于此。看来,Eclipse IDE在应用程序终止后不会关闭所有文件。


1
投票

希望这会有所帮助。我遇到了类似的问题,我的java代码将内容的副本复制到另一个文件夹后,我无法删除我的文件。在广泛的谷歌搜索之后,我显式地声明了每个文件操作相关的变量,并调用每个文件操作对象的close()方法,并将它们设置为NULL。然后,有一个名为System.gc()的函数,它将清除文件i / o映射(我不确定,我只是告诉网站上给出的内容)。

这是我的示例代码:

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
投票

答案是当你加载文件时,你需要应用“关闭”方法,在任何代码行中,对我有用

© www.soinside.com 2019 - 2024. All rights reserved.