在一个类中打开Java文件流,在另一个类中关闭/删除文件。

3

我希望删除已打开并完成写入但未关闭的文件。请参考以下代码:

类A(不可更改):

import java.io.FileOutputStream;

public class A {

    public void run(String file) throws Exception {
        FileOutputStream s = new FileOutputStream(file);

    }
}

类B:

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class B {

    public static void main(String[] args) throws Exception {
        String path = "D:\\CONFLUX_HOME\\TestClient\\Maps\\test\\newTest.txt";
        A a = new A();
        a.run(path);
        File f = new File(path);
        Files.delete(Paths.get(f.getAbsolutePath()));

    }

}

在A类中,只需打开流而不关闭文件。 在B类中,调用A的run方法,然后尝试删除文件。 由于文件仍然处于打开状态,因此无法删除该文件。
错误信息为: 由于另一个进程正在使用该文件,因此无法访问该文件。
实际情况是: 我们正在动态加载jar文件。 jar文件内部的类正在创建文件。当出现异常时,会创建一个大小为0字节的文件。我们需要删除此文件。由于在异常期间文件未关闭,因此无法删除该文件。
如果我们可以关闭jar类中的流,则可以解决此问题,但我们无法修改创建文件的jar文件,因为它们是特定于客户端的jar文件。
请建议如何在不修改A类代码的情况下删除已打开的文件。

哪个操作系统? - John
@Sharath,我稍微改了一下问题的表述,以便更清楚地理解问题 - 请确认它仍然正确,否则请恢复我的编辑。 - CupawnTae
是的,Tomcat和JBoss可以在Windows和Unix上运行。 - Sharath
antiResourceLocking选项应该可以解决Windows上的文件问题。在Linux上,这种情况本来就不应该发生,因为类Unix操作系统通常不会自动锁定打开的文件或正在运行的程序。 - John
@Sharath,你有使用不同的文件名选项吗?例如,在文件名中添加时间戳或计数器。然后在将来的某个时候,找到并删除留下的零字节文件。 - CupawnTae
显示剩余4条评论
6个回答

0

确保在文件写入时发生异常时,也要关闭文件。

例如:

public void run(String file) throws Exception {
    FileOutputStream s = null;
    try {
        s = new FileOutputStream(file);    
    } finally {
        try {
            s.close();
        } catch(Exception e) {
            // log this exception
        }
    }
}

1
我的理解是 OP 不允许修改导致文件挂起的 JAR 文件,因此答案应该展示如何在有问题的 JAR 文件之外进行操作。 - John
1
@user3360241:那么问题提出者需要提供相关的代码。鉴于问题提出者提供的信息,答案已经尽可能地具体了。 - mastov
从问题中并不清楚是否是这种情况。如果他们没有访问代码的权限,那么我认为一个简单的解决方案是在删除文件之前等待流被垃圾回收。 - Armand
如果这些流被容器管理的单例引用,会出现问题吗? - John
类A不应该被修改。 - Sharath
显示剩余2条评论

0

根据OP的评论进行更新。 另一种方法是继承A并重写run()方法。

public static void main(String[] args) throws Exception  {
        String path = "D:\\CONFLUX_HOME\\TestClient\\Maps\\test\\newTest.txt";


        A a = new A() {
            @Override
            public void run(String file) throws Exception {
                FileOutputStream s = new FileOutputStream(file);
                s.close();
            }
        };
        a.run(path);

        File f = new File(path);

        Files.delete(Paths.get(f.getAbsolutePath()));
        System.out.println("foo");
    }

类 A 不应该被修改。 - Sharath
抱歉。也许有一种方法可以直接访问FileOutputStream,如果A类的封装性较弱的话。但在您的示例中,这种方法不起作用。 - dly
@Sharath 更新了答案。如果您被允许对A进行子类化,这可能是一个选项。 - dly

0
将资源作为参数传递,这样调用者就有责任清理资源。
   public void run(FileOutputStream stream) throws Exception {
     ...
   }

调用者:

try(FileStream stream = new FileStream(path)){
      A a = new A();
      a.run(stream);
}catch(Exception e){
  .. exception handling
}

0

在进行删除操作之前,您必须先关闭文件,首先这是一个不好的做法,其次它会导致内存泄漏。


0
如果您正在使用Tomcat,则可以在Windows的$CATALINA_HOME/conf/context.xml中设置AntiLockingOption和antiJARLocking。
<Context antiJARLocking="true" antiResourceLocking="true" >

重要提示:

antiResourceLocking选项可以防止JSP在编辑后需要重新部署。

了解更多关于此选项的信息: http://tomcat.apache.org/tomcat-7.0-doc/config/context.html

antiResourceLocking:

如果设置为true,Tomcat将防止任何文件锁定。这将显著影响应用程序的启动时间,但允许在可能发生文件锁定的平台或配置上进行完整的Web应用程序热部署和卸载。如果未指定,则默认值为false。


0

我认为你不会找到一个纯Java的解决方案来解决这个问题。一个选择是安装Unlocker(小心跳过所有垃圾软件),并从你的代码中调用它。

如果你启用了UAC,你还需要在提升的进程中运行你的Java(例如以管理员身份启动命令提示符)。然后,假设unlocker在C:\Program Files\Unlocker中:

Process p = new ProcessBuilder("c:\\Program Files\\Unlocker\\Unlocker.exe",path,"-s").start();
p.waitFor();

然后你可以像以前一样删除文件。或者你可以使用"-d"代替"-s",Unlocker会帮你删除文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接