You are not logged in.
First of all, all the zip compression is embedded in an unique Delphi unit, named SynZip.pas, which is meant to replace the default zlib unit of Delphi, and enhance it. It works from
Delphi 7 to Delphi 2010, from the same source code, which is released under a GPL/LGPL/MPL tri-license.
Do you want to compress some data in memory? Use CompressString() and UnCompressString() methods. Both work with RawByteString encoded data:
function test: boolean;
var tmp: RawByteString;
begin
tmp := 'some data';
result := (UnCompressString(CompressString(tmp,False,comp))=tmp);
end;
Do you want to create a zip archive file? Use our TZipWrite class.
procedure TestCompression;
var FN: TFileName;
begin
FN := ChangeFileExt(paramstr(0),'.zip');
with TZipWrite.Create(FN) do
try
AddDeflated('one.exe',pointer(Data),length(Data));
assert(Count=1);
AddDeflated('ident.txt',M.Memory,M.Position);
assert(Count=2);
finally
Free;
end;
end;
Do you want to read a zip archive file? Use our TZipRead class.
procedure TestUnCompression;
var FN: TFileName;
i: integer;
begin
FN := ChangeFileExt(paramstr(0),'.zip');
with TZipRead.Create(FN) do
try
i := NameToIndex('ONE.exe');
assert(i>=0);
assert(UnZip(i)=Data);
i := NameToIndex('ident.TXT');
assert(i>=0);
assert(Entry[i].info^.zcrc32=crc32(0,M.Memory,M.Position));
finally
Free;
end;
DeleteFile(FN);
end;
And if you want the file to be embedded to your executable, there is a dedicated Create constructor in the TZipRead just for handling that:
- create your exe file with a TZipRead instance created with it;
- append your zip content to your exe (by using TZipWrite or by using copy /b on command line);
- that's all!
Couldn't it be easier?
Code above was extracted and adapted from our SynSelfTests test unit.
The SynZip.pas unit itself can be downloaded from our Source Code repository, on with most of our projects.
Offline
We provided some free and open source classes to read a .zip archive bundled (or not) to an exe. You can therefore append any .zip archive to your exe, then extract any picture inside this .zip with one class.
Use the following method:
constructor TZipRead.Create(const aFileName: TFileName; ZipStartOffset, Size: cardinal);
and provide paramstr(0) - i.e. your exe - as aFileName and ZipStartOffset as a minimal original exe size: it will search for the beginning of the .zip file from this offset. Leave Size parameter as 0: it will get the size from the file size itself.
The same class can get any .zip archive embedded as a resource to your exe, if you prefer.
They are two ways of appending .zip content to an exe:
1. use copy /b original.exe+pictures.zip newembedded.exe
2. use the TZipWrite class provided, and its AddFromZip() method to create your exe from Delphi code: you can even compress and append your images on the fly, with no temporary pictures.zip file.
Offline
If you don't need fastest performance available, but very small code size or just prefer one pascal unit, we've release a self-contained 100% pure pascal source code for the same purpose: see our PasZip unit. It contains the same TZipRead and TZipWrite classes. I use this one to create embedded setup files, together with our LVCL units.
The PasZip unit can be accessible in our Source Code repository: login as anonymous, then select Files and get the PasZip unit.
This PasZip unit is expected to work with Delphi up to 2007 version. New Unicode versions (2009/2010) of Delphi are not supported directly: use our SynPas unit instead, of make the corrections.
Offline
I've used such a method, embedding all necessary files as a .zip archive inside the exe resources, then extracting on request.
For example, we use HunSpell.dll external library to implement the spell checking of our SynProject open source tool.
Here is some extract from ProjectSpellCheck.pas:
constructor THunSpell.Create(DictionaryName: string='');
var Temp, HunSpell, Aff, Dic: TFileName;
i: integer;
begin
if DictionaryName='' then
DictionaryName := 'en_US';
Temp := GetSynopseCommonAppDataPath;
HunSpell := Temp+'hunspell.dll';
with TZipRead.Create(HInstance,'Zip','ZIP') do
try
Aff := DictionaryName+'.aff';
if not FileExists(Temp+Aff) then
StringToFile(Temp+Aff,UnZip(NameToIndex(Aff)));
Dic := DictionaryName+'.dic';
if not FileExists(Temp+Dic) then
StringToFile(Temp+Dic,UnZip(NameToIndex(Dic)));
if not FileExists(HunSpell) then
StringToFile(HunSpell,UnZip(NameToIndex('hunspell.dll')));
finally
Free;
end;
fHunLib := SafeLoadLibrary(HunSpell);
if fHunLib=0 then
exit;
if not LoadEntryPoints then begin
FreeLibrary(fHunLib);
fHunLib := 0;
exit;
end;
fDictionaryName := DictionaryName;
fHunHandle := Hunspell_create(pointer(Temp+Aff),pointer(Temp+Dic));
if fHunHandle=nil then
exit;
(....)
end;
We extract both hunspell.dll and all dictionary files from an embedded .zip resource. It use our free SynZip unit.
See http://synopse.info/fossil/artifact/4ca … a7726e27f9
Here is the content of the ProjectRes.rc file used to create the resource:
Default TXT "Default.ini"
Zip ZIP "ProjectRes.zip"
wizard2 10 "wizard2.png"
All necessary files are stored in the ProjectRes.Zip archive, then extracted on the fly.
Offline
TZipRead and TZipWrite now handle Unicode file name inside zip (UTF-8 encoded).
Follows appendix D as specified by http://www.pkware.com/documents/casestudies/APPNOTE.TXT
Under Unicode Delphi (Delphi 2009/2010/XE), it will allow full Unicode encoding of file name inside the .zip.
It will also allow TZipWrite to append some content to an existing .zip file, with no copy on disk: it will be very fast e.g. for .log adding into a .zip archive.
For both units SynZip and SynZipFiles
See http://synopse.info/fossil/fdiff?v1=42b … 89b5a55f86
and http://synopse.info/fossil/fdiff?v1=48b … b22b2ca8c1
Offline
how can unzip password protected zip?
Offline
how can unzip password protected zip?
This feature is not implemented yet.
You can use your own encryption using the SynCrypto unit, if you wish, but it won't be the standard Zip encryption.
For standard Zip encryption (AES or RC4), you'll have to use another unit.
Offline
what is `data` in the following code?
procedure TestCompression;
var FN: TFileName;
begin
FN := ChangeFileExt(paramstr(0),'.zip');
with TZipWrite.Create(FN) do
try
AddDeflated('one.exe',pointer(Data),length(Data)); //here what is data?
assert(Count=1);
AddDeflated('ident.txt',M.Memory,M.Position); //here what is M?
assert(Count=2);
finally
Free;
end;
end;
im trying this code for compressing a jpg file
procedure TestCompression;
var FN: TFileName;
Data : string;
begin
FN := ChangeFileExt(paramstr(0),'.zip');
with TZipWrite.Create(FN) do
try
AddDeflated('C:\Documents and Settings\All Users\Documents\leakTracker\14_06_12 11_33_50 AM.jpg',pointer(Data),length(Data));
assert(Count=1);
except
on E : Exception do
free ;
end;
end;
it does not work, it shows the zip size a s 1 KB
can you tell me what is wrong?
Offline
Both data and M are members of the test class.
They contain some data used to be compressed by the test routines.
Just click on variable with Ctrl on keyboard to navigate to the declaration of each variable.
So you are compressing the test data and name it as a .jpg file.
Offline
Ok, got it
i tried
procedure SynZipZipper(sZipFileName,sJpegFileName : string);
var
Data : string;
sUmlDPath : string;
begin
if FileExists(sZipFileName) then DeleteFile(pchar(sZipFileName));
with TZipWrite.Create(sZipFileName) do
try
AddDeflated(sJpegFileName);
AddDeflated(FullUmldFilePath);
free;
except
on E : Exception do
begin
free;
end;
end;
end;
Offline
procedure synZipZipper(const aSourceFilename,aTargetFilename:string);
begin if (fileExists(aTargetFilename)) then DeleteFile(aTargetFilename);
with TZipWrite.Create(aTargetFilename) do
try AddDeflated(aSourceFilename)
finally free end
end;
Offline
http://rouse.drkb.ru/components.php#fwzip
open source alternative with plenty of opportunities: large files, few need memory, password protect archive and unarchive, ppmd
Offline
@phillip
This is indeed better with a try... finally Free end to protect your TZipWrite instance.
Like any other Delphi class, by the way.
As I wrote above - see http://synopse.info/forum/viewtopic.php?pid=163#p163
Offline
Hi!
I zipped a file in Java using a ZipOutputStream object and when I try to unzip it in Delphi using the TZipRead class I get the error: "Error -2 during zip/deflate process."
Any ideas on how to fix it?
Thanks a lot!
Offline
Offline
Thanks for your reply.
Yes, I can open the generated file within Windows Explorer and 7-Zip.
Offline
Deflate.
Offline
The .zip file you sent to me is in an unsupported format.
The local fileInfo block has zcrc32=0 zzipSize=0 zfullZize=0.
This is clearly stated by the http://www.pkware.com/documents/casestudies/APPNOTE.TXT specifications as a possibility, if the bit 3 of the fileInfo "flags" is set.
A "data descriptor" block has to be recognized and read instead of the local fileInfo block.
We have therefore fixed UnZip() when crc and sizes are stored not within the file header but in this separate data descriptor block, after the compressed data.
It will either:
- Use the ending "central directory" information;
- Or search manually the "data descriptor" from the binary local data.
See http://synopse.info/fossil/info/324905c71ac
We have also added a new TZipRead.RetrieveFileInfo() method for this purpose.
Offline
Congrats, once again your code is the fastest of various I tried:
KAzip (very old component/library, had to update it for unicode/modern delphi)
Delphi's
Synopse
Both for listing and extracting your unit was the fastest after a series of benchmarks (listing the contents of 1000 zips, then extracting 1 a few times over).
Synopse was in both cases some ~10-20% faster than the other 2.
How do you extract a file btw? maybe my proc can be improved someway:
ss: TZipRead;
aa: RawByteString;
ss := TZipRead.Create(zip);
c := ss.NameToIndex(fil);
SetLength(aa,ss.Entry[c].infoLocal.zfullSize);
aa := ss.UnZip(c);
With TFileStream.Create(s,fmCreate) do begin
Write(aa[1],ss.Entry[c].infoLocal.zfullSize);
Free;
end;
Offline
The following methods are able to unzip files directly into a destination folder:
/// uncompress a file stored inside the .zip archive into a destination directory
function UnZip(aIndex: integer; const DestDir: TFileName;
DestDirIsFileName: boolean=false): boolean; overload;
/// uncompress a file stored inside the .zip archive into a destination directory
function UnZip(const aName, DestDir: TFileName;
DestDirIsFileName: boolean=false): boolean; overload;
/// uncompress all fields stored inside the .zip archive into the supplied
// destination directory
// - returns -1 on success, or the index in Entry[] of the failing file
function UnZipAll(DestDir: TFileName): integer;
Ensure you retrieved the latest version from our source code repository.
Offline
That works great, didn't see the overloads somehow, thanks.
ps: does the board support watching threads? it would be nice to be able to receive email notifications of replies.
Offline
Maybe you could migrate the board to phpbb
Offline
Reporting a possible issue: I'm getting an exception "ZIP format." with a very specific zip file, after calling
TZipRead.Create(zip)
it triggers here in SynZip.pas
for i := 0 to 127 do begin // resources size may be rounded up to alignment
lhr := @BufZip[Size-sizeof(TLastHeader)];
if lhr^.signature+1=(LASTHEADER_SIGNATURE+1) then
break;
dec(Size);
if Size<=sizeof(lhr^) then
break;
end;
if lhr^.signature+1<>(LASTHEADER_SIGNATURE+1) then begin
UnMap;
raise ESynZipException.Create('ZIP format');
end;
The file opens fine with winrar and 7zip, and if I make a new file with the contents (unpack and repack with winrar), it works fine as well with SynZip
Offline
That works. In the case of 2 files I found with this issue, "i" was 1233 and 2714 after the loop (I set 65535 max)
Another issue with some other files:
"Zip error: ZIP format size=0"
In 2 cases I found (working fine with winrar etc), both zzipSize and zfullSize were 0
Offline
TZipRead.UnZip is declared as returning a string, but it seems to be a relic of pre-Unicode Delphi, shouldn't it be a an AnsiString or a byte array? it's in PasZip, which apparently is a relic
In SynZip, TZipRead.Create raises an exception when a an entry has zero bytes, but a zero size file in a zip is valid.
Last edited by Eric (2015-06-04 11:13:14)
Offline
How can I store relative paths using TZipWrite? Thanks!
Last edited by paolo.sciarrini (2015-07-30 14:45:16)
Offline
I use SynZip.pas on Lazarus. When I create zip archive with unicode filenames, it work fine, but when unzip, have error. After some changes in SynZip.pas I have support for unicode names in zip archive.
1) In uses add LazFileUtils
2) In constructor TZipRead.Create(const aFileName: TFileName; ZipStartOffset, Size: cardinal);
replace string
file_ := CreateFile(pointer(aFileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
with
file_ := CreateFileW(PWideChar(WideString(aFileName)), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
result
constructor TZipRead.Create(const aFileName: TFileName; ZipStartOffset, Size: cardinal);
begin
file_ := CreateFileW(PWideChar(WideString(aFileName)), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
Create(file_,ZipStartOffset, Size);
end;
3) In function EnsurePath(var Path: TFileName): boolean
replace strings
if DirectoryExists(Path) then
...
if CreateDirectory(pointer(path),nil) then
with
if DirectoryExistsUTF8(Path) then
...
if CreateDirectoryW(PWideChar(WideString(path)),nil) then
result
function EnsurePath(var Path: TFileName): boolean;
var Parent: TFileName;
begin
result := false;
if Path='' then exit;
if Path[length(Path)]<>'\' then
Path := Path+'\';
if DirectoryExistsUTF8(Path) then
result := true else begin
Parent := ExtractFilePath(system.copy(Path,1,length(Path)-1));
if (Parent<>'') and not DirectoryExists(Parent) then
if not EnsurePath(Parent) then exit;
if CreateDirectoryW(PWideChar(WideString(path)),nil) then
result := true;
end;
end;
After this changes I can unzip files with unicode filenames
Offline
I create a small fix for AddFromZip - current implementation of AddFromZip produce invalid zip file in case source ZIP entry is STORED (not compressed) - see https://github.com/synopse/mORMot/pull/399
Not sure this is a best solution, but it works.
P.S. - in mORMot2 AddFromZip implementation is differs but seams has the same issue
Last edited by mpv (2021-07-05 10:54:31)
Online
Offline
https://github.com/synopse/mORMot/commi … 768be9b847 should fix it.
Your origin.zip file had a weird format with an "extralen" content.
How did you create it?
Offline
Thanks! After paths with
fhr.fileInfo.extraLen := 0; // source may have something here
all works.
My zip file is created using zip program from Ubuntu distribution
echo "Привет" > fileUtf8.txt && zip origin.zip fileUtf8.txt
Online
So Ubuntu "zip" add an "extralen" content to each zip entry.
My guess is that it had nothing to do with "stored" content.
This "extralen" content is likely to contain UNIX file properties.
It should now be handled in both mORMot 1 and mORMot 2.
Offline
Another small fix - https://github.com/synopse/mORMot/pull/401 for TZipWriteAbstract.AddFromZip - we must use an original file name instead of one, where we replace '/' -> '\' (see pull request description)
Last edited by mpv (2021-08-13 08:48:10)
Online
Thank you very much for all your hard work. I have two questions if you don't mind please.
1. Could you provide a code snippet on how to accomplish: use the TZipWrite class provided, and its AddFromZip() method to create your exe from Delphi code: you can even compress and append your images on the fly, with no temporary pictures.zip file. Just looking to append a zip file to an exe and extract it.
I don't quite understand it.
2. Any suggestions on how to implement a progressbar for zip extraction?
Thank you.
Offline
Does TZipWrite support adding comments to the zip file?
Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.
Offline
Please also note that there are two types of comments:
- "zip file comment" and,
- "comment of a file inside the zip file".
Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.
Offline
no problem, just providing the info.
Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.
Offline