如何确保文件已成功写入?

我正在为Java中的图形应用程序添加自动保存function。 应用程序定期自动保存当前文档,并在退出时自动保存。 当用户启动应用程序时,将重新加载自动保存文件。

如果自动保存文件以任何方式损坏(我假设文件处于保存状态时断电会这样做吗?),用户将失去工作。 如何防止这种情况并尽我所能保证自动保存文档处于一致状态?

更复杂的是,要自动保存文档,我需要保存一个.xml文件和几个.png文件。 此外,.png保存发生在JNI上的C代码中。

我当前的策略是使用扩展名.png.tmp编写每个.png,编写扩展名为.xml.tmp的.xml文件,然后重命名每个文件以删除.tmp部分,将.xml保留到最后。 在启动时,我只加载自动保存文档,如果我能找到.xml文件并忽略.xml.tmp文件。 在重命名新文档的.xml.tmp文件之前,我也不会删除以前的自动保存文档。

我想我对你写入磁盘时会发生什么的了解很少。 我知道在使用文件时,您可以拥有软件读/写缓冲区,以及操作系统和硬件缓冲区,并且所有这些都需要刷新。 我很困惑如何确切地知道什么东西真的被写入磁盘以及我可以做些什么来保护自己。 重命名操作是否可以确保刷新缓冲区?

顺便说一句,由于您有几个不同的文件作为这一个文档的一部分,考虑使用项目目录将它们全部保存在一起,或者使用一些封装格式(如.zip)将它们全部放在一个文件中。

你想要做的是用新的备份文件primefaces地替换旧的备份文件。 不幸的是,我不相信Java会给你足够的控制权。 您还需要推断底层操作系统中哪些操作是primefaces操作。 我知道Linux文件系统,所以我的答案将偏向于在该系统上运行的Java程序。 如果Windows没有做同样的事情我会感到震惊,但我不能肯定地说。

大多数Linux文件系统(例如元数据日志系统)允许您以primefaces方式重命名文件。 如果系统在重命名中途崩溃,那么当您重新启动时,就好像您从未重新命名过一个文件。 因此,以primefaces方式更新现有文件F的常用方法是将新数据写入临时文件T,然后将T重命名为F.任何系统或应用程序崩溃到该重命名都不会影响F,因此它将始终始终如一。

当然,在重命名之前,您需要确保临时文件是一致的。 确保将文件的所有流缓冲区刷新到OS( Channel.force()OutputStream.flush() ),并将OS缓冲区刷新到磁盘( FileOutputStream.getFD.sync() )。 当然,除非您的操作系统禁用硬盘本身的写缓存(可能没有),否则您的数据仍有可能被破坏。 如果您真的想要确定,请在XML中添加校验和。 如果你真的是偏执狂,你应该刷新操作系统和硬盘缓冲区缓存并重新读取文件以validation它是否一致。 这对于正常的消费者应用来说是超出任何合理的期望。

但这只是以primefaces方式写入单个文件。 您的问题更复杂:您有许多文件可以自动更新。 例如,我会说你有两个文件, img.pngmain.xml 。 我会做其中一个:

  1. 简单的解决方案是创建一个per-savefile目录。 您不必担心重命名每个单独的文件,并且您仍然可以primefaces地将新备份目录重命名为您要替换的旧备份目录。 也就是说,如果您的旧备份是bak / img.pngbak / main.xml ,请编写bak.tmp / img.pngbak.tmp / main.xml并将bak.tmp重命名为bak
  2. 将新的辅助文件命名为其他内容,并让它们与旧的辅助文件共存一段时间。 也就是说,编写img.2.pngmain.xml.tmp (应该引用img.2.png ,而不是img.png )并且只将main.xml.tmp重命名为main.xml 。 然后删除img.png
  3. 另外:如果你没有primefaces重命名,那么下一个最好的事情就是#2。 每当您保存项目时,请为其指定一个新名称(例如ver342.xml )。 加载时,只需查找最新的一致 XML(即其校验和validation)。 保持2或3左右是安全的。 如果已从较新的副本成功还原,则仅删除自动保存。