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

时间:2009-06-13 20:51:15

标签: java file fileoutputstream

我正在尝试使用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。

我做错了什么?

17 个答案:

答案 0 :(得分:94)

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

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

答案 1 :(得分:45)

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

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

问题是我对此没有解释。

我不知道BufferedReaderFileOutputStream是不兼容的。

答案 2 :(得分:17)

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

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

它对我有用。

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

答案 3 :(得分:5)

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

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

答案 4 :(得分: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);
}

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

答案 5 :(得分:2)

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

答案 6 :(得分: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上看到了一些意想不到的行为。文件处理。

答案 7 :(得分:1)

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

答案 8 :(得分: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();
    }
}

答案 9 :(得分: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等等。

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

答案 12 :(得分: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;
}

答案 13 :(得分:0)

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

答案 14 :(得分: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

作品。

答案 15 :(得分:0)

  

FOR Eclipse / NetBeans

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

这是我的代码:

{{1}}

<强>输出:

删除

答案 16 :(得分: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上有很好的解释。


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

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