Zitat von
CloudyofSky:
Leider hab ich auch noch keine Ahnung von Streams
Wie könnte ich zB an Hand eines Beispiels 2 Bilder in so einen Stream legen?
Das ist ganz einfach. Alles was du zum Thema Streams wissen musst findest du in der
OH (unter TStream, TFileStream und TMemoryStream). Ein Stream ist eigentlich sehr einfach aufgebaut. Ein TStream-Objekt hat immer zwei Enden, eines in das Bytes reinkommen und eines wo diese wieder rauskommen. Du kannst dabei bestimmen wo diese Enden jeweils hinzeigen. Alles was du mit so einem Stream machst hat keine große Logik. Du kannst eine Menge von Bytes ablegen (ohne dass es wichtig ist ob es Zahlen sind, Bilder, Musik) und natürlich auch Bytes lesen, nicht mehr.
Das ist schon fast alles was man über einen Stream wissen muss. Hier liegen die Vorteile (sehr flinkes Arbeiten, sehr hohe Flexibilität), aber auch die Nachteile. So kannst du zwar eine Menge von Bytes auslesen, aber das bleiben halt Typlose Bytes, die musst du schon selbst interpretieren.
Auch wenn man eine Menge vorgefertigter Komponenten findet, kann es schnell mal sein, dass du auf ein Dateiformat triffst, für dass du noch keine Delphi-Komponente findest. In einem solchen Fall wirst du dann (wahrscheinlich) einen Stream verwenden, die Daten einlesen und interpretieren. Ist also das, was auch gemacht wird, wenn du z.B. eine Bitmap lädtst.
Jetzt gibt es schon fertige Dateitypen und viele davon haben eine Version > 1.0, das kommt nicht von ungefähr. Man lernt einfach aus den Problemen, die andere Formate mit sich brachten. Du findest hier häufig eine Menge Meta-Informationen, die nötig sind um verschiedene Dateien möglichst flexibel zu interpretieren. So kannst du z.B. in einem Tiff mehr als ein Bild speichern (ein Multipage-Tiff), so haben Zip-Archive für jede gespeicherte Datei auch die CRC32 Checksumme gespeichert usw.
Deshalb ist meine Empfehlung vorab, dass du hier eher auf eine existierende Lösung zurückgreifst, da du hier a) nichts selbst lösen musst und b) von den Erfahrungen der Entwickler profitierst.
Schauen wir uns aber trotzdem mal dein Problem mit Streams an:
Du möchtest mehr als eine Datei in einer Art Container speichern. Jetzt gibt es verschiedene Möglichkeiten, wie du die Datei organisierst. Das Hauptproblem ist, dass du nicht weißt wie viele Dateien in so einem Container gespeichert sind. Hast du mehr als eine Datei im Container, so kannst du nicht mit Sicherheit sagen, wo eine Datei anfängt und eine andere endet. Der einfachste Weg um hier Abhilfe zu schaffen besteht darin, dass du einfach ein Tupel in den Container schreibst. Schreibst du erst die Länge als Integer/Cardinal und dann die Datei. Der Vorteil liegt darin, dass ein Integer (auf 32 Bit maschinen) immer genau 4 Byte groß ist. Liest du also die ersten 4 Byte, weißt du wie groß die gespeicherte (folgende) Datei ist. Wenn hinter dieser Datei noch mind. 4 Byte im Container stehen, geben diese folgenden 4 Byte die größe der nächsten Datei an.
Das ist schon alles.
Das ganze in Delphi
Delphi-Quellcode:
// kopieren der Bytes aus dem Stream in den Container
procedure addToContainer(const Stream : TStream; const Container : TStream);
begin
// prüfen ob die Streams zugewiesen wurden
if assigned(Stream) and assigned(Container) then
begin
// schreiben der Länge des folgenden Streams
Container.write(Stream.Size, sizeOf(Integer));
// schreiben der eigentlichen Daten des Streams
// Der Parameter 0 als Länge ist hier speziell belegt
// es wird der komplette Stream vom Anfang bis Ende kopiert
Container.copyFrom(Stream, 0);
end; // if assigned(Stream) and assigned(Container)
end;
Das wäre der Code, um einen Stream in den Container einzufügen. Bei beiden Parametern handelt es sich um Streams, das ermöglicht hier viel Flexibilität. Ein solcher Stream kann mit der SaveToStream Methode von einer Komponente gefüllt werden oder halt aus einer Datei stammen. Ein einfaches Beispiel:
Delphi-Quellcode:
var Container : TStream;
buffer : TStream;
bitmap : TBitmap;
begin
Container := TFileStream.Create('PFAD\ZUR\DATEI.xyz', fmCreate or fmShareExclusive);
// Ressourcen-Schutzblöcke stellen sicher, dass eine erzeugte Ressource auch
// wieder frei gegeben wird
try
bitmap := TBitmap.Create;
try
bitmap.LoadFromFile('Pfad zu irgendeiner Bitmap);
buffer := TMemoryStream.Create;
try
bitmap.SaveToStream(buffer);
addToContainer(buffer, Container);
finally
buffer.Free;
end;
// hier könntest du jetzt noch beliebige andere Bilder Laden und so einfügen
// oder auch Dateien oder oder oder
finally
bitmap.Free;
end;
finally
Container.Free;
end;
end;
So ungefähr kann das hinzufügen aussehen. Natürlich wirkt es immer etwas aufgefüllt mit den ganzen Schutzblöcken, aber diese stellen nun mal sicher, dass du Speicher auch bei einem Fehler wieder frei gibst. Gerade wenn du ganz generell mal das .Free vergisst, kann es bei der Arbeit mit Bildern schnell sein dass der gesamte Speicher belegt ist.
Ja, zu guter Letzt kommt noch das Laden aus dieser Datei. Der Aufbau ist dabei einfach, du liest erst 4 Byte (die Länge) und danach die Anzahl von Bytes, die du halt als Länge gelesen hast.
Delphi-Quellcode:
function readNextFromContainer(const Container : TStream) : TStream;
var length : Integer;
begin
// prüfen ob es noch mindest 4 Byte im Stream gibt
if Container.Position + sizeOf(Integer) < Container.Size then
begin
// lesen der Länge
Container.Read(length, sizeOf(Integer));
// prüfen ob die Länge gültig ist
if Container.Position + length < Container.Size then
begin
result := TMemoryStream.Create;
result.CopyFrom(Container, length);
end // if Container.Position + length < Container.Size
else
begin
result := nil;
end;
end // if Container.Position + sizeOf(Integer) < Container.Size
else
begin
// keine weitere Datei im Container
result := nil;
end;
end;
Ja, damit bekommst du einen Stream aus dieser Funktion zurück. Ist dieser nil, heißt dass das es keine weiteren gespeicherten Daten im Container gibt. Wie du hier vielleicht schon bemerkt hast, der Positionszeiger eines Streams wird aut. inkrementiert. Liest du x Bytes, dann zeigt der nun auf das 11 Byte. Somit muss man sich nicht um das inkrementieren beim Lesen/Schreiben kümmern, sollte ihn aber immer auf einen definierten Wert setzen. Beim Anlegen ist diese Position aut. 0.
Ja, hoffe es hilft etwas weiter.
Gruß Der Unwissende