Title: How can I zip content using a stream?
I'm surprised how often I've wanted to be able to do this in different situations: I have data in memory that I want to compress and send via e-mail or via TCP. Using the freeware Abbrevia compression toolkit (http://sourceforge.net/projects/tpabbrevia/), this can be accomplished by
creating a TAbZipper objecteither writing to it via a TStream, or saving the data I have to diskadding the file to the TAbZipper objectOpening the zipfile from the disk and attach to the e-mail, or send via TCP using a TFileStreamSure, that works... but it's a tad inefficient. Why is the filesystem getting involved at all? Unfortunately, the Abbrevia toolkit doesn't seem to offer a way to work only with TMemoryStream objects. But I found a way. First up, create yourself a descendant of TABZipArchive:
CODE
uses
AbZipTyp, AbArcTyp, AbZipPrc;
type
TZipStream =
class
(TAbZipArchive)
private
procedure
ZipHelper(Sender : TObject; Item : TAbArchiveItem; OutStream : TStream);
procedure
ZipHelperStream(Sender : TObject; Item : TAbArchiveItem; OutStream, InStream : TStream);
public
constructor
CreateFromStream( aStream : TStream;
const
ArchiveName :
string
);
override
;
end
;
{ TZipStream }
constructor
TZipStream.CreateFromStream(aStream: TStream;
const
ArchiveName:
string
);
begin
inherited
;
DeflationOption := doMaximum;
CompressionMethodToUse := smDeflated;
InsertHelper := ZipHelper;
InsertFromStreamHelper := ZipHelperStream;
end
;
procedure
TZipStream.ZipHelper(Sender: TObject; Item: TAbArchiveItem; OutStream: TStream);
begin
AbZip(TAbZipArchive(Sender), TAbZipItem(Item), OutStream);
end
;
procedure
TZipStream.ZipHelperStream(Sender: TObject; Item: TAbArchiveItem; OutStream, InStream: TStream);
begin
if
Assigned(InStream)
then
AbZipFromStream(TAbZipArchive(Sender), TAbZipItem(Item), OutStream, InStream);
end
;
And then use it like this. This example adds data from a TStringStream to an e-mail message as a zip attachment:
CODE
uses
IdAttachmentMemory;
procedure
EmailStrings;
var
m : TIdMessage;
// Indy e-mail message object
ss : TStringStream;
// to hold data we're going to zip
ms : TMemoryStream;
// temporary stream
z : TZipStream;
// our zip object
begin
m := TIdMessage.Create;
try
{ TODO: populate TIdMessage with From, Subject, etc. }
ss := TStringStream.Create(
''
);
try
{ TODO: populate the TStringStream object with data }
ss.Position :=
0
;
// must reposition at beginning of stream
ms := TMemoryStream.Create;
// temporary memory stream
try
z := TZipStream.CreateFromStream(ms,
''
);
try
z.Load;
z.AddFromStream(
'filename.txt'
, ss);
// filename within zip file
z.SaveArchive;
z.FStream.Position :=
0
;
with
TIdAttachmentMemory.Create(m.MessageParts, z.FStream)
do
Filename :=
'filename.zip'
;
// filename of attachment in e-mail
finally
z.Free;
end
;
finally
ms.Free;
end
;
finally
ss.Free;
end
;
{ TODO: send message }
finally
m.Free;
end
;
end
;