验证失败后移动文件(Java)

验证失败后移动文件(Java),java,io,saxon,jaxp,Java,Io,Saxon,Jaxp,我们正在验证XML文件,根据验证结果,我们必须将该文件移动到另一个文件夹中 当XML有效时,验证器返回一个值,我们可以毫无问题地移动文件。根据模式,当XML无效时也会发生同样的情况 但是,如果XML格式不正确,验证程序会抛出一个异常,当我们尝试移动文件时,它会失败。我们相信在内存中的某个地方仍然有一个控制文件的句柄。我们尝试在移动文件之前放置System.gc(),这解决了问题,但我们无法将System.gc()作为解决方案 代码如下所示。我们有一个文件对象,从中创建StreamSource。然

我们正在验证XML文件,根据验证结果,我们必须将该文件移动到另一个文件夹中

当XML有效时,验证器返回一个值,我们可以毫无问题地移动文件。根据模式,当XML无效时也会发生同样的情况

但是,如果XML格式不正确,验证程序会抛出一个异常,当我们尝试移动文件时,它会失败。我们相信在内存中的某个地方仍然有一个控制文件的句柄。我们尝试在移动文件之前放置System.gc(),这解决了问题,但我们无法将
System.gc()
作为解决方案

代码如下所示。我们有一个文件对象,从中创建StreamSource。然后将StreamSource传递给验证器。当XML格式不正确时,它抛出SAXException。在异常处理中,我们使用.renameTo()方法移动文件

sc = new StreamSource(xmlFile);
validator.validate(sc);
在我们尝试的接球中

validator.reset();
validator=null;
sc=null;
但是,
.renameTo()
仍然无法移动文件。如果我们把
System.gc()
放在陷阱中,移动就会成功

有人能告诉我如何在没有
System.gc()的情况下对其进行排序吗?

我们使用JAXP和saxon-9.1.0.8作为解析器


非常感谢

当您设置
sc=null
时,您向垃圾收集器指示StreamSource文件不再被使用,并且可以被收集。流在其
destroy()
方法中自行关闭,因此,如果它们被垃圾收集,它们将被关闭,因此可以在Windows系统上移动(在Unix系统上不会出现此问题)

要在不手动调用GC的情况下解决此问题,只需在
sc=null
之前调用
sc.getInputStream().close()
。无论如何,这是一个很好的做法

一种常见的模式是进行
try。。最后
阻止任何文件句柄的使用,例如

try {
    sc = new StreamSource(xmlFile);
    // check stuff
} finally {
    sc.getInputStream().close();
}
// move to the appropriate place

在Java 7中,您可以改为使用新块。

在catch中尝试sc.getInputStream().close()

尝试创建
FileInputStream
并将其传递到
StreamSource
中,然后在完成后关闭
FileInputStream
。通过传入
文件
您已经无法控制如何/何时关闭文件句柄。

已经给出的三个答案都是正确的:您必须关闭底层流,或者直接调用StramSource,或者获取流并关闭它,或者自己创建流并关闭它

然而,我已经在windows下看到了这种情况,至少三年了:即使你关闭了流,实际上是每个流,如果你试图移动或删除文件,它也会抛出异常。。除非。。。您可以显式调用System.gc()

但是,由于System.gc()不是JVM实际执行一轮垃圾收集的强制要求,而且即使JVM没有被强制删除所有可能的垃圾对象,您也没有真正的方法确保文件可以“立即”删除

我没有一个清楚的解释,我只能想象java.io的windows实现可能会以某种方式缓存文件句柄,直到句柄被垃圾回收后才关闭它

据报道,但我还没有证实,java.nio不受这种行为的影响,因为它对文件描述符有更低级的控制

我过去曾使用过一个解决方案,但这是一个相当棘手的问题,它是:

  • 将要删除的文件放在“列表”上
  • 让后台线程定期检查该列表,调用System.gc并尝试删除这些文件
  • 从列表中删除您设法删除的文件,并保留那些尚未准备好删除的文件
  • 通常情况下,“延迟”大约为几毫秒,有些例外情况是文件存活时间稍长

    对这些文件也调用deleteOnExit可能是一个好主意,这样,如果JVM在线程清理完一些文件之前终止,JVM将尝试删除它们。然而,deleteOnExit当时有它自己的bug,完全阻止了文件的删除,所以我没有。也许今天它已经解决了,你可以相信deleteOnExit


    这是我觉得最烦人、最愚蠢的JRE错误,我无法相信它仍然存在,但不幸的是,我一个月前在windows Vista上发现了它,安装了最新的JRE。

    相当老,但有些人可能仍然会发现这个问题

  • 我使用的是Oracle Java 1.8.077
  • 问题发生在Windows上,而不是Linux上
  • 文件
    关联的
    流源
    在由验证器或转换器处理时似乎会自动分配和释放文件资源。(
    getInputStream()
    返回
    null
  • 在Windows上,无法在处理后将文件移动到源文件的位置(删除源文件) 解决方案/解决方法:使用

    Files.move(从.toPath()移动到.toPath(),替换现有的、原子的移动);
    

    原子移动的使用这里是关键点。不管是什么原因,这都与Windows锁定文件的恼人行为有关。

    我认为StreamSource没有关闭方法…?谢谢@stefan,但我看不到StreamSource的.close()方法。我喜欢完整回答问题的方式,以及额外的有用信息,并在原始问题范围之外的伪代码中对琐碎的学究投了否决票。这是一个很棒的社区。你被否决了,因为你提供的第一个和第二个答案/编辑有误导性/错误,而且它仍然令人困惑,因为你仍然在引用不存在的
    sc.close()
    。谢谢antlersoft,这是有道理的,但我刚刚尝试过,但不幸的是,它不起作用。值得一试——你试过Mike Q的解决方案了吗?它应该能让你更好地控制开口