validation失败后移动文件(Java)

我们正在validationXML文件,根据validation结果,我们必须将文件移动到不同的文件夹中。

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

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

代码看起来像这样。 我们有一个File对象,我们从中创建一个StreamSource。 然后将StreamSource传递给validation器。 当XML格式不正确时,它会抛出SAXException。 在exception处理中,我们使用.renameTo()方法来移动文件。

 sc = new StreamSource(xmlFile); validator.validate(sc); 

我们尝试过捕获

 validator.reset(); validator=null; sc=null; 

但仍然.renameTo()无法移动文件。 如果我们将System.gc()放入catch中,那么移动将成功。

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

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

非常感谢

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

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

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

一个常见的模式是try .. finally阻止任何文件句柄使用,例如。

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

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

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

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

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

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

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

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

我过去使用过的解决方案,但是这个解决方案是:

  1. 将文件放在“列表”上删除
  2. 让后台线程定期检查该列表,calla System.gc并尝试删除这些文件。
  3. 从列表中删除您设法删除的文件,并保留那些尚未准备好的文件。

通常,“滞后”大约是几毫秒,文件的一些例外可以存活一些。

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

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

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

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

解决方案/解决方法:使用移动文件

 Files.move(from.toPath(), to.toPath(), REPLACE_EXISTING, ATOMIC_MOVE); 

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