将数据库复制到绑定的客户端

时间:2019-04-01 13:06:46

标签: delphi sqlite

我创建了一个共享应用程序。服务器需要复制一个Sqlite数据库 并将其流式传输到客户端。

我用以下代码获取数据库:

procedure TfmxServer.actStreamTheDbExecute(Sender: TObject);
var
  ms: TMemoryStream;
begin
  ms := tmemorystream.Create;
  ms := dmplanner.GetDbAsStream;  // get it from the datamodule
  ms.Position := 0;
  thrprofServer.SendStream(thrmanServer.RemoteProfiles.First,
          'Stream_TheDB', ms);  // send it to the client
end;


function TdmPlanner.GetDbAsStream: TMemoryStream;  // datamodule
var
  fs: TFilestream;
  ms: TMemoryStream;
begin
  fs := tfilestream.Create(consqlite.Params.Values['Database'] , fmOpenRead);
  ms := tmemorystream.Create;
  try
   ms.loadfromstream(fs); // ms.size = 315392, file size = (315,392 bytes
   result := ms;               // so I am getting the full db3 file.
   result.Position := 0;
  finally
   freeandnil(fs);
   freeandnil(ms); // does this kill the result?
  end;
end;

我捕获了流,并使用以下代码编写了数据库:

procedure TfrmMobile_Client_Main.DoStreamTheDb(
  const Aresource: TremoteResource);
var
  fs: TFilestream;
  ms: TMemoryStream;
begin
  fs := tfilestream.Create
    (dmplannerclient.consqlite.Params.Values['Database'] ,
    fmopenreadwrite or fmCreate);
  try
   ms := TMemoryStream.Create;
   ms := TMemoryStream(AResource.Value.AsStream);
   ms.Position := 0;    // ms.size = 315392, so I got the whole file.
   ms.SaveToStream(fs);
  dmPlannerClient.FillLbx(lbxRecipeNames);
  // now fill a listbox, but when I open a query, I get
  //  [FireDAC][Phys][SQLite] ERROR: unable to open database file.
  finally
   freeandnil(fs);
   freeandnil(ms);
  end;
end;

所以我的问题是,如何将数据库复制到客户端 然后在客户端上使用它?

更好的是,如何使用内存数据库而不是磁盘数据库? 我尝试将FDConnection文件名设置为:memory:,但这 没用。

Delphi CE Rio 10.3.2

谢谢...丹尼尔+

1 个答案:

答案 0 :(得分:2)

我不认为有一种方法可以将整个Sqlite数据库复制到绑定的数据库中 缺少将整个数据库文件复制到客户端的客户端,因为它可能包含 许多表格和其他资源(例如视图,存储的proc等)。

但是,实际上将整个数据库复制为文件 很简单。在客户端中,您可以使用本地FDConnection在其中打开表 和FDQuery。

服务器代码:

procedure TApp1Form.SendDBAsStream;
var
  StreamToSend : TMemoryStream;

const
  DBName = 'D:\Delphi\Code\Sqlite\DB1.Sqlite';

begin
  StreamToSend := TMemoryStream.Create;
  try
    StreamToSend.LoadFromFile(DBName);
    StreamToSend.Position := 0;
    TetheringAppProfile1.Resources.FindByName('SqliteDB').Value := StreamToSend;
  finally
    // Don't free StreamToSend ?
  end;
end;

客户代码

procedure TApp2Form.TetheringAppProfile1Resources0ResourceReceived(const Sender:
    TObject; const AResource: TRemoteResource);
var
  ReceivedStream : TStream;
  FileStream : TFileStream;
begin
  FileName := ExtractFilePath(Application.ExeName) + 'Temp.Sqlite';
  AResource.Value.AsStream.Position := 0;

  FileStream := TFileStream.Create(FileName, fmCreate);
  ReceivedStream := AResource.Value.AsStream;
  try
    ReceivedStream.Position := 0;
    FileStream.CopyFrom(ReceivedStream, ReceivedStream.Size);
  finally
    FileStream.Free;
    //  ReceivedStream.Free;  No!  The tethering framework frees the stream
  end;
  OpenTable;
end;

 procedure TApp2Form.OpenTable;
 begin
   if FDConnection1.Connected then
     FDConnection1.Connected := False;
   FDConnection1.Params.Clear;
   FDConnection1.Params.Add('Database=' + FileName);
   FDConnection1.DriverName := 'Sqlite';

   try
     FDConnection1.Connected := True;
     FDQuery1.Open('select * from mytable');
   except
     ShowMessage(Exception(ExceptObject).Message + ' ' + FileName);
   end;
 end;

我在Win10 64位的Delphi 10.2.3中测试了上述内容,对我来说很好用。

如果您只想将几个表复制到客户端,我会做的是

  • 在服务器中,打开FDQuery中的表之一,然后将其数据分配给 FDM可由FDMemTable1.Data := FDQuery1.Data

  • 在FDMemTable1上调用SaveToStream并将流作为流资源发送到客户端

  • 在客户端上,调用FDMemTable.LoadFromStream以加载接收到的流。我认为, 因为我还没有尝试过客户端需要包含一个TFDPhysSQLiteiteDriverLink 支持从流中加载。