如何通过FireDac(Delphi)流式传输来自FireBird数据库的zlib压缩的图像

时间:2017-09-19 13:38:07

标签: delphi streaming firebird firedac

实际上,我之前已经问过这个问题,但我删除了这个问题,因为可能是我说错了或没有正确表达我的问题或目标。

我正在连接到我们的ERP软件数据库(FireBird v2.1)以检索用于导出为XML的数据。但与此同时,我需要将所有产品图像保存到单独的文件夹中(我将汇总所有数据和图像,然后上传到Web服务器以导入电子商务应用程序)。

我们的ERP软件公司将数据库分为两个独立的部分(1个用于信息,1个用于文件(图像,文件等) - 文件数据库)
问题是,FILES数据库中的所有资产都用zlib压缩,所以我不能直接通过LiveBindings传输这些图像(LinkPropertyToFieldBitmap中的eval错误:加载位图失败)我知道这是正常的,因为文件是压缩的并插入为BLOB。

我需要将这些zlib压缩数据作为流来使用它作为解压缩过程的输入。

我打算使用以下程序将解压缩的图像保存为文件。

procedure TForm1.DecompressStream(Stream: TStream);
var
  LOutput: TFileStream;
  LUnZip: TZDecompressionStream;
begin
  Stream := TStream.Create();
  { Create the Output and Decompressed streams. }
  LOutput := TFileStream.Create('SKU OF PRODUCT.jpg', fmCreate);
  LUnZip := TZDecompressionStream.Create(Stream);

  { Decompress data. }
  LOutput.CopyFrom(LUnZip, 0);

  { Free the streams. }
  Stream.Free;
  LUnZip.Free;
  LOutput.Free;
end;

注意:也许上面的过程不是正确的但是在能够以流的形式获取zlib数据之后我可以调试以纠正它。 感谢..

更新:我正在使用LiveBindings从数据库中获取数据,但使用LiveBindings对于压缩数据和图像处理不是必需的。

1 个答案:

答案 0 :(得分:2)

您已经说过,您将仅使用该数据集进行读取(没有写回DBMS),并且您的目标实际上只是解压缩到客户端的BLOB流。目前无法以某种舒适的方式拦截BLOB抓取( OnBlobFetching 事件的种类)。

拦截BLOB流存储的最近路径是在 TFDDatSRow.InternalSetData 方法中(它是转换所获取数据的理想位置,就在它们存储在FireDAC的内部数据存储之前)。但这需要修改源代码。

如果没有源代码修改,您可以编写例如AfterGetRecord事件的事件处理程序,并从那里解压缩流。如果您决定直接在字段中覆盖已获取的流(理想情况下将数据集设置为只读模式),请务必小心不要将修改后的数据更改提交到数据库中。

一个例子:

procedure TForm1.FDQuery1AfterGetRecord(DataSet: TFDDataSet);
var
  BlobStream: TFDBlobStream;
  HelpStream: TMemoryStream;
begin
  { create BLOB stream for reading and writing }
  BlobStream := DataSet.CreateBlobStream(DataSet.FieldByName('Data'), bmReadWrite) as TFDBlobStream;
  try
    { create intermediate stream }
    HelpStream := TMemoryStream.Create;
    try
      { decompress BLOB stream into helper one }
      ZDecompressStream(BlobStream, HelpStream);
      { and overwrite the original BLOB stream content with uncompressed data; note, that
        TFDBlobStream must know about the modification, otherwise it won't store the data
        into the storage when this stream is released (LoadFromStream won't work here) }
      BlobStream.Clear;
      BlobStream.Write(HelpStream.Memory^, HelpStream.Size);
    finally
      HelpStream.Free;
    end;
  finally
    BlobStream.Free;
  end;
end;

或类似地在较低级别:

procedure TForm1.FDQuery1AfterGetRecord(DataSet: TFDDataSet);
var
  DataRow: TFDDatSRow;
  DataCol: TFDDatSColumn;
  InLength: LongWord;
  InBuffer: Pointer;
  OutLength: Integer;
  OutBuffer: Pointer;
begin
  { get the current row storage object }
  DataRow := DataSet.GetRow;
  { for column indexing in following calls find column by name }
  DataCol := DataSet.Table.Columns.ColumnByName('Data');
  { try to get pointer to the raw data buffer for the column with given index }
  if DataRow.GetData(DataCol.Index, rvDefault, InBuffer, 0, InLength, False) then
  begin
    { decompress the data buffer into another allocated by this procedure }
    ZDecompress(InBuffer, InLength, OutBuffer, OutLength);
    try
      { start editing this storage row }
      DataRow.BeginEdit;
      try
        { let the storage copy the decompressed data from the buffer }
        DataRow.SetData(DataCol.Index, OutBuffer, OutLength);
      finally
        { finish this storage row editing without creating new row version, so the engine
          won't take the data modification as update }
        DataRow.EndEdit(True);
      end;
    finally
      { and release the buffer allocated by ZLib library function call, input buffer used
        here belongs to FireDAC's storage }
      FreeMem(OutBuffer);
    end;
  end;
end;