【问题标题】:file.delete() returns false even though file.exists(), file.canRead(), file.canWrite(), file.canExecute() all return true即使 file.exists()、file.canRead()、file.canWrite()、file.canExecute() 都返回 true,file.delete() 也会返回 false
【发布时间】:2010-11-02 18:03:22
【问题描述】:

我正在尝试删除一个文件,在其中写入一些内容后,使用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。

是不是我做错了什么?

【问题讨论】:

  • 我忘了说没有发现异常。
  • 您确定该文件没有被其他进程使用吗?你锁了吗?它可以与 deleteOnExit + exiting 一起使用吗?
  • 你在什么操作系统上运行?可以手动删除文件吗?某些东西可能持有该文件的打开句柄。
  • 我怎样才能知道它是否被另一个进程使用?我没锁。我不能使用 deleteOnExit 方法,因为我需要删除文件并在此之后继续我的应用程序。我的想法是我尝试清空一个临时文件夹,该文件夹用于为一个会话存储来自不同文件夹的文件。当我在我的应用程序上按下打开按钮时,这意味着必须清空文件夹,以便为其他文件腾出空间。如果我在写入该文件时跳过该部分,没关系,我可以删除它。
  • 你能把你的flush&close包在一个finally块中吗?我知道你已经这样做了,但它不起作用,但它会增加你的问题的清晰度。

标签: java file fileoutputstream


【解决方案1】:

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

【讨论】:

  • 虽然我没有用任何类型的流打开它(只是做一个new File(path)),我遇到了同样的问题,并在delete()之前添加System.gc()使它工作!
  • 不适合我。我无法删除的文件是下载的FromURL 文件的ZIP 文件。好消息是下载的文件将被删除。有什么想法吗?
  • 为我工作。 @andreadi 我从来没有在我的代码中使用过 FileChannel.map。似乎这个 bug 被关闭为无法解决,这让我难以置信,因为 System.gc 技巧每次都有效。
  • 单个 gc() 可能无济于事 System.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
  • 哇,感谢您发布您的解决方案,这对我帮助很大!从 JDK7 迁移到 JDK9 后,我遇到了“文件无法删除”的问题。与 JDK7 一起工作正常,不再与 JDK9 一起工作。在调用 [...].close() 之后调用 System.gc() 的顶部解决了这种奇怪的行为!我自己永远也想不通……我的意思是,为什么是 Java,为什么?
【解决方案2】:

这个办法很奇怪。问题是当我之前读过文件的内容时,我使用了BufferedReader。看完后,我关闭了缓冲区。

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

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

我不知道BufferedReaderFileOutputStream 不兼容。

【讨论】:

  • 我认为这是一个错误:java.sun.com/javase/6/docs/api/java/io/… 它在那里说该方法应该关闭流以及与之关联的任何资源。我通常喜欢使用 IOUtils.closeQuietly 以倒序关闭所有流(最后打开的流是最先关闭的),但这往往是矫枉过正。
  • 我遇到了这个确切的问题,我以为我快疯了。感谢您的解决方案!
  • JDK 7 是 2011 年,问题仍未解决。我很高兴我找到了这个帖子 - 我根本不知道出了什么问题......
  • 我有一个非常烦人的问题,我阅读并关闭了一个文件,然后尝试删除。这里没有提到任何帮助。然后一个小时后我发现这只是文件权限,因为该文件是由另一个用户创建的:-D
  • 只是在黑暗中刺伤...你能打电话给finalize() 以确保清理吗?或者设置to = null?见Forcing Finalization and Garbage Collection
【解决方案3】:

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

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

它对我有用。

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

【讨论】:

  • 针对这个问题;通常人们谈论将引用设置为 null 或调用 system.gc();但对我来说,即使在服务器重启后文件也没有被删除!!但是 file.setWritable(true);刚刚工作..
【解决方案4】:

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

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

【讨论】:

  • 这仅适用于 Windows。在任何真正的操作系统上,您都可以删除和重命名打开的文件。
【解决方案5】:

正如 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 切换和读取文件时,没有发生上述“坏”的事情。
【解决方案6】:

您没有理由不能删除此文件。我想看看谁持有这个文件。在 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 上看到了一些意想不到的行为。文件处理。

【讨论】:

【解决方案7】:

如果您在 Eclipse IDE 中工作,这可能意味着您在上次启动应用程序时没有关闭该文件。当我在尝试删除文件时收到相同的错误消息时,这就是原因。看来,Eclipse IDE 在应用程序终止后不会关闭所有文件。

【讨论】:

    【解决方案8】:

    希望这会有所帮助。我遇到了类似的问题,在我的 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();
        }
    }
    

    【讨论】:

      【解决方案9】:

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

      【讨论】:

        【解决方案10】:

        曾经在 ruby​​ 中出现过一个问题,Windows 中的文件需要“fsync”才能在写入和关闭文件后真正转身并重新读取文件。也许这是一个类似的表现(如果是这样,我认为是一个 Windows 错误,真的)。

        【讨论】:

          【解决方案11】:

          此处列出的解决方案均不适用于我的情况。我的解决方案是使用 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 等即可。

          【讨论】:

          • 什么 S.O.你用吗?如果您可以访问 STREAMS,请关闭它们,它将起作用。但是如果文件“已经”被锁定了,那么你就有问题了。
          • -1 在代码中循环删除文件并不是一个好主意。为什么不直接使用有效的 gc() 选项?
          • @bharal 注意我在上面声明了其他解决方案(包括 gc())在我的特定情况下都不起作用。虽然我同意使用 while 循环并不理想,但在某些情况下它可能是一个合适的解决方案。在上面的代码中添加额外的检查,例如超时或最大迭代变量,将是使 while 循环更安全的好方法。
          • @etech 如果您的文件从未存在过,那么您只是创建了一个无限循环。
          【解决方案12】:

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

          【讨论】:

            【解决方案13】:

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

            【讨论】:

              【解决方案14】:

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

              【讨论】:

                【解决方案15】:

                我在 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
                

                有效。

                【讨论】:

                  【解决方案16】:

                  对于 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");
                    }
                  }
                  

                  输出:

                  删除

                  【讨论】:

                    【解决方案17】:

                    另一种可能发生这种情况的极端情况:如果您通过 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上有很好的解释。


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

                    • 您可以禁用 JAR 文件缓存 - 为当前的 URLConnection 或当前 JVM 会话中所有未来的 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);它们可能不适用于其他/更高版本。

                    【讨论】:

                      猜你喜欢
                      • 2013-02-26
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-01-14
                      • 2018-02-17
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-08-11
                      相关资源
                      最近更新 更多