2008. november 26., szerda

How to store records to a stream for later retrieval


Problem/Question/Abstract:

How to store records to a stream for later retrieval

Answer:

Stores a record to stream. Record can later be retrieved with RecordFromStream procedure.

procedure RecordToStream
  (DSet: TDataSet; {Dataset in question}
  Stream: TStream; {Stream to store to}
  PhysFieldsOnly: Boolean; {Do not store lookup and calculated fields}
  FieldsNotStore: array of TField); {Additional fields that should not be stored}

  function DoStoreFld(aFld: TField): Boolean;
    {Checks whether the field should be stored}
  var
    i: Integer;
  begin
    Result := not PhysFieldsOnly or (aFld.FieldNo > 0);
    {FieldNo of Lookup and calculated fields is <= 0}
    if Result then
      for i := 0 to High(FieldsNotStore) do
        if aFld = FieldsNotStore[i] then
        begin
          Result := false;
          break;
        end;
  end;

  procedure WriteFldname(fldname: string);
  var
    L: longint;
  begin
    L := length(fldname);
    Stream.Write(L, sizeOf(L));
    Stream.Write(fldname[1], L);
  end;

var
  I, Cnt, Len: Longint;
  Fld: TField;
  FldBuff: Pointer;
  BStream: TBlobStream;
begin
  Cnt := DSet.FieldCount;
  Getmem(FldBuff, 256);
  try
    for i := 1 to Cnt do
    begin
      Fld := DSet.Fields[i - 1];
      if not DoStoreFld(Fld) then
        Continue;
      WriteFldname(Fld.Fieldname);
      if Fld is TBlobField then
      begin
        BStream := TBlobStream.Create(Fld as TBlobField, bmRead);
        try
          Len := BStream.Size;
          Stream.Write(len, SizeOf(Len));
          Stream.CopyFrom(BStream, Len);
        finally
          BStream.Free;
        end;
      end
      else
      begin
        Len := Fld.dataSize;
        Fld.Getdata(FldBuff);
        Stream.Write(Len, SizeOf(Len));
        Stream.Write(FldBuff^, Len);
      end;
    end;
    Len := 0;
    {Mark the end of the stream with zero}
    Stream.Write(Len, SizeOf(Len));
  finally
    Freemem(FldBuff, 256);
  end;
end;

Reads record from the stream. The record was previously stored with RecordToStream procedure. Dset must be in edit/insert mode.

procedure RecordFromStream
  (DSet: TDataSet; {Dataset in question}
  Stream: TStream; {Stream to retrieve from}
  FieldsToIgnore: array of TField); {Fields that should not be retrieved}

  function DoReadFld(aFld: tField): Boolean;
  var
    i: Integer;
  begin
    Result := (aFld <> nil) and (aFld.FieldNo > 0);
    {calculated and lookup fields are allways ignored}
    if Result then
      for i := 0 to High(FieldsToIgnore) do
        if aFld = FieldsToIgnore[i] then
        begin
          Result := false;
          break;
        end;
  end;

  function ReadFldname: string;
  var
    L: longint;
  begin
    Stream.Read(L, sizeOf(L));
    if L = 0 then
      result := ''
    else
    begin
      SetLength(Result, L);
      Stream.Read(Result[1], L);
    end;
  end;

var
  Len: Longint;
  Fld: TField;
  Fldname: string;
  FldBuff: Pointer;
begin
  Getmem(FldBuff, 256);
  try
    Fldname := ReadFldname;
    while Fldname <> '' do
    begin
      if Fldname = '' then
        break;
      Fld := DSet.FindField(Fldname);
      Stream.Read(Len, SizeOf(Len));
      if DoReadFld(Fld) then
      begin
        if Fld is TBlobField then
        begin
          with TBlobStream.Create(Fld as TBlobField, bmWrite) do
          try
            CopyFrom(Stream, Len);
          finally
            Free;
          end;
        end
        else
        begin
          if Fld.datasize <> Len then
            raise Exception.CreateFmt('Field size changed: Field: %s', [Fldname]);
          Stream.Read(FldBuff^, Fld.dataSize);
          Fld.Setdata(FldBuff);
        end;
      end
      else
      begin
        Stream.Seek(Len, soFromCurrent);
      end;
      Fldname := ReadFldname;
    end
  finally
    Freemem(FldBuff, 256);
  end;
end;

Nincsenek megjegyzések:

Megjegyzés küldése