Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Datei mit TStream kopieren (https://www.delphipraxis.net/103780-datei-mit-tstream-kopieren.html)

Antigo 21. Nov 2007 17:38


Datei mit TStream kopieren
 
Hi,
Ich möchte gern eine Datei per TFilestream einlesen und dann wiederrum per Tfilestream auch wieder speichern. Im Endeffekt möchte ich auf diese Weise eine Datei übers Netzwerk kopieren und damit bei einem Verbindungsabbruch nicht alles weg ist möchte ich die Datei stückchenweise rüberschicken.
Also Datei öffnen, die ersten XX Bytes lesen und auf der Netzwerkfreigabe speichern. Die nächsten XX Bytes lesen, speichern usw. Wenn jetzt die Verbindung abbricht mache ich nach wiederherstellung derselbigen einfach da weiter wo ich aufgehört habe. Soweit die Theorie.

Also hab ich mir gedacht ich definiere erstmal einen Puffer vom Typ Variant weil ich ja keine Ahnung hab was für Daten kommen und es mich ja auch nicht interessiert, ich muss es ja nur kurz zwischenspeichern. Als nächstes öffne ich einen Input und einen Output Stream.

Delphi-Quellcode:
var StreamIn, StreamOut:TStream;
   i,buffersize:integer;
   buffer:Variant;
begin
  buffersize:=strtoint(edit3.text);
  StreamIn := TFileStream.Create(edit1.text,fmOpenRead);
  StreamOut := TFileStream.Create(edit2.text, fmCreate);
so jetzt möchte ich solange immer wieder den puffer lesen und schreiben bis ich fertig bi
Delphi-Quellcode:
  for i:=1 to StreamIn.Size div buffersize do
  begin  
    StreamIn.ReadBuffer(buffer,buffersize);
    StreamOut.WriteBuffer(buffer,buffersize);
  end;
  StreamIn.Free;
  Streamout.free;

Wenn ich das so mache kopiert er die datei einwandfrei (buffersize=1 erstmal) aber gibt folgende Fehlermeldung aus: Ungültiger Variant Typ.

Per Einzelanweisung hab ich dann herausgefunden, das der Fehler erst dann ausgegeben wird, wenn alles kopiert wurde, auch wenn ich bspw nur 10 bytes übertrage...
weiss jemand wo der fehler liegt? Ist der Ansatz an sich vielleicht schon kappes?

Dank schonmal im vorraus :)

sirius 21. Nov 2007 17:43

Re: Datei mit TStream kopieren
 
Schau mal nach "CopyFrom"

Antigo 21. Nov 2007 17:46

Re: Datei mit TStream kopieren
 
Super, Ich danke dir :)

Antigo 21. Nov 2007 18:06

Re: Datei mit TStream kopieren
 
Jetzt hab ich doch noch ein Problem.

Und zwar scheint es mir so, dass er die vielen CopyFrom Aufträge zusammen fasst und dann gemeinsam verschickt. Ich hab jedenfalls mal einen 1 MB Puffer eingestellt und eine 20MB Datei über wlan kopiert. Nach jedem Copy From sollte er beep;en. Jetzt kommen allerdings direkt am Anfang jede menge beeps, dann werden die Intervalle zwischen den beeps etwas größer und das Programm steht still. Gucke ich mir jetzt den Netzwerk Traffic an hab ich volle Auslastung.
Ich möchte aber nicht alles sammeln und dann schicken, sondern eben Päckchenweise. Weiss jemand wie ich darauf warten kann, dass der eine CopyFrom Befehl abgearbeitet wurde? Erst dann will ich den nächsten starten (auch wenns Zeit kostet).

gsh 21. Nov 2007 18:12

Re: Datei mit TStream kopieren
 
Also ich glaube du hast da noch nen bug drinnen ... zeig mal bissi code

Lies dir vorallem die hilfe über CopyFrom genau durch
Des ist glaub ich der wichtigste satz:
Zitat:

CopyFrom kopiert die in Count angegebene Anzahl Bytes aus dem in Source angegebenen Stream in den Stream. Dann verschiebt es die aktuelle Position um die in Count angegebene Anzahl Bytes und gibt die Anzahl der kopierten Bytes zurück.

sirius 21. Nov 2007 18:12

Re: Datei mit TStream kopieren
 
Copyfrom arbeitet sicherlich nicht parallel. Was machst du denn genau mit WLAN?
Delphi-Quellcode:
var StreamIn, StreamOut:TStream;
begin

  StreamIn := TFileStream.Create(edit1.text,fmOpenRead);
  StreamOut := TFileStream.Create(edit2.text, fmCreate);

  StreamOut.CopyFrom(StreamIn,0);

  StreamIn.free;
  StreamOut.free;
end;

Antigo 21. Nov 2007 18:21

Re: Datei mit TStream kopieren
 
Ich mache genau das, was du gepostet hast, nur in Einzelschritten

Delphi-Quellcode:
procedure TForm1.Button3Click(Sender: TObject);
var StreamIn, StreamOut:TStream;
   i,buffersize:integer;
   buffer:Variant;
begin
  buffersize:=strtoint(edit3.Text);
  StreamIn := TFileStream.Create(edit1.text,fmOpenRead);
  StreamOut :=TFileStream.Create(edit2.text, fmCreate);


  for i:=1 to floor(StreamIn.Size / buffersize) do
  begin
    StreamOut.CopyFrom(StreamIn,buffersize);
    beep;

    //Status
    if i mod 4 = 0 then
    begin
      Progressbar1.Position:= 100*i*buffersize div StreamIn.Size;
      Application.ProcessMessages;
    end;
  end;
 
  //Rest der datei
  if StreamIn.Size mod buffersize > 0 then
    StreamOut.CopyFrom(StreamIn,0);

  StreamIn.Free;
  Streamout.free;

end;
Jedenfalls beept es wie gesagt am anfang ganz oft und die Progressbar schnellt nach vorne und dann gehts immer langsamer bis das Programm hängt...


Und zum WLAN: ich kopier einfach nur eine datei auf eine Netzwerkfreigabe, über wlan halt, welches sehr langsam ist (600kByte/s)

sirius 21. Nov 2007 18:29

Re: Datei mit TStream kopieren
 
Mit dem letzten
StreamOut.CopyFrom(StreamIn,0);
kopierst du den ganzen Stream nochmal von vorn (das bedeutet die 0).

Aber was davor passiert weis ich auch nicht.
Probier es mal so, wie ich es getan habe. Klappt es dann?

Muetze1 21. Nov 2007 19:03

Re: Datei mit TStream kopieren
 
Sendest du selber über die Sockets?

sirius 21. Nov 2007 19:08

Re: Datei mit TStream kopieren
 
Zitat:

Sendest du selber über die Sockets?
Wenn er alles filebasiert macht, dann wohl über den Windows Netzwerk Client :zwinker:

Luckie 21. Nov 2007 19:34

Re: Datei mit TStream kopieren
 
Warum geht dir beim Kopieren was verloren? Du kopierts doch und verschiebst nicht. Und warum Variant als Datentyp für den Buffer? Nimm doch ein Byte-Array oder PByte.

Antigo 21. Nov 2007 20:33

Re: Datei mit TStream kopieren
 
Zitat:

Zitat von sirius
Mit dem letzten
StreamOut.CopyFrom(StreamIn,0);
kopierst du den ganzen Stream nochmal von vorn (das bedeutet die 0).

Aber was davor passiert weis ich auch nicht.
Probier es mal so, wie ich es getan habe. Klappt es dann?

Oh das mit der 0 war tatsächlich sinnlos. Jetzt siehts so aus:

Delphi-Quellcode:
 for i:=1 to floor(StreamIn.Size / buffersize) do
  begin
    StreamOut.CopyFrom(StreamIn,buffersize);

    //Status
    Progressbar1.Position:= 100*i*buffersize div StreamIn.Size;
    label2.caption:=inttostr(i*buffersize) +' / '+ inttostr(Streamin.Size);
    Application.ProcessMessages;
  end;

  if StreamIn.Size mod buffersize > 0 then
    StreamOut.CopyFrom(StreamIn,StreamIn.size mod buffersize);
das kopieren an sich funktioniert generell ja auch, nur läuft es halt nicht so ab wie ich mir das vorstelle.



Zitat:

Zitat von sirius
Zitat:

Sendest du selber über die Sockets?
Wenn er alles filebasiert macht, dann wohl über den Windows Netzwerk Client :zwinker:

jo so siehts aus ;)



Zitat:

Zitat von Luckie
Warum geht dir beim Kopieren was verloren? Du kopierts doch und verschiebst nicht. Und warum Variant als Datentyp für den Buffer? Nimm doch ein Byte-Array oder PByte.

Wie kommst du auf verlieren? Hab ich das irgendwo geschrieben?

Warum Variant? Naja aus Ahnungslosigkeit wie mans richtig macht ;)

Aber der buffer ist durch das CopyFrom ja sowieso weggefallen.


Nochmal zu meinem Problem:
Kopiert wird alles vernünftig, nur hab ich wiegesagt den Eindruck, dass das ganze asynchron abläuft, so wie als wenn man etwas über sockets sendet und da zu schnell sachen hintereinander abschickt. Dann werden die ja auch zusammen gefasst.

Eigentlich sollte es halt so sein, dass nach und nach kleine Pakete verschickt werden und die Progressbar nach meinem oben geposteten Quellcode halbwegs gleichmäßig durchlaufen sollte, und der Kopiervorgang auch beendet sein sollte, sobald die Progressbar durch ist. Das ist aber nicht der Fall. Nachdem diese durchgelaufen ist, hängt das Programm und der Netzwerkmonitor (STRG+Alt+Entf unter WinXP) zeigt noch max. Traffic an. Das geht noch einige sek. so weiterm bis die Übertragung dann tatsächlich abgeschlossen ist. sehr merkwürdig das ganze.

sirius 21. Nov 2007 20:37

Re: Datei mit TStream kopieren
 
Zitat:

Nachdem diese durchgelaufen ist, hängt das Programm und der Netzwerkmonitor (STRG+Alt+Entf unter WinXP) zeigt noch max. Traffic an. Das geht noch einige sek. so weiterm bis die Übertragung dann tatsächlich abgeschlossen ist. sehr merkwürdig das ganze.
Das liegt daran:
Zitat:

Mit dem letzten
StreamOut.CopyFrom(StreamIn,0);
kopierst du den ganzen Stream nochmal von vorn (das bedeutet die 0).
Du darfst da nicht 0 übergeben, sondern, das was tatsächlich noch kopiert werden muss (irgendwie Stream.Size-Stream.Position)

Und der Rest dürfte problemlos klappen.

Antigo 21. Nov 2007 20:45

Re: Datei mit TStream kopieren
 
den Fehler hab ich ja bereits eingesehen und korrigiert:

Delphi-Quellcode:
  if StreamIn.Size mod buffersize > 0 then
    StreamOut.CopyFrom(StreamIn,StreamIn.size mod buffersize);
Wenn sich also die Dateigröße nicht komplett in n buffersize große Pakte einteilen lässt (was ja sehr wahrscheinlich ist), dann wird zum schluss noch ein kleineres Paket mit dem Rest verschickt. Das sollte aber weniger als eine Sekunde dauern bei der kleinen Buffer größe die ich gewählt habe und keine 10 sek.

Bei meiner Beispieldatei mit 23510720 Byte und einer Puffergröße von 1024 Byte werden damit im letzten schwung 23510720 mod 1024=704 Byte übertragen. Trotzdem muss ich 10 sek warten bis der darunterstehende code die Progressbar auf 100 bringt...

Luckie 22. Nov 2007 07:42

Re: Datei mit TStream kopieren
 
Zitat:

Zitat von Antigo
Wie kommst du auf verlieren? Hab ich das irgendwo geschrieben?

Zitat:

Zitat von Antigo
und damit bei einem Verbindungsabbruch nicht alles weg ist


Antigo 22. Nov 2007 09:36

Re: Datei mit TStream kopieren
 
Zitat:

Zitat von Luckie
Zitat:

Zitat von Antigo
Wie kommst du auf verlieren? Hab ich das irgendwo geschrieben?

Zitat:

Zitat von Antigo
und damit bei einem Verbindungsabbruch nicht alles weg ist


Naja ich hab das Problem ständig. Mein WLAN ist nicht so superstabil und gibt gerade dann mal für eine sek. den Geist auf, wenn eine 300MB Datei grad zu 98% übertragen ist (im lokalen Netzwerk).
Wenn ich dann mit den Windows Funktionen am kopieren war, passiert ganz einfach folgendes: Alles ist weg. Und das möchte ich eben verhindern.

Antigo 22. Nov 2007 17:03

Re: Datei mit TStream kopieren
 
Ich krieg das Problem nicht gelöst. Wollte es jetzt nochmal mit Buffer Variablen und den Read und Write Funktionen versuchen. Ausserdem wollte ich jetzt das das von lucki empfohlene Byte Array als Speicher nutzen.

Delphi-Quellcode:
var StreamIn, StreamOut:TStream;
   i,buffersize:integer;
   buffer:array of Byte;
begin
  buffersize:=strtoint(edit3.Text);
  StreamIn := TFileStream.Create(edit1.text,fmOpenRead);
  StreamOut :=TFileStream.Create(edit2.text, fmCreate);

  setlength(buffer,buffersize);
  for i:=1 to floor(StreamIn.Size / buffersize) do
  begin
    StreamIn.Read(buffer,buffersize);
    StreamOut.Write(buffer,buffersize);

    //Status
    Progressbar1.Position:= 100*i*buffersize div StreamIn.Size;
    label2.caption:=inttostr(i*buffersize) +' / '+ inttostr(Streamin.Size);
    Application.ProcessMessages;
  end;

 { if StreamIn.Size mod buffersize > 0 then
    StreamOut.CopyFrom(StreamIn,StreamIn.size mod buffersize); }

  Progressbar1.Position:=100;
  label2.caption:=inttostr(StreamIn.size) +' / '+ inttostr(Streamin.Size);


  StreamIn.Free;
  Streamout.free;

end;
Da schmeisst mich der Compiler aber nach dem ersten Schreibversuch mit einer Zugriffsverletzung raus. Kann jemand helfen?

sirius 22. Nov 2007 17:11

Re: Datei mit TStream kopieren
 
nimm mal buffer[0] in den Read und Erite Methoden.

Antigo 22. Nov 2007 17:20

Re: Datei mit TStream kopieren
 
jo das funktioniert, auch wenn ich nicht verstehe warum.
Leider hilft das ganze bei meinem Problem überhaupt nicht. Die Schleife wird durchgegangen, die Progressbar geht auf fast 100% und die Anzeige im Label geht auf einen Wert der knapp unter StreamIn.Size / StreamIn.Size liegt. Dann dauerts eine Weile und erst DANN wird der darauf folgende Code
Delphi-Quellcode:
 Progressbar1.Position:=100;
  label2.caption:=inttostr(StreamIn.size) +' / '+ inttostr(Streamin.Size);


  StreamIn.Free;
  Streamout.free;
ausgeführt. Die Übertragung geht bis zu diesem Zeitpunkt weiter. Also ich kann mir das echt nicht erklären. Nach meinen Verständnis dürfte die for Schleife erst dann weitergehen (i erhöhen; von vorne anfangen), wenn der Buffer übertragen und geschrieben wurde. Demenstprechend müsste alles geschrieben sein, wenn die Statusbar auf 100% ist (den kleinen Rest der nicht in buffersize aufgeht hab ich absichtlich mal weggelassen).

Hat noch jemand eine Idee was man versuchen könnte? Gäbs sonst eine andere Methode ausser Streams, eine Datei stückcheinweise zu übertragen/speichern/kopieren?

danke schonmal :)

sirius 22. Nov 2007 17:32

Re: Datei mit TStream kopieren
 
Nicht wirklich.

Die delphieigenen Funktionen:
-assignfile
-resetfile
-rewrite
-closefile
arbeiten wie TFileStream / THandleStream sehr direkt über die WinAPI-Funktionen CreateFile, WriteFile, ReadFile und SetFilePointer

Antigo 22. Nov 2007 18:14

Re: Datei mit TStream kopieren
 
so ich hab das jetzt mal umgemodelt, also das selbe was ich mit streams gemacht hab auf untypisierte Dateien und Blockread/Blockwrite umgebaut.

Delphi-Quellcode:
var FInput,FOutput: File;
    i, buffersize,transferred:integer;
    buffer:array of Byte;
begin
  buffersize:=strtoint(edit3.Text);
  AssignFile(FInput,edit1.Text);
  Reset(FInput,1);

  AssignFile(FOutput,edit2.Text);
  Rewrite(FOutput,1);

  setlength(buffer,buffersize);
  for i:=1 to floor(FileSize(FInput) / buffersize) do
  begin
    Blockread(FInput,buffer[0],buffersize,transferred);
    Blockwrite(FOutput,buffer[0],buffersize,transferred);

    Seek(FInput,i*buffersize);


     //Status
    Progressbar1.Position:= 100*i*buffersize div FileSize(FInput);
    label2.caption:=inttostr(i*buffersize) +' / '+ inttostr(FileSize(FInput));
    Application.ProcessMessages;
  end;

  Progressbar1.Position:=100;
  label2.caption:=inttostr(FileSize(FInput)) +' / '+ inttostr(FileSize(FInput));
Das Ergebnis ist identsich. Statusbar läuft durch. Alle Befehle werden abgearbeitet, aber die Übertragung geht noch lange Zeit nach durchlauf der for Schleife weiter.

Mir fällt jetzt auch nix weiter mehr ein. Falls noch jemand eine Idee hat, her damit, ansonsten werd ich das Projekt begraben.

Antigo 24. Nov 2007 12:41

Re: Datei mit TStream kopieren
 
Zitat:

Zitat von sirius
Nicht wirklich.

Die delphieigenen Funktionen:
-assignfile
-resetfile
-rewrite
-closefile
arbeiten wie TFileStream / THandleStream sehr direkt über die WinAPI-Funktionen CreateFile, WriteFile, ReadFile und SetFilePointer

es lässt mich nicht los ^^

Also ich hab mir jetzt mal die API Funktion WriteFile angeschaut. Da steht in der Dokumentation:
Zitat:

WriteFile Function


Writes data to the specified file at the position specified by the file pointer. This function is designed for both synchronous and asynchronous operation.
Also kann es sehr wohl sein, dass das ganze bei mir asynchron arbeitet.

Ich hab noch nie direkt mit den Windows API Funktionen gearbeitet (zum. nicht wissentlich ^^)

Kann mir da vielleicht jemand weiterhelfen?

Apollonius 24. Nov 2007 13:11

Re: Datei mit TStream kopieren
 
Nein, denn für asynchronen Gebrauch muss CreateFile mit FILE_FLAG_OVERLAPPED aufgerufen worden sein - du arbeitest immer synchron, sofern du nichts anderes bei CreateFile sagst.

Antigo 24. Nov 2007 13:30

Re: Datei mit TStream kopieren
 
ok gut zu wissen. Aber ich arbeite ja nicht direkt mit den API Funktionen, sondern mit assignfile, blockwrite etc.
Woher weiss ich denn, was delphi mit diesen Fkt. macht?

Apollonius 24. Nov 2007 13:52

Re: Datei mit TStream kopieren
 
Einfach mal ein bisschen in System.pas stöbern. Nach weniger als einer Minute bin ich eben auf die Funktion TextOpen gestoßen, die, in der auskommentierten Pascal-Fassung (es wird zum Kompilieren Assembler verwendet), die Zeile
Delphi-Quellcode:
CreateFileA(t.Name, OpenMode, FILE_SHARE_READ, nil, Flags, FILE_ATTRIBUTE_NORMAL, 0);
enthält. Also kein FILE_FLAG_OVERLAPPED im vorletzten Parameter.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:27 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