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

发布于 2024-11-27 06:02:48 字数 752 浏览 1 评论 0原文

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

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

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

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

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

在我们尝试过的 catch 中

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

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

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

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

非常感谢

We are validating XML files and depending on the result of the validation we have to move the file into a different folder.

When the XML is valid the validator returns a value and we can move the file without a problem. Same thing happens when the XML is not valid according to the schema.

If however the XML is not well formed the validator throws an exception and when we try to move the file, it fails. We believe there is still a handle in the memory somewhere that keeps hold of the file. We tried putting System.gc() before moving the file and that sorted the problem but we can't have System.gc() as a solution.

The code looks like this. We have a File object from which we create a StreamSource. The StreamSource is then passed to the validator. When the XML is not well formed it throws a SAXException. In the exception handling we use the .renameTo() method to move the file.

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

In the catch we tried

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

but still .renameTo() is not able to move the file. If we put System.gc() in the catch, the move will succeed.

Can someone enlight me how to sort this without System.gc()?

We use JAXP and saxon-9.1.0.8 as the parser.

Many thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

亣腦蒛氧 2024-12-04 06:02:48

尝试创建一个 FileInputStream 并将其传递到 StreamSource 中,然后在完成后关闭 FileInputStream。通过传入File,您将失去对如何/何时关闭文件句柄的控制。

Try creating a FileInputStream and passing that into StreamSource then close the FileInputStream when you're done. By passing in a File you have lost control of how/when to close the file handle.

甜心 2024-12-04 06:02:48

当您设置 sc = null 时,您向垃圾收集器指示 StreamSource 文件不再被使用,并且可以对其进行收集。流在其 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 中,您可以使用新的 尝试使用资源块。

When you set sc = null, you are indicating to the garbage collector that the StreamSource file is no longer being used, and that it can be collected. Streams close themselves in their destroy() method, so if they are garbage collected, they will be closed, and therefore can be moved on a Windows system (you will not have this problem on a Unix system).

To solve the problem without manually invoking the GC, simply call sc.getInputStream().close() before sc = null. This is good practice anyway.

A common pattern is to do a try .. finally block around any file handle usage, eg.

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

In Java 7, you can instead use the new try with resources block.

不甘平庸 2024-12-04 06:02:48

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

Try sc.getInputStream().close() in the catch

茶底世界 2024-12-04 06:02:48

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

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

然而,由于 System.gc() 并不是 JVM 实际执行一轮垃圾收集所必需的,并且即使它是 JVM 也不会被强制删除所有可能的垃圾对象,因此您没有真正的方法来确保该文件可以“立即”删除。

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

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

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

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

通常,“滞后”约为几毫秒,但有一些例外,文件的存活时间会更长一些。

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

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

All the three answers already given are right : you must close the underlying stream, either with a direct call to StramSource, or getting getting the stream and closing it, or creating the stream yourself and closing it.

However, I've already seen this happening, under windows, since at least three years : even if you close the stream, really every stream, if you try to move or delete the file, it will throw exception .. unless ... you explicitly call System.gc().

However, since System.gc() is not mandatory for a JVM to actually execute a round of garbage collection, and since even if it was the JVM is not mandated to remove all possible garbage object, you have no real way of being sure that the file can be deleted "now".

I don't have a clear explanation, I can only imagine that probably the windows implementation of java.io somehow caches the file handle and does not close it, until the handle gets garbage collected.

It has been reported, but I haven't confirmed it, that java.nio is not subject to this behavior, cause it has more low level control on file descriptors.

A solution I've used in the past, but which is quite a hack, was to :

  1. Put files to delete on a "list"
  2. Have a background thread check that list periodically, calla System.gc and try to delete those files.
  3. Remove from the list the files you managed to delete, and keep there those that are not yet ready to.

Usually the "lag" is in the order of a few milliseconds, with some exceptions of files surviving a bit more.

It could be a good idea to also call deleteOnExit on those files, so that if the JVM terminates before your thread finished cleaning some files, the JVM will try to delete them. However, deleteOnExit had it's own bug at the time, preventing exactly the removal of the file, so I didn't. Maybe today it's resolved and you can trust deleteOnExit.

This is the JRE bug that i find most annoying and stupid, and cannot believe it is still in existence, but unfortunately I hit it just a month ago on windows Vista with latest JRE installed.

分開簡單 2024-12-04 06:02:48

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

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

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

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

这里是关键点。不管是什么原因,它都与 Windows 锁定文件的烦人行为有关。

Pretty old, but some people may still find this question.

  1. I was using Oracle Java 1.8.0_77.
  2. The problem occurs on Windows, not on Linux.
  3. The StreamSource instanciated with a File seems to automatically allocate and release the file resource when processed by a validator or transformer. (getInputStream() returns null)
  4. On Windows moving a file into the place of the source file (deleting the source file) after the processing is not possible.

Solution/Workaround: Move the file using

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

The use of ATOMIC_MOVE here is the critical point. Whatever the reason ist, it has something to do with the annoying behavior of Windows locking files.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文