保证一条信息一次存储在一个地方

时间:2011-03-14 15:06:24

标签: delphi

在应用程序关闭时,我想通过Internet(使用Indy)将文件发送到服务器。如果数据发送正常,我想从计算机中删除该文件。关键部分是我的文件不应同时存储在两个地方(服务器,本地计算机)。

例如,我的应用程序在将文件发送到服务器之后可能会意外停止,并且在从本地磁盘删除文件之前。在这种情况下,文件将存在于两个地方。

由于以下原因,应用程序可能会停止:电源故障,Control + Alt + Del,操作系统关闭,用户关闭,系统挂起(可能还有其他原因我忘了?)。

如何保证文件存储在一个地方?


我认为我们可以考虑将文件写入磁盘是即时的,因为文件非常小。

5 个答案:

答案 0 :(得分:2)

不,没有保证。操作系统和控制操作系统的最终用户总是可以终止进程。但是,操作系统通常不会尝试这样的事情,而且用户肯定不会在不知不觉中杀死你的应用程序。

您可以在关机期间运行您喜欢的任何代码(例如,在Form1Close中)。但是,如果您运行某些代码而不处理消息几秒钟,Windows可能会认为您的应用程序被冻结,并询问用户是否希望将其删除。因此,与往常一样,您应该在自己的线程中执行“慢”代码,以便主应用程序线程仍然具有响应性。 [但是如果最终用户没有要求Windows终止该进程,它可以无限期地运行,即使它表现不佳。]

此外,如果您希望此代码真正运行几秒钟,您最好告诉最终用户发生了什么。例如,您可以显示please wait窗口。

如果我是你,我会在主表单中写一个OnCloseQuery处理程序。这个设置CanClose := false,显示状态窗口,并启动关闭线程。当该线程完成时,它将关闭主表单(例如通过向其发送消息)。 OnCloseQuery处理程序还应检查关闭线程是否正在运行。如果是这样,它应该只设置CanClose := false但不能启动(另一个)关闭线程。例如,如果最终用户反复单击关闭框,则会发生这种情况。我认为OnCloseQuery过程将在线程启动主窗体的关闭时运行。这次它应该正常关闭。您可以通过编写一些代码告诉OnCloseQuery处理程序它是由线程启动的,或者在线程完成时设置ShutdownThreadComplete := true标志来实现这一点。因此,您将执行CanClose := ShutdownThreadComplete

这样的事情:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if not ShutdownThreadStarted then
  begin
    CreatePleaseWaitMessage;
    StartShutdownThread;
    ShutdownThreadStated := true;
    CanClose := false;
    Exit;
  end;

  CanClose := ShutdownThreadComplete;
end;

答案 1 :(得分:1)

您可以使用以下Windows API调用来确保将数据写入磁盘:

var 
  F: TFileStream;

begin
  F := TFileStream.Create(....
  try
    F.Write(...)
  (...)
    FlushFileBuffers(F.Handle); // this will flush the content to disk
  finally
    F.Free;
  end;
end;

请参阅the official documentation for this API

附加说明:

在将文件发送到磁盘之前,切勿删除文件内容。 ACID行为的一个公理是保留旧数据,直到新数据可用。对你来说也一样。

一种可能性是后台进程。您可以向后台进程发送消息(作为轻型Windows服务运行,例如),然后退出客户端应用程序。后台进程将负责将文件发送到服务器,并且已从服务器接收到重复单元确认。然后后台进程将能够删除该文件,并等待下一个进程。

客户端只需确保后台进程已处理请求。因为它将是localy,它将是瞬间的。

答案 2 :(得分:1)

我不确定为什么你要求文件不能同时保存在客户端和服务器上。但是,如果您可以略微放宽该限制,您可以在阅读后重命名该文件以指示发送文件正在进行中,然后在确认服务器已收到该文件后最终将其删除。

如果这是不可接受的,您可以使用以下更复杂的步骤:

  • 通知服务器您将发送文件,获取加密密钥
  • 从磁盘读取文件
  • 加密文件并将加密版本写入磁盘,确保已刷新
  • 打开未加密的文件并用0覆盖它的内容,确保它已刷新
  • 删除未加密的文件(从此刻起,客户端磁盘上的文件实际上不再存在)
  • 将加密文件发送到服务器
  • 删除加密文件

如果在发送过程中出现错误,则下次应用程序启动时会找到加密文件,然后将加密文件发送到服务器。服务器已经知道最初生成它的解密密钥。

答案 3 :(得分:0)

不,如果在主Form的OnClose(或OnDestroy)中你做了: 1 /写入文件 2 / closeFile 该文件将以完整的方式编写和关闭。 查看closefile帮助以获得更多解释。

答案 4 :(得分:0)

这就是我(部分)解决问题的方法

 Delete the file from disk (keep copy in RAM);
 Send the file to server;
 if server receives file ok
 then DoNothing (file was already deleted)
 else Write file back to disk;

问题是在第1步和第2步之间,文件无处存在。如果应用程序在该点死亡,则文件丢失(但是,这比在两个地方更容易接受)。不幸的是,步骤1和步骤2之间的延迟是巨大的,因为与远程服务器的通信非常慢(甚至可能长达1-2秒)。