![]() |
beliebige Datei in Stream speichern
Hallo Forum!
Wie schreibe ich eine beliebige Datei in einen Stream und wie bekomme ich die Datei dort wieder heraus? Soweit ich das bisher herausgefunden habe, muss ich auf alle Fälle SaveToStream verwenden. Ich habe jetzt versucht, mit AssignFile eine Dateivariable zu initialisieren um diese dann per SaveToStream in einen Stream zu packen, bin ich da völlig auf dem Holzweg? :freak: |
So, also im Moment sieht mein Code wie folgt aus:
Code:
Als Fehlermeldung bekomme ich in der Zeite, in der SaveToStream steht: "Record, Objekt oder Klassentyp erforderlich"
procedure TForm1.ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket); var DataEnvelope : TStream; data : file; begin AssignFile(data, 'datei\test.exe'); data.SaveToStream(DataEnvelope, dfBinary); closefile(data); Socket.SendStream(DataEnvelope); DataEnvelope.Free; end; |
Moin Evilboyz,
Du vermischt da verschiedene Methoden auf Dateien zuzugreifen. Schau Dir am Besten mal TFileStream an. Beispiel:
Delphi-Quellcode:
var
fsWork : TFileStream; sBuffer : string; begin fsWork := TFileStream.Create(DateiPfad,Modus); sBuffer := StringOfChar(#00,fsWork.Size); try // Komplett in Buffer einlesen fsWork.Read(sBuffer[1],fsWork.Size); finally FreeAndNil(fsWork); end; // Mit dem Buffer arbeiten end; |
Ahja, jetzt scheint es zu funktionieren ... Danke Dir!!!
:coder: |
Doch noch nicht ganz ....
Also die Sache mit Zitat:
Zitat:
Was ich außerdem noch nicht hinbekommen habe, ist den Stream auf der anderen Seite wieder in eine Datei zu schreiben, ich hab den jetzt über SendStream abgeschickt und auf Clientseite per receiveBuf empfangen (irgendwas kommt da immerhin schonmal an). SaveToFile meckert mir der Compiler dann allerdings an und sagt: "Undefinierter Bezeichner: SaveToFile" Irgendwie habe ich den Eindruck, dass ich immernoch verschiedene Möglichkeiten der Übertragung vermische! |
Tja, da ich natürlich nicht aufgebe und beim Nachlesen (Entwicklerhandbuch - Borland D5) immer wieder neue Erkenntnisse erlange (so sollte es ja auch sein), schreibe ich eben mal wieder selber eine Antwort.
Ich sende also, wie schon gesagt, per SendStream meinen Stream. lt. Seite 30-12 (ist ja auch egal) scheint es aber keine ReceiveStream Methode auf der anderen Seite zu geben. Wenn ich die Daten per ReceiveBuf empfange, dann scheint die Übertragung Byteweise statt zu finden und ich muss irgendwie darauf warten, bis die Übertragung fertig ist. Wie also mache ich das? Ja und die Sache mit #00 ist mir nach wie vor unklar! |
Moin Evilboyz,
statt Byteweise zu lesen, könntest Du ja auch immer Blockweise lesen, bis nichts mehr zu lesen ist (Rückgabewert -1). Mit dem StringOfChar wird die Buffergrösse auf den zumindest erforderlichen Platz gesetzt, und gleichzeitig auf #00 initialisiert, damit der Buffer einen definierten Inhalt hat. Wenn es sich um grössere Dateien handelt, könnte man natürlich auch (wie bei ReceiveBuf ;-) ) Blockweise lesen, und dann je Lesevorgang die Initialisierung wiederholen, damit zumindest der Rest des Buffers (falls die Buffergrösse kein Vielfaches der zu lesenden Anzahl Byte ist) mit einem definierten Wert belegt ist. Die #00 nehme ich, da dies ein Wert ist, der zumindest in Texten eigentlich nicht auftauchen kann (Unicode mal ausgenommen), und somit das erste auftreten einer #00 sich oft als Ende der Daten ansehen lässt (wie bei nullterminierten Strings, wie sie bei den Windows API Funktionen üblich sind). Beim Einlesen (ob nun über TFileStream.Read, oder über ReceiveBuf) könnte das dann so aussehen:
|
Hi,
ich bekomme es immer noch nicht hin! :cry: Folgenden Code habe ich jetzt also auf Serverseite. Wie finde ich bei SendBuf nun aber die den Int - Wert, den ich übergeben muss? Wenn ich testhalber 1000 eintrage wird etwas gesendet, wenn ich die Länge von SBuffer ermitteln und diese übergeben will, bekomme ich einen Fehler beim Compilieren.
Code:
Auf Clientseite kann ich doch nun mit
procedure TForm1.ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket); var fsWork : TFileStream; sBuffer : String; begin Events.Lines.Add(Socket.ReceiveText); fsWork := TFileStream.Create('datei\test.exe', fmOpenRead); sBuffer := StringOfChar(#00,fsWork.Size); try Komplett in Buffer einlesen fsWork.Read(sBuffer[1],fsWork.Size); finally FreeAndNil(fsWork); end; Socket.SendBuf(sBuffer,1000); end;
Code:
die empfangenen Daten in "Data" (Data : TStream) schreiben, oder?
Socket.ReceiveBuf(Data, Socket.ReceiveLength);
Langsam komme ich mir echt schon blöd vor, aber ich stehe voll auf dem Schlauch! |
Zitat:
|
ist dieser Code richtig???
Code:
:coder: ... *nichtaufgibt*
procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket); var Data : TStream; sBuffer : String; begin while Socket.ReceiveBuf(sBuffer, Socket.ReceiveLength)<>-1 do begin Data.Write(sBuffer, length(sBuffer)); end; Data.Free; end; |
Moin Evilboyz,
in Deinem Beispiel fehlt die Initialisierung des Buffers, ausserdem muss, wenn der Buffer ein String ist zwingend die Position angeben werden, ab der aus dem Buffer gelesen, bzw. ab der in den Buffer geschrieben werden soll. Ausserdem ist TStream eine abstrakte Klasse, die man nur als Basisklasse für Ableitungen eigener Klassen nutzen sollte (wenn Du hier im Forum mal nach "abstrakt" suchst, solltest Du dazu eine Nähere Erklärung finden):
Delphi-Quellcode:
Ich hab' Dein Beispiel mal "ergänzt".
procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
var fsData : TFileStream; // Nur mal als Beispiel einen FileStream angenommen sBuffer : String; begin // FileStream zum Schreiben öffnen fsData := TFileStream.Create('IrgendeinDateiPfad',fmOpenWrite); try // Buffer initialisieren (erst hier. Falls TFileStream.Create schiefgeht, muss der Buffer ja nicht initialisiert sein sBuffer := StringOfChar(#00,Socket.ReceiveLength); // Da in der Maximallänge gelesen wird, braucht man keine Schleife // Wichtig: sBuffer[1], damit an der ersten Stelle des Buffers geschrieben wird if Socket.ReceiveBuf(sBuffer[1],Socket.ReceiveLength) <> -1 then begin // Eingelesene Daten in Datei wegschreiben, wenn etwas eingelesen wurde fsData.Write(sBuffer[1],Length(sBuffer)); end; finally // Stream wieder freigeben FreeAndNil(fsData); end; end; So müsste es eigentlich gehen. Schau doch mal, ob Du damit klarkommst. |
:bounce1: :bounce2: :spin: :mrgreen:
Es funktioniert!!!! Vielen Dank für die Hilfe! Eine kleine Anmerkung habe ich allerdings noch. Da die Datei in mehreren Blöcken empfangen wird, ist es wichtig, darauf zu achten, dass der Zeiger im FileStream vor dem Schreiben jedes Blocks am Ende steht!
Code:
Ansonsten wird die Datei niemals größer als 3 KB ... :mrgreen:
fsData.Seek(0, SoFromEnd);
|
Moin Evilboyz,
Zitat:
|
Hi ihrs!
Ich hab das jetzt mal Interressehalber nachgemacht, und sitze da jetzt schon ca 3 stunden dran, so langsam..... Naja, guckt euch das vielleicht mal an: Server:
Code:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ScktComp; type TForm1 = class(TForm) ServerSocket1: TServerSocket; procedure ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); procedure FormCreate(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); var recbuffer:string; recstream:tfilestream; begin recbuffer:=stringofchar(#00,socket.ReceiveLength); try socket.ReceiveBuf(recbuffer[1], length(recbuffer)); recstream:=tfilestream.create('c:\test.exe', fmopenwrite); recstream.Write(recbuffer[1], length(recbuffer)); finally freeandnil(recbuffer); freeandnil(recstream); end; end; procedure TForm1.FormCreate(Sender: TObject); begin serversocket1.Active:=true; serversocket1.Open; end; end. Client:
Code:
Ich erhalte beim Schicken einer Datei beim Servertool einen error unbekannter Herkunft (10052) und einen "asyncronous socket error" aber ich habe eigentlich bei beiden Programmen den Port auf 22 gestellt.
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ScktComp; type TForm1 = class(TForm) ClientSocket1: TClientSocket; Edit1: TEdit; Edit2: TEdit; Button1: TButton; OpenDialog1: TOpenDialog; Button2: TButton; procedure Button2Click(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button2Click(Sender: TObject); begin if opendialog1.Execute then begin edit2.Text:=form1.OpenDialog1.FileName; end; end; procedure TForm1.Button1Click(Sender: TObject); var meanfile : tfilestream; meanbuffer : string; begin if (edit1.Text<>'') and (edit2.text<>'') then begin try clientsocket1.Address := edit1.Text; except showmessage('geben sie eine gültige IP-Addresse an!'); end; clientsocket1.active:=true; meanfile:=tfilestream.create(edit2.text, fmopenread); meanbuffer:=stringofchar(#00, meanfile.size); try meanfile.read(meanbuffer[1], length(meanbuffer)); clientsocket1.Open; if form1.ClientSocket1.socket.Sendbuf(meanbuffer, length(meanbuffer))<>-1 then showmessage('Datei erfolgreich verschickt'); finally freeandnil(meanfile); clientsocket1.close; clientsocket1.Free; end; end; end; end. *Helpneed* Also danke schonmal für alle Antworten! BB! Jan |
Hallo Jan,
also grundsätzlich konnte ich in Deinem Code mal keinen Fehler entdecken. Fehler 10052 bedeutet laut Delphi Hilfe "Verbindung pausiert, wenn SO_KEEPALIVE gesetzt wird." .... Soweit ich mich erinnern kann, hatte ich den Fehler auch hin und wieder, wenn eine Seite des Programms unplanmäßig beendet wurde. Ansonsten hast Du, um die Verbindung aufzubauen, Socket.open verwendet, ich habe das über socket.active := true gemacht, aber das kann ja eigentlich keinen Unterschied machen! Außerdem habe ich (siehe weiter oben) den Filestream vor dem schreiben beim Empfänger auf die letzte Position gesetzt. Das hat zwar sicher nichts mit Deinem Problem zu tun, allerdings habe ich damit noch ein Problem! Und zwar wird die Datei bei mir wunderbar übertragen, allerdings schleichen sich beim Schreiben dann falsche Bits, nämlich die der Bufferinitialisierung, ein, wenn meine Datei über 100 KB hat. Hat einer eine Idee, woran das liegen könnte? Ich poste einfach mal meinen Code ... @jan: Vielleicht hilft er Dir ja weiter!?! Server (Dateisender):
Code:
Client (Dateiempfänger):
procedure TForm1.ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket); var fsWork : TFileStream; sBuffer, sIncome : String; begin sIncome := Socket.ReceiveText; if sIncome='GetUpdate' then begin fsWork := TFileStream.Create('datei\bindif.exe', fmOpenRead); sBuffer := StringOfChar(#0,fsWork.Size); try // Komplett in Buffer einlesen fsWork.Read(sBuffer[1],fsWork.Size); finally FreeAndNil(fsWork); end; if Socket.SendBuf(sBuffer[1],Length(sBuffer))=-1 then showMessage('Es konnte keine Datei übertragen werden') else begin Events.Lines.Add('Datei wurde erfolgreich an Client übertragen'); end; end end; procedure TForm1.ConnectClick(Sender: TObject); begin ServerSocket.Port := StrToInt(Port.Text); ServerSocket.active := true; PageControl.ActivePage := TabSheet1; end; procedure TForm1.DisconnectClick(Sender: TObject); begin ServerSocket.active:=false; Events.Lines.Add('Server disconnected'); Clients.Text := '0'; end;
Code:
procedure TForm1.ConnectClick(Sender: TObject);
begin ClientSocket1.Port := StrToInt(Port.Text); ClientSocket1.Host := Host.Text; ClientSocket1.active := true; end; procedure TForm1.DisconnectClick(Sender: TObject); begin ClientSocket1.Active:=false; end; procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); var fsData : TFileStream; sBuffer : String; begin fsData := TFileStream.Create('datei\bindif.exe',fmOpenWrite); try sBuffer := StringOfChar(#23,Socket.ReceiveLength); if Socket.ReceiveBuf(sBuffer[1],Socket.ReceiveLength) <> -1 then begin fsData.Seek(0, SoFromEnd); //Hier scheint irgendwie noch ein //Problem aufzutreten fsData.Write(sBuffer[1],length(sBuffer)); Events.Lines.Add('Datenblock empfangen'); end; finally FreeAndNil(fsData); end; end; |
So, wunderschönen guten Tag Euch allen :mrgreen:
Es funktioniert nun tatsächlich! Ich sende meinen FileStream jetzt per SendStream:
Code:
Empfangen tue ich das ganz primitiv über ReceiveText:
fsWork := TFileStream.Create('datei\bindif.exe', fmOpenRead);
sBuffer := StringOfChar(#0,fsWork.Size); fsWork.Seek(0, soFromBeginning); Socket.SendStream(fsWork);
Code:
Da das ganze tatsächlich so einfach funktioniert, stelle ich mir natürlich die Frage, ob ich irgendwas noch nicht bedacht habe. Spricht irgendetwas gegen diese Vorgehensweise?
sBuffer := Socket.ReceiveText;
fsData.Write(sBuffer[1],Length(sBuffer)); Natürlich ist mir immer noch nicht klar, warum die andere Variante nicht funktioniert hat und da ich ein wissbegieriger Mensch bin, würde mich das trotzdem noch interessieren .... In jedem Fall: Danke mal für Eure Hilfe ... :spin: |
Hallo evilboyz,
warum initialisierst du denn beim sender eigentlich noch den Buffer? Den brauchst du doch nichtmehr. Könntest du vielleicht mal den kompletten qtext von dir hier zeigen, vielleicht als attachment wenn er sonst zu lang ist. Ich komme bei mir einfach nichtmehr weiter. Wenn ich den Server schliesse, also quasi ins nimmerland schicke, dann bekomme ich keinen error beim sender, aber wenn ich den server laufen lasse, dann kommt beim server etwas an, aber der sender bekommt eine errormessage. Kannst du mir mal sagen, warum du den server senden lässt und den client empfangen? normalerweise geht sowas andersrum. Deswegen komme ich auch mit deinen events nicht so ganz zurecht. So long Jan |
hi Jan,
der Server sendet in meinem Fall eine Datei als Antwort auf eine Clientanfrage. Den Code kann ich Dir morgen früh posten, da ich ihn gerade nicht hier habe, sind aber echt auch nur noch 3 oder 4 Zeilen (siehe letzter Beitrag). Den Verbindungsaufbau mache ich ganz standardmäßig, indem ich einfach .active auf true setze und vorher die Werte für Port und IP zuweise. Testest Du das ganze eigentlich lokal oder direkt schon übers Netz? Ich hatte nämlich bisher relativ viele Probleme mit Firewalleinstellungen und ich könnte mir vorstellen, dass sich in einem solchen Fall das Programm mit einem Fehler verabschiedet! Aber wie gesagt, wenn es bei Dir bis morgen nicht läuft, kann ich Dir den Code nochmal geben. |
Hallo evil,
ich teste das lokal(127.0.0.1). Und ich brauch echt morgen dann mal deinen code. Weil ich am verzweifeln bin, es wird immer nur eine 4k grosse datei geschrieben, und wenn ich den stream beim sender freigebe dann sifft das tool. Alles sehr seltsam. BB Jan |
Hi,
ich vermute mal stark, dass Du vergessen hast, den Zeiger des Dateistreams auf die letzte Position zu setzen, dann schreibt er nämlich immer von erster Stelle und kommt nie über 4 KB. Sender:
Code:
Empfänger:
unit Client_Server;
interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ScktComp, ComCtrls, ExtCtrls; type TForm1 = class(TForm) ServerSocket: TServerSocket; [...] procedure ConnectClick(Sender: TObject); procedure DisconnectClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin Events.Lines.Add('Client Connectet'); Clients.Text := IntToStr(StrToInt(Clients.Text)+1); end; procedure TForm1.ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin Events.Lines.Add('Client Disconnected'); Clients.Text := IntToStr(StrToInt(Clients.Text)-1); end; procedure TForm1.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); var fsWork : TFileStream; sIncome : String; begin // Anfrage von Client annehmen sIncome := Socket.ReceiveText; if sIncome='GetFile' then begin // Datei in Stream laden fsWork := TFileStream.Create('datei\bindif.exe', fmOpenRead); fsWork.Seek(0, soFromBeginning); // Stream als Antwort an Client senden if Socket.SendStream(fsWork) then begin Events.Lines.Add('Datei wurde erfolgreich an Client übertragen'); end else Events.Lines.Add('Ein Fehler ist aufgetreten, die Datei konnte nicht gesendet werden'); end else begin Events.Lines.Add(sIncome); end; end; procedure TForm1.ConnectClick(Sender: TObject); begin ServerSocket.Port := StrToInt(Port.Text); //Festlegung des Ports ServerSocket.active := true; //Aktivierung der ServerSocket Events.Lines.Add('Server connected on Port ' + Port.Text); PageControl.ActivePage := TabSheet1; end; procedure TForm1.DisconnectClick(Sender: TObject); begin ServerSocket.active:=false; Events.Lines.Add('Server disconnected'); Clients.Text := '0'; end; end.
Code:
unit Client_Server;
interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ScktComp; type TForm1 = class(TForm) Eingabe: TEdit; Button: TButton; ClientSocket1: TClientSocket; Eventlog: TGroupBox; [...] TCustomWinSocket); procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.ButtonClick(Sender: TObject); begin if ClientSocket1.Active then ClientSocket1.Socket.SendText(Eingabe.text); end; procedure TForm1.ConnectClick(Sender: TObject); begin ClientSocket1.Port := StrToInt(Port.Text); //Festlegung des Ports ClientSocket1.Host := Host.Text; //IP des Zielrechners ClientSocket1.active := true; //Aufbau der Verbindung procedure TForm1.DisconnectClick(Sender: TObject); begin ClientSocket1.Active:=false; Events.Lines.Add('Disconnected'); end; procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); var fsData : TFileStream; sBuffer : String; begin // FileStream zum Schreiben öffnen fsData := TFileStream.Create('datei\bindif.exe',fmOpenWrite); sBuffer := StringOfChar(#00,fsData.Size); try // Dateiinhalt als String empfangen und in Datei schreiben sBuffer := Socket.ReceiveText; fsData.Seek(0, SoFromEnd); fsData.Write(sBuffer[1],Length(sBuffer)); Events.Lines.Add('Datenblock empfangen'); finally FreeAndNil(fsData); end; end; procedure TForm1.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); var fsEmpty : TFileStream; begin if FileExists('datei\bindif.exe') then DeleteFile('datei\bindif.exe'); fsEmpty := TFileStream.Create('datei\bindif.exe',fmCreate); fsEmpty.Free; end; end. |
Re: beliebige Datei in Stream speichern
Hallo zusammen!
Ich finde den Buffer als string zu definieren ist nicht grade die eleganteste Lösung. Da wird es bei größeren Dateien Probleme geben. Mein Vorschlag:
Delphi-Quellcode:
Ich denke, dass es so funktionieren müsste.
procedure TForm1.Button1Click(Sender: TObject);
var str : TFileStream; buffer : Pointer; begin try str := TFileStream.Create('xxx.txt',fmOpenRead); buffer := AllocMem(str.Size); str.Read(Buffer^,str.Size); Server.Socket.Connections[0].SendBuf(buffer^,str.Size); finally FreeMem(buffer); FreeAndNil(str); end; end; procedure TForm1.ClientRead(Sender: TObject; Socket: TCustomWinSocket); var len : integer; buffer : Pointer; str : TFileStream; begin len := Socket.ReceiveLength; try str := TFileStream.Create('xxx2.txt',fmCreate); buffer := AllocMem(len); Socket.ReceiveBuf(buffer^,len); str.Write(buffer^,len); finally FreeAndNil(str); FreeMem(buffer); end; end; |
Re: beliebige Datei in Stream speichern
Moin Bonestorm,
was verstehst Du in diesem Zusammenhang unter "grössere Datei"? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:08 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