AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken DataSnap DataSet übertragen mit Blobfelder macht Probleme
Thema durchsuchen
Ansicht
Themen-Optionen

DataSnap DataSet übertragen mit Blobfelder macht Probleme

Ein Thema von Kostas · begonnen am 10. Sep 2015 · letzter Beitrag vom 21. Jan 2016
Antwort Antwort
Kostas

Registriert seit: 14. Mai 2003
Ort: Gerstrhofen
1.095 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

DataSnap DataSet übertragen mit Blobfelder macht Probleme

  Alt 10. Sep 2015, 16:58
Datenbank: Firebird • Version: 2.5 • Zugriff über: DataSnap
Hallo Zusammen,

das Problem vorab: Wenn ein Dataset vom DataSnapServer zum einem DataSnapClient transportiert wird welches mehrere
Records beinhaltet die auch ein binary Blob beinhalten, können die Blobinhalte verloren gehen!
Der Blob Inhalt wird massiv vergrössert.

Um das zu demonstrieren habe ich auch eine Testanwendung erzeugt mit einem DataSnapServer und ein DataSnapClient.
Die Datenbank ist eine Firebird DB mit einer Tabelle. Die Tabelle beinhaltet 15 Records.
Die ersten fünf Bilder sind gleich mit einer Größe von 2kb(2338). Die nächsten fünf Bilder ebenfalls gleich mit 22kb(21959)
und die nächsten fünf Bilder ebenfalls gleich mit einer Größe von 263kb(263368)

(Screen shot "AllImages")
Rufe ich das komplette DataSet mit alle 15 Bilder ab und speichere sie auf der Platte, ist zu sehen:
-die ersten fünf 2kb Bilder sind gleich.
-die nächsten fünf Bilder mit je 22kb wird jedes zweite und den Faktor 100 vergrößert
-die letzten fünf Bilder mit je 263kb sind wieder gleich.

(Screen shot "ImagesOneIaATime")

Rufe ich die Bilder so ab dass das DataSet immer nur ein Record liefert, werden alle 15 Bilder mit der richtigen Größe
abgerufen.

Hat jemand eine Idee warum das so ist?
Dürfen in DataSets die mehr wie ein Record haben keine Blob Felder enthalten, ist da was bekannt?
Kann das auch bei String Blobs passieren?


//Server Methoden um das DataSet zu liefern
Delphi-Quellcode:
function TdmDAL.GetIMAGES: TDataSet;
var
  oQuery: TFDQuery;
begin
  result := nil;
  oQuery := nil;

  oQuery := TFDQuery.Create(nil);
  try
    TThread.Synchronize(nil, procedure
    begin
      oQuery.Connection := FDConnection1;
      oQuery.FetchOptions.AutoFetchAll := afAll;
      oQuery.FetchOptions.RecordCountMode := cmTotal;

      oQuery.SQL.add('/*Get all images*/');
      oQuery.SQL.add('SELECT *');
      oQuery.SQL.add('FROM IMAGES');
      oQuery.Open;
    end);

    result := oQuery;
  finally
// oQuery.Free;
  end;

end;

function TdmDAL.GetIMAGE(ImageID:integer): TDataSet;
var
  oQuery: TFDQuery;
begin
  result := nil;
  oQuery := nil;

  oQuery := TFDQuery.Create(nil);
  try
    TThread.Synchronize(nil, procedure
    begin
      oQuery.Connection := FDConnection1;
      oQuery.FetchOptions.AutoFetchAll := afAll;
      oQuery.FetchOptions.RecordCountMode := cmTotal;

      oQuery.SQL.add('/*Get all images*/');
      oQuery.SQL.add('SELECT *');
      oQuery.SQL.add('FROM IMAGES');
      oQuery.SQL.add('WHERE IMAGEID = :IMAGEID');

      oQuery.Params[0].Value := ImageID;

      oQuery.Open;
    end);

    result := oQuery;
  finally
// oQuery.Free;
  end;

end;

//Client Methoden um die Images vom Server abzurufen.
Delphi-Quellcode:

Function SaveBlobIntoFile(aField: TField; aFile: String): Boolean;
Var
   S : TStream;
   FileS : TFileStream;

begin
   Result := False;
   If Not aField.IsBlob Then Exit;
// If aField.IsNull Then EXIT;

   S := aField.DataSet.CreateBlobStream(aField, bmRead);
   FileS := TFileStream.Create(aFile, fmCreate);
   Try
     Try
       FileS.CopyFrom(S, S.Size);
       Result := FileExists(aFile);
     Except
       on e:exception Do
       Begin
         Result := False;
       End;
     End;
   Finally
     S.Free;
     FileS.Free;
   End;
end;


procedure TfrClientMain.Button1Click(Sender: TObject);
var LDataSet: TDataSet;
  ImagaPath:String;
begin

  if not dmComm.InitConnection then exit;
  try

    LDataSet := dmComm.ClientContainer.FProxyInst.GetIMAGES;

    LDataSet.Open;
    LDataSet.first;

    ImagaPath := ExtractFilePath(Application.ExeName) + 'Images\';
    ForceDirectories(ImagaPath);
    while not LDataSet.Eof do
    begin
      SaveBlobIntoFile(LDataSet.FieldByName('IMAGE'), ImagaPath + LDataSet.FieldByName('IMAGEID').AsString+'.png');
      LDataSet.Next;
    end;

    ShellExecute(Handle, nil, PChar(ImagaPath), nil, nil, SW_SHOW);

  finally
    dmComm.CloseConnection;
  end;

end;

procedure TfrClientMain.Button2Click(Sender: TObject);
begin
  if not dmComm.InitConnection then exit;
  try

    Edit1.text := dmComm.ClientContainer.FProxyInst.ReverseString(Edit1.text);
  finally
    dmComm.CloseConnection;
  end;

end;

procedure TfrClientMain.Button3Click(Sender: TObject);
var LDataSet: TDataSet;
  ImagaPath:String;
  i:integer;

begin

  if not dmComm.InitConnection then exit;
  try
    ImagaPath := ExtractFilePath(Application.ExeName) + 'Images\';
    ForceDirectories(ImagaPath);

    for i := 1 to 15 do
    begin

      if Assigned(LDataSet) and LDataSet.Active then LDataSet.close;

      LDataSet := dmComm.ClientContainer.FProxyInst.GetIMAGE(i);
      LDataSet.Open;
      LDataSet.first;
      SaveBlobIntoFile(LDataSet.FieldByName('IMAGE'), ImagaPath + LDataSet.FieldByName('IMAGEID').AsString+'.png');
    end;

    ShellExecute(Handle, nil, PChar(ImagaPath), nil, nil, SW_SHOW);

  finally
    dmComm.CloseConnection;
  end;

end;
Gruß Kostas
Miniaturansicht angehängter Grafiken
allimages.png   imagesoneiaatime.png  
Angehängte Dateien
Dateityp: zip BlobTransfer.zip (4,34 MB, 5x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.453 Beiträge
 
Delphi 12 Athens
 
#2

AW: DataSnap DataSet übertragen mit Blobfelder macht Probleme

  Alt 15. Jan 2016, 18:02
Ohne das jetzt im einzelnen zu analysieren, nur zwei Dinge, die mir direkt auffallen:
  1. Das Synchronize sollte nicht notwendig sein, da die Connection ja für die Session privat erzeugt wird.
  2. Die Verwaltung der dynamisch erzeugten Queries ist unklar. Wer gibt das wann wieder frei?

Ich verwende mittlerweile eigentlich nur noch das Verfahren mit TFDJSONDataSets , wie es in diesem Tutorial beschrieben wird. Damit hatte ich zumindest bisher noch nicht solche Probleme, wie du sie schilderst.

Die Queries setze ich auch im Designer auf das Datenmodul. Dann sind sie sicher über den gesamten Client-Call vorhanden und werden mit dem Datenmodul freigegeben.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Kostas

Registriert seit: 14. Mai 2003
Ort: Gerstrhofen
1.095 Beiträge
 
Delphi 10 Seattle Enterprise
 
#3

AW: DataSnap DataSet übertragen mit Blobfelder macht Probleme

  Alt 15. Jan 2016, 18:39
Ohne das jetzt im einzelnen zu analysieren, nur zwei Dinge, die mir direkt auffallen:
  1. Das Synchronize sollte nicht notwendig sein, da die Connection ja für die Session privat erzeugt wird.
  2. Die Verwaltung der dynamisch erzeugten Queries ist unklar. Wer gibt das wann wieder frei?

Ich verwende mittlerweile eigentlich nur noch das Verfahren mit TFDJSONDataSets , wie es in diesem Tutorial beschrieben wird. Damit hatte ich zumindest bisher noch nicht solche Probleme, wie du sie schilderst.

Die Queries setze ich auch im Designer auf das Datenmodul. Dann sind sie sicher über den gesamten Client-Call vorhanden und werden mit dem Datenmodul freigegeben.
Hallo Uwe,

Zu Punkt 2: Wenn ich die Query frei gebe, knallt es. Irgend wo habe ich es gelesen dass die Freigabe DataSnap übernimmt. Ich habe auch Langzeittest durchgeführt und feststellen können dass das DataSet
wirklich sauber freigegeben wird. Zumindest wurde nicht mehr Speicher beansprucht.

Ich werde die Tage das Beispiel umbauen nach TFDJSONDataSets. Mal sehen wie sich das verhält.

Herzlichen Dank Uwe.
Gruß Kostas
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.453 Beiträge
 
Delphi 12 Athens
 
#4

AW: DataSnap DataSet übertragen mit Blobfelder macht Probleme

  Alt 15. Jan 2016, 18:55
Zu Punkt 2: Wenn ich die Query frei gebe, knallt es. Irgend wo habe ich es gelesen dass die Freigabe DataSnap übernimmt. Ich habe auch Langzeittest durchgeführt und feststellen können dass das DataSet
wirklich sauber freigegeben wird. Zumindest wurde nicht mehr Speicher beansprucht.
Klar, du kannst es nicht als result zurückgeben und dann in der nächsten Zeile freigeben. Deswegen bevorzuge ich ja auch die Instanzen im DataModule, die sind garantiert über die gesamte Lifetime der ServerClass-Instanz vorhanden.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Kostas

Registriert seit: 14. Mai 2003
Ort: Gerstrhofen
1.095 Beiträge
 
Delphi 10 Seattle Enterprise
 
#5

AW: DataSnap DataSet übertragen mit Blobfelder macht Probleme

  Alt 21. Jan 2016, 16:37
Hallo Uwe,

ich habe umgestellt auf TFDJSONDataSets und es funktioniert deutlich besser. Zumindest haben die Bilder immer die gleiche Größe,
egal ob ich Sie einzeln abrufe oder alle auf einmal. Jetzt habe ich mich auch erinnert warum ich TDataSet verwendet habe und nicht
TFDJSONDataSets. Der Hintergrund, die Clients führen ein Select über TDataSet durch. Das TDataSet wird zum DataSnapServer übertragen.
Der Server empfängt das DataSet und exportiert daraus ein JSON String. Dieses JSON String wird danach in einer FirebirdDB in ein Blob-Feld
abgelegt. Mehrere Clients können diese Pakete abrufen. Die Pakete bleiben bis zu zwei Jahre in der DB bis sie entsorgt werden.

Wir hatten schon mal darüber gesprochen und festgestellt, dass sich das JSON Format von TFDJSONDataSets in der Zukunft ändern könnte.
Wenn sich das interne Format ändern würde, könnte ich die Daten aus meinen Blobs nicht importieren. Deshalb das DataSet. Ist natürlich
auch keine Garantie dass sich das JSON Format nicht ändert.

Gruß Kostas
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.453 Beiträge
 
Delphi 12 Athens
 
#6

AW: DataSnap DataSet übertragen mit Blobfelder macht Probleme

  Alt 21. Jan 2016, 16:46
FireDAC verwendet seit einiger Zeit eine Versionierung des Stream-Formats. Damit lassen sich auch in neueren FireDACs die alten Streams noch lesen. Umgekehrt gilt das logischerweise nicht.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Kostas

Registriert seit: 14. Mai 2003
Ort: Gerstrhofen
1.095 Beiträge
 
Delphi 10 Seattle Enterprise
 
#7

AW: DataSnap DataSet übertragen mit Blobfelder macht Probleme

  Alt 21. Jan 2016, 16:54
FireDAC verwendet seit einiger Zeit eine Versionierung des Stream-Formats. Damit lassen sich auch in neueren FireDACs die alten Streams noch lesen. Umgekehrt gilt das logischerweise nicht.
oh, das ist eine gute Nachricht!
Besten Dank Uwe.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:43 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz