无法从Datasnap Server检索大于260.000字节的TStream

Mar*_*lot 5 delphi datasnap firedac delphi-10.1-berlin

我有一个Delphi 10.1 Berlin Datasnap Server,它不能返回大于260.000字节的数据包(通过TStream).

我在Delphi 的\ Object Pascal\DataSnap\FireDAC示例之后编写了它,它也显示了这个问题.

只需打开该示例,在ServerMethodsUnit.pas上将qOrders组件的IndexFieldName设置为空白,并将其SQL属性更改为:

select * from Orders
union 
select * from Orders
Run Code Online (Sandbox Code Playgroud)

现在要发送的数据量超过260.000字节,这似乎是您无法从客户端检索它的点.获得EFDException [FireDAC] [Stan] -710.二进制存储格式无效.

数据作为Stream从服务器上的FDSchemaAdapter获取,然后加载到客户端上的另一个FDSchemaAdpater.客户端和服务器之间的连接也是FireDAC.

这是服务器返回该流的方式:

function TServerMethods.StreamGet: TStream;
begin
  Result := TMemoryStream.Create;
  try
    qCustomers.Close;
    qCustomers.Open;
    qOrders.Close;
    qOrders.Open;
    FDSchemaAdapter.SaveToStream(Result, TFDStorageFormat.sfBinary);
    Result.Position := 0;
  except
    raise;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

这就是客户端检索它的方式:

procedure TClientForm.GetTables;
var
  LStringStream: TStringStream;
begin
  FDStoredProcGet.ExecProc;
  LStringStream := TStringStream.Create(FDStoredProcGet.Params[0].asBlob);
  try
    if LStringStream <> nil then
    begin
      LStringStream.Position := 0;
      DataModuleFDClient.FDSchemaAdapter.LoadFromStream(LStringStream, TFDStorageFormat.sfBinary);
    end;
  finally
    LStringStream.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

客户端无法获取Blob参数的所有数据.我在服务器上保存Stream的内容,以及在客户端上到达Blob参数的内容,并且它们具有相同的大小,但Blob参数的内容的内容被截断,最后几个Kbytes为零.

这就是我在服务器上保存将转到Stream的内容的方法:

FDSchemaAdapter.SaveToFile('C:\Temp\JSON_Server.json', TFDStorageFormat.sfJSON);
Run Code Online (Sandbox Code Playgroud)

这是我检查客户端blob参数的方法:

TFile.WriteAllText('C:\Temp\JSON_Client.json', FDStoredProcGet.Params[0].asBlob);
Run Code Online (Sandbox Code Playgroud)

我可以看到客户端获取截断的数据.

您知道如何修复它,或者从Datasnap Server检索所有Stream内容到我的客户端的解决方法吗?

更新:我已更新到Delphi 10.1 Berlin Update 2,但问题仍然存在.

谢谢.

Mar*_*lot 0

我已经编写了一个解决方法。由于我无法传递大于 255Kb 的数据,因此我将其拆分为不同的 255Kb 数据包并单独发送(我还添加了压缩以最小化带宽和往返次数)。

在服务器上,我已将 StremGet 更改为两个不同的调用:StreamGet 和 StreamGetNextPacket。

function TServerMethods.StreamGet(var Complete: boolean): TStream;
var Data: TMemoryStream;
    Compression: TZCompressionStream;
begin
  try
    // Opening Data
    qCustomers.Close;
    qCustomers.Open;
    qOrders.Close;
    qOrders.Open;

    // Compressing Data
    try
      if Assigned(CommStream) then FreeAndNil(CommStream);
      CommStream := TMemoryStream.Create;
      Data := TMemoryStream.Create;
      Compression := TZCompressionStream.Create(CommStream);
      FDSchemaAdapter.SaveToStream(Data, TFDStorageFormat.sfBinary);
      Data.Position := 0;
      Compression.CopyFrom(Data, Data.Size);
    finally
      Data.Free;
      Compression.Free;
    end;

    // Returning First 260000 bytes Packet
    CommStream.Position := 0;
    Result := TMemoryStream.Create;
    Result.CopyFrom(CommStream, Min(CommStream.Size, 260000));
    Result.Position := 0;

    // Freeing Memory if all sent
    Complete := (CommStream.Position = CommStream.Size);
    if Complete then FreeAndNil(CommStream);
  except
    raise;
  end;
end;

function TServerMethods.StreamGetNextPacket(var Complete: boolean): TStream;
begin
  // Returning the rest of 260000 bytes Packets
  Result := TMemoryStream.Create;
  Result.CopyFrom(CommStream, Min(CommStream.Size - CommStream.Position, 260000));
  Result.Position := 0;

  // Freeing Memory if all sent
  Complete := (CommStream.Position = CommStream.Size);
  if Complete then FreeAndNil(CommStream);
end;
Run Code Online (Sandbox Code Playgroud)

CommStream:TStream 在 TServerMethods 上被声明为私有。

客户端以这种方式检索它:

procedure TClientForm.GetTables;
var Complete: boolean;
    Input: TStringStream;
    Data: TMemoryStream;
    Decompression:  TZDecompressionStream;
begin
  Input := nil;
  Data := nil;
  Decompression := nil;

  try
    // Get the First 260000 bytes Packet
    spStreamGet.ExecProc;
    Input := TStringStream.Create(spStreamGet.ParamByName('ReturnValue').AsBlob);
    Complete := spStreamGet.ParamByName('Complete').AsBoolean;

    // Get the rest of 260000 bytes Packets
    while not Complete do begin
      spStreamGetNextPacket.ExecProc;
      Input.Position := Input.Size;
      Input.WriteBuffer(TBytes(spStreamGetNextPacket.ParamByName('ReturnValue').AsBlob), Length(spStreamGetNextPacket.ParamByName('ReturnValue').AsBlob));
      Complete := spStreamGetNextPacket.ParamByName('Complete').AsBoolean;
    end;

    // Decompress Data
    Input.Position := 0;
    Data := TMemoryStream.Create;
    Decompression := TZDecompressionStream.Create(Input);
    Data.CopyFrom(Decompression, 0);
    Data.Position := 0;

    // Load Datasets
    DataModuleFDClient.FDSchemaAdapter.LoadFromStream(Data, TFDStorageFormat.sfBinary);
  finally
    if Assigned(Input) then FreeAndNil(Input);
    if Assigned(Data) then FreeAndNil(Data);
    if Assigned(Decompression) then FreeAndNil(Decompression);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

现在效果很好。