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


90

我正在尝试使用写入文件后删除文件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()一切回归真实。在调用这些方法之后,我尝试file.delete()返回false。

我做错了什么吗?


我忘了提到,没有例外。
珍妮·史密斯2009年

您确定文件没有被其他进程使用吗?你锁了吗?它可以与deleteOnExit +退出一起使用吗?
instanceof me

您正在运行什么操作系统?可以手动删除文件吗?某些东西可能会打开文件的句柄。
akarnokd

我如何找出它是否被其他进程使用?我没有锁。我不能使用deleteOnExit方法,因为我需要删除该文件并在此之后继续应用。我的想法是我尝试清空一个临时文件夹,该文件夹用于在一个会话中存储来自其他文件夹的文件。当我按申请中的打开按钮时,这意味着必须清空该文件夹,以便为其他文件腾出空间。如果我在写入该文件时跳过了该部分,就可以了,可以删除它。
珍妮·史密斯,2009年

1
您能否将冲洗和关闭包装在finally块中?我知道您已经完成了此操作,但是它不起作用,但是可以使您的问题更加清楚。
bharal

Answers:


99

Java中的另一个错误。我很少找到他们,只有我十年来的第二个。正如其他人所提到的,这是我的解决方案。我曾经用过一点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();
    }
}

7
尽管我没有使用任何流打开它(只是执行new File(path)),但是我遇到了同样的问题,并System.gc()delete()使其起作用之前添加了它!
ixM 2013年

1
另一个错误是什么?
bogdan.mustiata

不适合我。我无法删除的文件是downloadedFromURL文件的ZIP文件。好消息是下载的文件将被删除。任何想法?
Andrea_86

为我工作。而且@andreadi我从未在代码中使用FileChannel.map。似乎此错误已解决为无法解决,这使我难以置信,因为该System.gc技巧每次都起作用。
亚当·伯利

单个gc()可能无济于事System.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj

48

起作用的把戏很奇怪。事情是,当我以前读取文件的内容时,我使用BufferedReader。阅读后,我关闭了缓冲区。

同时我切换了,现在我正在使用读取内容FileInputStream。同样在完成阅读后,我关闭了流。现在它正在工作。

问题是我没有对此的解释。

我不知道BufferedReader并且FileOutputStream不兼容。


4
那时我认为这是一个错误: java.sun.com/javase/6/docs/api/java/io/… 在那儿说,该方法应该关闭流和与之关联的任何资源。我通常喜欢使用IOUtils.closeQuietly关闭反向顺序中的所有流(最后一个打开的流是第一个关闭的流),但是这往往会导致过度杀伤。
拉维·瓦劳

2
我遇到了这个确切的问题,我以为我快疯了。感谢您的解决方案!
Electrons_Ahoy

14
JDK 7是2011年,问题仍然没有解决。我很高兴我找到了这个线程-我根本不知道出什么问题了……
大卫,

我在读取并关闭然后试图删除的文件时遇到了一个非常烦人的问题。这里没有提到任何帮助。然后一个小时后,我发现这只是文件权限,因为该文件是由另一个用户创建的:-D
runholen 2014年

只是在黑暗中刺伤...您能打电话finalize()来确保清理吗?还是定了to = null?请参阅强制完成和垃圾回收
jww 2014年

19

我尝试了这个简单的事情,它似乎正在工作。

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

这个对我有用。

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


1
对于这个问题;通常人们谈论将引用设置为null或调用system.gc();但是对我来说,即使在服务器重启后文件也没有被删除!但是file.setWritable(true); 刚刚工作..
Deepak Singhal 2012年

6

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

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


1
仅在Windows上如此。在任何实际的操作系统上,您都可以删除和重命名打开的文件。
阿奇

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

因此,我知道文件的内容就在其中。因为对我来说,通常要写入文件的内容,而不是是否可以关闭文件,所以,无论是否关闭文件都没有关系。对于您而言,这很重要,我建议您自己关闭文件并根据其处理任何异常。


我尝试了第一件事-在finally块中关闭输出流。而且它没有用。如果在那之后尝试阅读,发生的事情是我收到一条消息,指出流已关闭。使用FileInputReader切换和读取文件时,上述“坏”事情都没有发生。
珍妮·史密斯,2009年

2

没有理由您不应该删除该文件。我希望看看谁拥有此文件。在unix / linux中,可以使用lsof实用程序来检查哪个进程对该文件进行了锁定。在Windows中,您可以使用Process Explorer。

对于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 wrt文件处理上看到了一些意外行为。


1
对于Windows,您可以从MS下载免费的Process Explorer:technet.microsoft.com/zh-cn/sysinternals/bb896653.aspx
akarnokd 2009年

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

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


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等。


您使用什么?如果您可以访问STREAMS,请将其关闭,即可使用。但是,如果文件“已”被锁定,那么您就有问题了。
marcolopes

1
-1循环执行代码以删除文件不是一个好主意。为什么不只使用有效的gc()选项?
bharal

@bharal注意,我在上面指出,在我的特定情况下,其他解决方案(包括gc())均无效。尽管我同意使用while循环并不理想,但在某些情况下它可能是一个合适的解决方案。在上面的代码中添加额外的检查,例如超时或最大迭代变量,将是使while循环更安全的好方法。
etech

@etech如果您的文件不存在,则您刚刚创建了一个无限循环。
bharal

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

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

关于此NetBeans问题,有很好的解释。


显然,有两种方法可以“解决”此问题:

  • 您可以为当前JVM URLConnectionURLConnection当前JVM会话中的所有future (全局)禁用JAR文件缓存:

    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); 它们可能不适用于其他/更高版本。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.