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

时间:2011-08-01 15:44:49

标签: java io saxon jaxp

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

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

但是,如果XML格式不正确,验证器会抛出异常,当我们尝试移动文件时,它会失败。我们相信在某个地方仍然有一个句柄可以保存文件。我们在移动文件之前尝试放置System.gc()并对问题进行排序,但我们不能将System.gc()作为解决方案。

代码看起来像这样。我们有一个File对象,我们从中创建一个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作为解析器。

非常感谢

5 个答案:

答案 0 :(得分:5)

尝试创建FileInputStream并将其传递到StreamSource,然后在完成后关闭FileInputStream。通过传入File,您无法控制关闭文件句柄的方式/时间。

答案 1 :(得分:3)

当您设置sc = null时,您向垃圾收集器指示StreamSource文件已不再使用,并且可以收集它。 Streams在他们的destroy()方法中关闭自己,所以如果它们被垃圾收集,它们将被关闭,因此可以在Windows系统上移动(在Unix系统上你不会遇到这个问题)。

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

常见的模式是围绕任何文件句柄使用try .. finally块,例如

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

在Java 7中,您可以使用新的try with resources块。

答案 2 :(得分:1)

在catch

中尝试sc.getInputStream()。close()

答案 3 :(得分:1)

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

但是,我已经在windows下看到过这种情况至少三年了:即使你关闭了流,真的是每一个流,如果你试图移动或删除文件,它都会抛出异常..除非...你明确地调用了System.gc()。

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

我没有明确的解释,我只能想象,java.io的windows实现可能会以某种方式缓存文件句柄并且不会关闭它,直到句柄被垃圾收集。

据报道,但我还没有确认,java.nio不受此行为影响,因为它对文件描述符有更低级别的控制。

我过去使用过的解决方案是:

  1. 将文件删除到“列表”
  2. 让后台线程定期检查该列表,calla System.gc并尝试删除这些文件。
  3. 从列表中删除您设法删除的文件,并保留那些尚未准备好的文件。
  4. 通常情况下,“滞后”大约是几毫秒,文件的一些例外可以存活一些。

    也可以在这些文件上调用deleteOnExit,这样如果JVM在线程完成清理某些文件之前终止,JVM将尝试删除它们。但是,deleteOnExit当时有它自己的bug,完全阻止了文件的删除,所以我没有。也许今天已经解决了,你可以信任deleteOnExit。

    这是我发现最烦人和愚蠢的JRE错误,并且不能相信它仍然存在,但不幸的是我在一个月前在安装了最新JRE的Windows Vista上点击它。

答案 4 :(得分:0)

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

  1. 我使用的是Oracle Java 1.8.0_77。
  2. 问题出在Windows上,而不是Linux上。
  3. StreamSource实例化的File似乎在验证器或转换器处理时自动分配和释放文件资源。 (getInputStream()返回null
  4. 在Windows上无法处理后将文件移动到源文件的位置(删除源文件)。
  5. 解决方案/解决方法:使用

    移动文件
    Files.move(from.toPath(), to.toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
    

    这里使用ATOMIC_MOVE是关键点。无论原因是什么,它都与Windows锁定文件的恼人行为有关。