Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Datasnap File Upload (https://www.delphipraxis.net/164871-datasnap-file-upload.html)

BeBored 3. Dez 2011 18:24

Datasnap File Upload
 
Hallo,
ich sitze in der Arbeit an einer Client/Server Anwendung die wir mit Datasnap realisieren.
Sonst lief auch alles, nur seit heute bekomme ich beim Upload einer Datei eine Exception.
Über einen TOpenDialog wählt man eine Datei. Die wird mit Pfad in eine Listbox eingetragen (lbFiles).
Hier der Client:
Delphi-Quellcode:
aFileStream: TFileStream;
Delphi-Quellcode:
for i := 0 to lbFiles.Items.Count - 1 do
  begin
    aFileStream := TFileStream.Create(lbFiles.Items[i], fmOpenRead);
    if aClient.UploadFile(aFileStream, Store, ExtractFileName(lbFiles.Items[i])) then // Hier exception!
      begin
        Konsole.TextHinweis(ExtractFileName(lbFiles.Items[i]) + ' gesendet!');
      end;
  end;
Die Exception:
Exception-Klasse TDBXError mit Meldung 'Remote-Fehler: Ungültiger Dateiname - %s'.

Der Server:
Delphi-Quellcode:
const
  BufferSize = 1024;
var
  aFileStream: TFileStream;
  Buffer: TBytes;
  BytesReadCount: Integer;
  Success: boolean;
begin
  Success := false;
  if not DirectoryExists(ExtractFilePath(ParamStr(0)) + 'Files\') then
  begin
    MkDir(ExtractFilePath(ParamStr(0)) + 'Files\' + Store);
  end;
  try
    if FileExists(Name) then
    begin
      DeleteFile(Name);
    end;
    aFileStream := TFileStream.Create(ExtractFilePath(ParamStr(0)) + 'Files\' + Name, fmCreate);
    SetLength(Buffer, BufferSize);
    repeat
      BytesReadCount := str.Read(Buffer[0], BufferSize);
      if (BytesReadCount > 0) then
        aFileStream.Write(Buffer[0], BytesReadCount);
    until (BytesReadCount < BufferSize);
    str.Position := 0;
    Success := True;
  finally
    aFileStream.Free;
    SetLength(Buffer, 0);
  end;
  Result := Success;
end;
Ich verstehe nicht wo der Fehler liegt, weil gestern noch alles lief...
Hat wer eine Idee?

BeBored 3. Dez 2011 22:52

AW: Datasnap File Upload
 
In der Listbox steht übrigens der richtige Pfad mit der richtigen Datei. Store und Name sind nur für den Server und soweit ich das sehe auch nicht das Problem.

Bummi 3. Dez 2011 23:01

AW: Datasnap File Upload
 
Einzig ungewöhlich finde ich den Parameter "Name"

himitsu 3. Dez 2011 23:05

AW: Datasnap File Upload
 
Jupp, wo "Name" doch ein beliebtes property in allen VCL-Komponenten ist.

Zitat:

Delphi-Quellcode:
MkDir(ExtractFilePath(ParamStr(0)) + 'Files\' + Store)
FileExists(Name)
DeleteFile(Name)
TFileStream.Create(ExtractFilePath(ParamStr(0)) + 'Files\' + Name)


Mit Pfad, ohne Pfad, Pfad mit Store oder Pfad ohne Store?
Delphi-Quellcode:
ExtractFilePath(ParamStr(0)) + 'Files\' + Store
Name
ExtractFilePath(ParamStr(0)) + 'Files\' + Name
PS: Daß die DP Name fett darstellt, sollte dir schon zu Denken geben. :angle2:

BeBored 3. Dez 2011 23:16

AW: Datasnap File Upload
 
Ja stimmt mit Name und habe ich (VNC sei dank) auch gleich geändert aber der Fehler kommt nach wie vor am Client.
Ist Store '00000' dann wird die Datei im Ordner Files gespeichert, ist sie anders (z.B. 52125) dann wird der Ordner 52125 in Files angelegt und die Datei dort gespeichert. Der Server liefert auch kein False zurück, bis dahin kommt er garnicht.

...hab es jetzt gefunden... der Fehler war das ich auf Files geprüft habe aber dann immer Files\00000 anlegen wollte. nenene

Ne das war es doch nicht...

BeBored 3. Dez 2011 23:40

AW: Datasnap File Upload
 
Hier mal die Upload Methode
Delphi-Quellcode:
function TServerMethodsClient.UploadFile(str: TStream; Store: string; fName: string): Boolean;
begin
  if FUploadFileCommand = nil then
  begin
    FUploadFileCommand := FDBXConnection.CreateCommand;
    FUploadFileCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FUploadFileCommand.Text := 'TServerMethods.UploadFile';
    FUploadFileCommand.Prepare;
  end;
  FUploadFileCommand.Parameters[0].Value.SetStream(str, FInstanceOwner);
  FUploadFileCommand.Parameters[1].Value.SetWideString(Store);
  FUploadFileCommand.Parameters[2].Value.SetWideString(fName);
  FUploadFileCommand.ExecuteUpdate; // Genau hier gibt es dann die Exception
  Result := FUploadFileCommand.Parameters[3].Value.GetBoolean;
end;
Vielleicht hilft es ja :cry:

DeddyH 3. Dez 2011 23:45

AW: Datasnap File Upload
 
Zitat:

Zitat von BeBored (Beitrag 1139199)
Delphi-Quellcode:
FUploadFileCommand.Text := 'TServerMethods.UploadFile';

Sollte es nicht besser LoadFromFile heißen statt der direkten Zuweisung? Ich kann aber auch mal wieder alles falsch verstehen.

BeBored 3. Dez 2011 23:49

AW: Datasnap File Upload
 
Zitat:

Zitat von DeddyH (Beitrag 1139201)
Zitat:

Zitat von BeBored (Beitrag 1139199)
Delphi-Quellcode:
FUploadFileCommand.Text := 'TServerMethods.UploadFile';

Sollte es nicht besser LoadFromFile heißen statt der direkten Zuweisung? Ich kann aber auch mal wieder alles falsch verstehen.

Das wurde von Delphi oder besser von der TSQLConnection erstellt. Das wird auch jedesmal wenn ich eine neue Methode implementiere überschrieben.

DeddyH 3. Dez 2011 23:51

AW: Datasnap File Upload
 
Jo, dann hab ich das wohl falsch interpretiert, ich hab auch ehrlich gesagt keinen Plan von DataSnap.

himitsu 3. Dez 2011 23:53

AW: Datasnap File Upload
 
Delphi-Quellcode:
var
  aFileStream: TFileStream;
  Buffer: array[1..16*1024] of Byte; // 16 KB
  BytesReadCount: Integer;
begin
  if not DirectoryExists(ExtractFilePath(ParamStr(0)) + 'Files') then
    MkDir(ExtractFilePath(ParamStr(0)) + 'Files\' + Store);
  aFileStream := TFileStream.Create(ExtractFilePath(ParamStr(0)) + 'Files\' + FileName, fmCreate);
  try
    repeat
      BytesReadCount := str.Read(Buffer, SizeOf(Buffer));
      aFileStream.Write(Buffer, BytesReadCount);
    until BytesReadCount < SizeOf(Buffer);
  finally
    aFileStream.Free;
  end;
end;
Beschreibung:
  • fmCreate überschreibt bestehende Dateien ... es ist also nicht nötig diese vorher zu löschen
  • Das .Create gehört vor den Try-Finally-Block, denn wenn es vor dem .Create schon knallt, dann knallt es im Finally nochmals beim .Free, da die Variable nicht initialisiert hattest.
    > Tipp: Hör auf das, was dir der Compiler sagt, denn der hat dich ganz bestimmt gewarnt.
    Ausnahme: Man initialisiert die Variable vorher entsprechend mit NIL.
  • Dynamische Arrays (z.B. TBytes) und Strings muß man nicht unbedingt manuell freigeben.
    Vorallem wenn Delphi das eine Millisekune später auch nochmal macht ... Im geheimen/unsichtbaren END-Code deiner Methode.
  • Da du eh mit einer Konstanten Puffergröe arbeitest, kannst du den Puffer auch gleich konstannt erstellen.
    > 16 KB sollten locker nich mit noch auf deinen Stack drauf passen ... wenn nicht, dann stimmt eh irgendwo etwas nicht.
  • 1 KB ist eh etwas "suboptimal"
    DataSnap hat standardmäßig einen Übertragungspuffer von 32 KB und auch die Clustergröße deiner Festplatte (die Größe der Verwaltungseinheiten) wird bei dir bestimmt mehr als ein 1 KB sein.
  • Name > FileName
  • das Success ist vollkommen "sinnlos"
    • Erstemal kannst du deine werte direkt an result übergeben und brauchst keine susätzliche Variable.
      (ist nicht wie beim Return in C, PHP, JS und Co., wo bei dessen Zuweisung die Prozedur beendet wird)
    • außerdem gibt dein Resul keinen "sinnvolen" Wert zurück.
      • es gibt True zurück, wenn alles (vermutlich) erfolgreich war
        (ist nicht wie beim Return in C, PHP, JS und Co., wo bei dessen Zuweisung die Prozedur beendet wird)
      • bei einem Fehler gibt es eine Exception und das Result wird nicht verwendet
      Also reicht eine Proedure ... Fehler werden ja eh als Exception zurückgegeben.

      Die Exceptions jetzt abzufangen (try-except) wäre ganz schlimm, denn ein Boolean sagt nur daß etwas falsch lief,
      aber die Exception, welche gesagt hätte was genau nichtr ging, hätte man dann geschrottet.

      Im Server also einfach
      Delphi-Quellcode:
      procedure UploadFile(Stream: TStream; StorePath, FileName: string);
      verwenden.

PS: Sollte die Datei im Store-Pfad liegen, oder hab ich das falsch verstanden?
Wenn ja, dann muß "Store" natürlich noch mit in den Pfad des TFileStream.Create aufgenommen werden.


Zitat:

Das wird auch jedesmal wenn ich eine neue Methode implementiere überschrieben.
Du mußt das ja auch im Server ändern ... dann wird es im Clienten richtig importiert.

BeBored 4. Dez 2011 01:13

AW: Datasnap File Upload
 
Danke für deine Hilfe zu so später Stunde. :)
Ich habe deine Änderungen jetzt umgesetzt und mit Server+Client auf einem Rechner funktioniert es auch, aber sobald ich den Server extern öffne und dann übertragen will, gibt es wieder die gleiche Exception. :(

BeBored 4. Dez 2011 01:24

AW: Datasnap File Upload
 
Soweit ich bisher feststellen kann, wird der richtige Dateiname übertragen, aber die Dateien sind immer nur 1024 byte groß.
Die Exception kommt natürlich auch :)

BeBored 4. Dez 2011 01:31

AW: Datasnap File Upload
 
Das ist bisher der Server:
Delphi-Quellcode:
procedure TServerMethods.UploadFile(str: TStream; Store, FileName: string);
var
  aFileStream: TFileStream;
  BytesReadCount: Integer;
  Buffer: array [1 .. 16 * 1024] of Byte;
begin
  if not DirectoryExists(ExtractFilePath(ParamStr(0)) + 'Files\' + Store) then
  begin
    MkDir(ExtractFilePath(ParamStr(0)) + 'Files\' + Store);
  end;
  aFileStream := TFileStream.Create(ExtractFilePath(ParamStr(0)) + 'Files\' + Store + '\' + FileName, fmCreate);
  try
    repeat
      BytesReadCount := str.Read(Buffer, BufferSize);
      aFileStream.Write(Buffer, BytesReadCount);
    until (BytesReadCount < SizeOf(Buffer));
    str.Position := 0;
  finally
    aFileStream.Free;
  end;
end;
Und der Client:
Delphi-Quellcode:
procedure TfrmMain.btnSendClick(Sender: TObject);
var
  aFileStream: TFileStream;
  i: Integer;
begin
  if lbFiles.Count > 0 then
  begin
    try
      for i := 0 to lbFiles.Items.Count - 1 do
      begin
        aFileStream := TFileStream.Create(lbFiles.Items[i], fmOpenRead);
        try
          begin
            aClient.UploadFile(aFileStream, Store, ExtractFileName(lbFiles.Items[i]));
            lbFiles.Clear;
          end
        except
          begin
            ShowMessage('Übertragung fehlerhaft!');
          end;
        end;
      end;
    finally
      aFileStream.Free;
      btnSend.Enabled := False;
    end;
  end
  else
  begin
    Konsole.TextHinweis('Keine Dateien zum Senden vorhanden.');
  end;
end;

BeBored 4. Dez 2011 01:40

AW: Datasnap File Upload
 
Grad gesehen das ich ein
Delphi-Quellcode:
SizeOf(Buffer)
vergessen hatte. Nun wird die komplette Datei übertragen und eine Exception gibt es auch nicht mehr.

Vielen Dank für die späte Hilfe! :thumb:

Gor1 10. Jan 2012 10:35

AW: Datasnap File Upload
 
Hallo,

ich habe die Upload-Routine aus dem obigen Beispiel auf Delphi 2009 bzw. Delphi XE2 ausprobiert, allerdings bekomme ich beim zweiten Aufruf der UploadFile-Methode im Client eine Exception (bzw. auch bei der Freigabe des DataSnap-Datenmoduls auf dem Client). Ich habe dann mal die Freigabe des aFileStream im Client auskommentiert, damit funktioniert es dann.
Komischerweise scheint es aber auch keine Speicherlecks (auf dem Client) zu geben, jedenfalls liefert FastMM keine. Irgendwo scheint der Stream also dann doch freigegeben zu werden.

Hier meine Upload-Routine, die mehrfach aufgerufen wird:
Delphi-Quellcode:
   
procedure UploadFile(PfadName, FileName: String);
    var lFileStream: TFileStream;
    begin
      if (FileName <> '') and FileExists(PfadName + FileName) then
      begin
        lFileStream := TFileStream.Create(PfadName + FileName, fmOpenRead);
        try
          try
            DataModule.ServerMethods1Client.UploadFile(lFileStream, 'KundeXY', FileName);
          except
            ShowMessage('Übertragung fehlerhaft!');
          end;
        finally
//        lFileStream.Free;
        end;
      end;
    end;
Hat einer von euch eine Idee, warum das so ist?
Es funktioniert zwar grundsätzlich so, aber es bleibt ein ungutes Gefühl, wenn ein lokales Objekt erzeugt wird aber dann nicht mehr (explizit) freigegeben wird.

himitsu 10. Jan 2012 10:53

AW: Datasnap File Upload
 
Zitat:

Zitat von Gor1 (Beitrag 1145184)
allerdings bekomme ich beim zweiten Aufruf der UploadFile-Methode im Client eine Exception

Wie lautet die Exception-Message (daß man auch immer wieder erts nachfragen muß :wall:) und wie ist die Servermethode implementiert?

irgendwo = Wenn man sagt der Owner soll es machen, dann macht Er (DataSnap) das auch.


Tipp:
Delphi-Quellcode:
procedure UploadFile(PfadName, FileName: String);
    var lFileStream: TFileStream;
    begin
      if (FileName <> '') and FileExists(PfadName + FileName) then
      begin
        lFileStream := TFileStream.Create(PfadName + FileName, fmOpenRead);
        try
          try
            DataModule.ServerMethods1Client.UploadFile(lFileStream, 'KundeXY', FileName);
          except
            ShowMessage('Übertragung fehlerhaft!');
          end;
        finally
          if not DataModule.InstanceOwner then // DataSnap ist hier einfach nur blöd implementiert, also besser so
            lFileStream.Free;
        end;
      end;
    end;
PS: Bei Streams mit (standardmäßig) über 29 KB muß man etwas aufpassen.

PPS:

Eine bessere treffendere Namensgebung wäre auch nicht unbedingt zu verachten. (z.B. alternativ zu den Vorgabewerten DataModule und ServerMethods1Client)

Delphi-Quellcode:
ShowMessage('Übertragung fehlerhaft!');
... Und was lief schief? ... das weiß nun keiner mehr.
stattdessen eventuell sowas:
Delphi-Quellcode:
except
  on E: Exception do
    ShowMessage('Übertragung fehlerhaft!' + sLineBreak + E.Message);
end;

Gor1 10. Jan 2012 12:15

AW: Datasnap File Upload
 
Die Exception-Meldung war "Im Projekt Testprog.exe ist ein Exception der Klasse EExternalException mit der Meldung 'Externe Exception C0000008' aufgetreten".

Die Servermethode ist im Wesentlichen so implementiert wie in dem Beispiel von BeBored oben, aber der Vollständigkeit halber hier nochmal:

Delphi-Quellcode:
procedure TServerMethods1.UploadFile(str: TStream; Store, FileName: String);
var aFileStream: TFileStream;
    BytesReadCount: Integer;
    Buffer: array [1 .. 16 * 1024] of Byte;
    strPfad: String;
begin
  strPfad := CPfad + '\' + Store;
  if not DirectoryExists(strPfad) then
  begin
    MkDir(strPfad);
  end;
  aFileStream := TFileStream.Create(strPfad + '\' + FileName, fmCreate);
  try
    repeat
      BytesReadCount := str.Read(Buffer, SizeOf(Buffer));
      aFileStream.Write(Buffer, BytesReadCount);
    until (BytesReadCount < SizeOf(Buffer));
    str.Position := 0;
  finally
    aFileStream.Free;
  end;
end;
Der Tipp mit der Abfrage nach dem InstanceOwner war sehr hilfreich, sie liefert hier True. Allerdings ist mir nicht klar, warum der InstanceOwner des Streams hier das DataModule ist. Kann man das irgendwo einstellen?

Mit der besseren Namensgebung hast du natürlich recht, aber es ging mir hier erst mal um einen einfachen Test und die korrekte Funktion.

himitsu 10. Jan 2012 12:51

AW: Datasnap File Upload
 
Zitat:

Zitat von Gor1 (Beitrag 1145203)
Der Tipp mit der Abfrage nach dem InstanceOwner war sehr hilfreich, sie liefert hier True.

TRUE ist halt die Standardeinstellung bei dieser automatisch generierten Klasse.

Zitat:

Zitat von Gor1 (Beitrag 1145203)
Allerdings ist mir nicht klar, warum der InstanceOwner des Streams hier das DataModule ist. Kann man das irgendwo einstellen?

Genau dort, wo ich es auch abfrage?
(das ist die globale Einstellung für sowas)

Dieser Wert wird vor den automatisch generierten Clientklassen abgefragt und jeweils für deren Objekt-Parameter verwendet.

Gor1 11. Jan 2012 10:51

AW: Datasnap File Upload
 
Danke für deine Hilfe, ich habe die entsprechenden Stellen jetzt gefunden.

Captnemo 12. Sep 2012 07:53

AW: Datasnap File Upload
 
Hallo,

ich habe mir eure Funktion oben mal zu nutze gemacht um ein Bild zu übertragen. Klappt auch im Großen und Ganzen wunderbar.
Nur habe ich an einer Stelle einen Fehler, den ich mir nicht erklären kann. Hier erst mal mein Code im Client:
Delphi-Quellcode:
procedure TForm2.Button2Click(Sender: TObject);
var
  aClient: TServerMethods1Client;
  aFileStream: TFileStream;
  i: Integer;
 begin
  aClient := TServerMethods1Client.Create(SQLCOnnection1.DBXConnection);
  Try
    aFileStream := TFileStream.Create(JvFilenameEdit1.Text, fmOpenRead);
    try
      aClient.UploadFile(aFileStream, 'Upload', ExtractFileName(JvFilenameEdit1.Text));
    except
      ShowMessage('Übertragung fehlerhaft!');
    end;
  Finally
    aFileStream.Free;
    aClient.Free;
  End;
end;
(Jetzt bitte mal nicht auf die Try...expept-Blöcke achten ;-) )

Wenn ich jetzt was übertragen will, dann tritt beim aClient.Free eine Exception auf:
"Im Projekt DSClient.exe ist eine Exception der Klasse $C0000008 mit der Meldung 'system exception (code 0xc0000008) at
0x77b712f7' aufgetreten."

Ich war eigentlich immer der Meinung, dass man selbst erzeugte Objekte auch selber wieder freigeben sollte. Aber möglicherweise
besteht ja noch ein Zugriff auf dieses Objekt. Ich weiß aber nich wo.

Wenn ich das aClient.Free weglasse, dann habe ich bei XP beim verlassen des Programms eine Exception:
"Exception EAccessViolation in Modul DSClient.exe bei 00005D08.
Zugriffsverletzung bei Adresse 00405D08 in Modul 'DSClient.exe'. Lesen von Adresse 0000000C."

Ich habe noch eine zweite Funktion, die ich aus einen Tutorial übernommen habe:
Delphi-Quellcode:
procedure TForm2.Button1Click(Sender: TObject);
var
  aClient: TServerMethods1Client;
begin
  aClient := TServerMethods1Client.Create(SQLCOnnection1.DBXConnection);
  Try
    Edit1.Text := aClient.ReverseString(Edit1.Text);
  Finally
    aClient.Free;
  End;
 end;
Die läuft aber ohne Exception, und hier gehe ich aClient ja auch frei.

Hat jemand eine Idee, was ich falsch gemacht habe?

Captnemo 12. Sep 2012 07:59

AW: Datasnap File Upload
 
Sorry, hat mehrfach gepostet.

Uwe Raabe 12. Sep 2012 09:18

AW: Datasnap File Upload
 
Zitat:

Zitat von Captnemo (Beitrag 1182579)
Delphi-Quellcode:
      aClient.UploadFile(aFileStream, 'Upload', ExtractFileName(JvFilenameEdit1.Text));

Zeig mal, was da in UploadFile passiert.

Uwe Raabe 12. Sep 2012 09:44

AW: Datasnap File Upload
 
Um das mal etwas näher zu erläutern: Beim Create eines Proxy kann man als zweiten Parameter angeben, ob der Proxy der InstanceOwner ist. Lässt man diesen Parameter weg, wird true angenommen. Als InstanceOwner kümmert sich der Proxy selbst um die übergebenen und erhaltenen Instanzen. Ich vermute, das ist in diesem Fall auch so. Daher meine Frage nach der Implementation von UploadFile.

Captnemo 12. Sep 2012 10:54

AW: Datasnap File Upload
 
Hier ist mein Proxy:
Delphi-Quellcode:
//
// Erzeugt vom DataSnap-Proxy-Generator.
// 11.09.2012 15:05:33
//

unit proxy;

interface

uses Data.DBXCommon, Data.DBXClient, Data.DBXDataSnap, Data.DBXJSON, Datasnap.DSProxy, System.Classes, System.SysUtils, Data.DB, Data.SqlExpr, Data.DBXDBReaders, Data.DBXJSONReflect;

type
  TServerMethods1Client = class(TDSAdminClient)
  private
    FEchoStringCommand: TDBXCommand;
    FReverseStringCommand: TDBXCommand;
    FUploadFileCommand: TDBXCommand;
  public
    constructor Create(ADBXConnection: TDBXConnection); overload;
    constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload;
    destructor Destroy; override;
    function EchoString(Value: string): string;
    function ReverseString(Value: string): string;
    procedure UploadFile(str: TStream; Store: string; FileName: string);
  end;

implementation

function TServerMethods1Client.EchoString(Value: string): string;
begin
  if FEchoStringCommand = nil then
  begin
    FEchoStringCommand := FDBXConnection.CreateCommand;
    FEchoStringCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FEchoStringCommand.Text := 'TServerMethods1.EchoString';
    FEchoStringCommand.Prepare;
  end;
  FEchoStringCommand.Parameters[0].Value.SetWideString(Value);
  FEchoStringCommand.ExecuteUpdate;
  Result := FEchoStringCommand.Parameters[1].Value.GetWideString;
end;

function TServerMethods1Client.ReverseString(Value: string): string;
begin
  if FReverseStringCommand = nil then
  begin
    FReverseStringCommand := FDBXConnection.CreateCommand;
    FReverseStringCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FReverseStringCommand.Text := 'TServerMethods1.ReverseString';
    FReverseStringCommand.Prepare;
  end;
  FReverseStringCommand.Parameters[0].Value.SetWideString(Value);
  FReverseStringCommand.ExecuteUpdate;
  Result := FReverseStringCommand.Parameters[1].Value.GetWideString;
end;

procedure TServerMethods1Client.UploadFile(str: TStream; Store: string; FileName: string);
begin
  if FUploadFileCommand = nil then
  begin
    FUploadFileCommand := FDBXConnection.CreateCommand;
    FUploadFileCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FUploadFileCommand.Text := 'TServerMethods1.UploadFile';
    FUploadFileCommand.Prepare;
  end;
  FUploadFileCommand.Parameters[0].Value.SetStream(str, FInstanceOwner);
  FUploadFileCommand.Parameters[1].Value.SetWideString(Store);
  FUploadFileCommand.Parameters[2].Value.SetWideString(FileName);
  FUploadFileCommand.ExecuteUpdate;
end;


constructor TServerMethods1Client.Create(ADBXConnection: TDBXConnection);
begin
  inherited Create(ADBXConnection);
end;


constructor TServerMethods1Client.Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean);
begin
  inherited Create(ADBXConnection, AInstanceOwner);
end;


destructor TServerMethods1Client.Destroy;
begin
  FreeAndNil(FEchoStringCommand);
  FreeAndNil(FReverseStringCommand);
  FreeAndNil(FUploadFileCommand);
  inherited;
end;

end.
und hier mein Server:
Delphi-Quellcode:
unit ServerMethodsUnit1;

interface

uses System.SysUtils, System.Classes, Datasnap.DSServer, Datasnap.DSAuth;

type
{$METHODINFO ON}
  TServerMethods1 = class(TComponent)
  private
    { Private declarations }
  public
    { Public declarations }
    function EchoString(Value: string): string;
    function ReverseString(Value: string): string;
    procedure UploadFile(str: TStream; Store, FileName: String);
  end;
{$METHODINFO OFF}

implementation


uses System.StrUtils;

procedure TServerMethods1.UploadFile(str: TStream; Store, FileName: String);
var
  aFileStream: TFileStream;
  BytesReadCount: Integer;
  Buffer: array [1 .. 16 * 1024] of Byte;
  strPfad: String;
begin
  strPfad := 'C:\' + Store;
  if not DirectoryExists(strPfad) then
  begin
    MkDir(strPfad);
  end;
  aFileStream := TFileStream.Create(strPfad + '\' + FileName, fmCreate);
  try
    repeat
      BytesReadCount := str.Read(Buffer, SizeOf(Buffer));
      aFileStream.Write(Buffer, BytesReadCount);
    until (BytesReadCount < SizeOf(Buffer));
    str.Position := 0;
  finally
    aFileStream.Free;
  end;
end;

function TServerMethods1.EchoString(Value: string): string;
begin
  Result := Value;
end;

function TServerMethods1.ReverseString(Value: string): string;
begin
  Result := System.StrUtils.ReverseString(Value);
end;
end.
Was du sagst, mit dem InstanceOwner, verstehe ich. Aber was ist dann der Unterschied zwischen den beiden Funktionen?
Delphi-Quellcode:
procedure TForm2.Button1Click(Sender: TObject);
var
  aClient: TServerMethods1Client;
begin
  aClient := TServerMethods1Client.Create(SQLCOnnection1.DBXConnection);
  Try
    Edit1.Text := aClient.ReverseString(Edit1.Text);
  Finally
    aClient.Free;
  End;
 end;

procedure TForm2.Button2Click(Sender: TObject);
var
  aClient: TServerMethods1Client;
  aFileStream: TFileStream;
 begin
  aClient := TServerMethods1Client.Create(SQLCOnnection1.DBXConnection);
  Try
    aFileStream := TFileStream.Create(JvFilenameEdit1.Text, fmOpenRead);
    try
      aClient.UploadFile(aFileStream, 'Upload', ExtractFileName(JvFilenameEdit1.Text));
    except
      ShowMessage('Übertragung fehlerhaft!');
    end;
  Finally
    aFileStream.Free;
    aClient.Free;
  End;
end;
in beiden erzeuge ich das Objekt aClient auf die gleiche Weise. Demnach müßte ja in beiden Funktion der gleiche Fehler auftreten. Tut er aber nicht.

Uwe Raabe 12. Sep 2012 12:30

AW: Datasnap File Upload
 
Zitat:

Zitat von Captnemo (Beitrag 1182596)
Delphi-Quellcode:
  FUploadFileCommand.Parameters[0].Value.SetStream(str, FInstanceOwner);

In dieser Zeile wird dem Value mitgeteilt, ob er sich um die Freigabe des übergebenen Streams kümmern soll oder nicht. Da du das einfache Create verwendest, ist FInstanceOwner implizit true. Du kannst also entweder beim Create noch ein False übergeben oder (empfohlen) das aFileStream.Free im Button2Click weglassen. Das Problem ist also nicht das aClient.Free!

Aus diesem Grund hast du auch bei ReverseString keine Probleme, da dort keine Instanzen freigegeben werden müssen.

Captnemo 12. Sep 2012 12:52

AW: Datasnap File Upload
 
Das habe ich verstanden. Vielen Dank. Mal sehen, wie weit ich jetzt komme :-D

himitsu 12. Sep 2012 13:23

AW: Datasnap File Upload
 
Datasnap gibt (standardmäßig) Streams frei, welche übertragen wurden, also wenn man die automatisch generierten Clientklassen verwendet.

Wenn du es selbst nochmal freigibst, dann knallt es natürlich.


Vertausche mal
Delphi-Quellcode:
aFileStream.Free;
und
Delphi-Quellcode:
aClient.Free;
, oder kommentiere
Delphi-Quellcode:
aFileStream.Free;
aus.

[edit]
Komisch, alles ab 13:30 war eben noch nicht sichtbar. :shock:

In einer der automatisch generierten Clientklassen baut Delphi ein Property ein, wo du dieses Freigeben deaktivieren kannst.

Uwe Raabe 12. Sep 2012 13:42

AW: Datasnap File Upload
 
Zitat:

Zitat von himitsu (Beitrag 1182626)
Vertausche mal
Delphi-Quellcode:
aFileStream.Free;
und
Delphi-Quellcode:
aClient.Free;

Jetzt nicht wirklich, oder? Die Instanz wird zwar automatisch freigegeben, aber die Variable aFileStream wird damit nicht zwangsläufig auf nil gesetzt.

himitsu 12. Sep 2012 13:51

AW: Datasnap File Upload
 
Neee nee, das war nicht zur Fehlerbehandlung ... Ich war nur mal neugierig, wie/ob sich dann die Fehlerposition verschiebt.

Ich bin eher der Meinung, daß der Stream schon direkt nach dem Übertragen freigegeben wird, womit ich schon beim
Delphi-Quellcode:
aFileStream.Free;
eine Exception erwartet hätte.
Hmmmm, aber vielleicht hatte es ja dort geknallt und der Debugger zeigte nur die falsche Codezeile an. (macht er gerne mal, da der Stacktrace die Rücksprungadressen behinhaltet, also den nächsten Befehl)


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz