Sometimes we need to compress our data, either to reduce network traffic when transmitting or just to save space in storage media. Lucky for us, Delphi has provided ready to use compression/decompression library. This library was a wrapper around zlib open source project.
Delphi wraps the zlib library into two TStream descendant classes. TCompressionStream and TDecompressionStream. As the names suggested, TCompressionStream is for compressing and TDecompressionStream is for decompressing. To use either TStream class you would need to include zlib.pas unit in your uses list.
TCompressionStream is a write only TStream descendant. Therefore, you can only write data to its instance. In its constructor you must pass another TStream instance that will get the result of compression.
For example, if you want to compress some data into memory (in this example we will use TMemoryStream), we should setup the TCompressionStream instance like below.
... uses ... , zlib ... ; ... var vMem: TStream; vCompressor: TStream; begin vMem := TMemoryStream.Create; try vCompressor := TCompressionStream.Create(clMax, vMem); try ... end;
clMax specifies the compression level.
The actual compressing process is done by feeding data from the source to the TCompressionStream. In the above example context, if we want to compress content of stream, we could do like this.
var ... B: array[1..2048] of byte; R: integer; vSrc: TStream; ... begin ... repeat R := vSrc.Read(B, SizeOf(B)); if R > 0 then vCompressor.Write(B, R); until R < SizeOf(B); ... end;
When the above code finished, vMem will hold the compressed data.
Some of you might think the code in repeat-until block could be simplified into
But actually this one liner is not that reliable. Because CopyFrom method relies on the size of the source stream. And in some TStream class, reading the size will move the internal pointer to the end of the stream which might render the actual copying process fails because it thought that it already reached the last byte in the source stream. So, while in most cases CopyFrom will work fine, but not on all cases. Therefore let's pick the safer route.
And here is a complete working code for compressing content of a file to another file.
procedure Compress(const ASrc, ADest: string); var B: array[1..2048] of byte; R: Integer; vSrc: TStream; // source file stream vDest: TStream; // destination file stream vCompressor: TStream; // compression stream begin if not FileExists(ASrc) then raise Exception.Create('Source file does not exist'); vDest := TFileStream.Create(ADest, fmCreate); try vCompressor := TCompressionStream.Create(clMax, vDest); try vSrc := TFileStream.Create(ASrc, fmOpenRead); try repeat R := vSrc.Read(B, SizeOf(B)); if R > 0 then vCompressor.Write(B, R); until R < SizeOf(B); // C.CopyFrom(S, 0); finally vSrc.Free; end; finally vCompressor.Free; end; finally vDest.Free; end; end;
As the opposite of TCompressionStream, TDecompressionStream is read-only TStream descendant. No writing data to the instance of this class, or you will get exception. But similar with TCompressionStream, TDecompressionStream constructor also requires you to supply a TStream instance that supplies the data to be decompressed.
For example, if you want to decompress a file and the content of this file is contained in a TFileStream instance named vSrcFile, then you need to instantiate your TDecompressionStream like this.
vDecompressor := TDecompressionStream(vSrcFile);
The actual decompressing process is done when reading from the decompressor stream into the destination of your choice. Something like this.
repeat R := vDecompressor.Read(B, SizeOf(B)); if R > 0 then vDest.Write(B, R); until R < SizeOf(B);
Note that in the above code, vDest is a TStream instance.
And here is a complete working code to decompress the content of compressed file (of course only files compressed by zlib) into another file.
procedure TForm1.Decompress(const ASrc, ADest: string); var B: array[1..2048] of byte; R: Integer; vSrc: TStream; // source file stream vDest: TStream; // destination file stream vDecompressor: TStream; // compression stream begin if not FileExists(ASrc) then raise Exception.Create('Source file does not exist'); vSrc := TFileStream.Create(ASrc, fmOpenRead); try vDecompressor := TDecompressionStream.Create(vSrc); try vDest := TFileStream.Create(ADest, fmCreate); try repeat R := vDecompressor.Read(B, SizeOf(B)); if R > 0 then vDest.Write(B, R); until R < SizeOf(B); finally vDest.Free; end; finally vDecompressor.Free; end; finally vSrc.Free; end; end;
I have written a demo project to show how to do compression and decompression. The demo project will compress a file to another file, and also decompress a file. Here is full source code of the demo project: Demo.zip 237.59KB 3200 downloads. Feel free to use the codes for anything you want.
Upon running the demo project, you will get something like shown below.
Now just click Compress! button to compress the selected source file to the specied target file. After that, you can decompress the compressed file to another file. Finally you can inspect the result of decompression, and compare it to the original file. When both are exactly the same, then our codes are working correctly.