El siguiente código muestra como crear un archivo comprimido gzip (.gz) usando solo las funciones que aparecen en la unit zlib que viene con Delphi, sin necesidad de utilizar dlls externas. Al utilizar este formato podremos abrir los datos comprimidos con otros programas como gzip, 7zip, winrar, etc ... pudiendo así generar ficheros que podrán ser abiertos por otras personas, sin necesidad de que tengan instalada nuestra aplicación.
El archivo se crea siguiendo el formato que se describe en este documento RFC 1952 GZIP File Format Specification version 4.3. Como se puede leer en el texto anterior, el formato gzip es bastante sencillo, tiene una cabecera, los datos comprimidos y un "pie" y solo permite comprimir un archivo de cada vez, por eso normalmente se suele combinar con otros formatos como "tar" para poder comprimir mas de un fichero dentro del mismo archivo comprimido.
La siguiente "unit" exporta una función (MakeGZ) que crea un fichero .gz a partir de un Stream de entrada y lo guarda en un Stream de salida, permitiendo así comprimir un fichero, texto, datos en memoria o cualquier otra cosa que pueda ser manejado como un Stream, pudiendo guardar el fichero resultante en disco o en memoria.
unit gzip; interface uses Sysutils, Windows, Classes; procedure MakeGZ(Src, Dst: TStream); implementation uses zlib; type { +---+---+---+---+---+---+---+---+---+---+ |ID1|ID2|CM |FLG| MTIME |XFL|OS | +---+---+---+---+---+---+---+---+---+---+ } TGZHeader = packed record ID1: Byte; // IDentification 1 = 31 ID2: Byte; // IDentification 2 = 139 CM: Byte; // Compression Method = 8 FLG: Byte; // Flags MTIME: Cardinal; // Modification TIME XFL: Byte; // Extra Flags OS: Byte; // Operating System end; { +---+---+---+---+---+---+---+---+ | CRC32 | ISIZE | +---+---+---+---+---+---+---+---+ } TGZFooter = packed record CRC32: Cardinal; // CRC-32 ISIZE: Cardinal; // Input SIZE end; var CRCTable: array[0..255] of Cardinal; procedure MakeCRCTable; var c: Cardinal; n,k: Integer; begin for n:= 0 to 255 do begin c:= n; for k:= 0 to 7 do if Odd(c) then c:= $edb88320 xor (c shr 1) else c:= c shr 1; CRCTable[n]:= c; end; end; function CCheck(code: Integer): Integer; begin Result := code; if code < 0 then raise ECompressionError.Create('Error al comprimir'); end; // Calcula el CRC-32 function UpdateCRC(CRC: Cardinal; Buffer: PByte; Len: Integer): Cardinal; begin CRC:= CRC xor $ffffffff; while Len > 0 do begin CRC:= CRCTable[(CRC xor Cardinal(Buffer^)) and $ff] xor (CRC shr 8); dec(Len); inc(Buffer); end; Result:= CRC xor $ffffffff; end; const BUFFERSIZE = High(Word); procedure MakeGZ(Src, Dst: TStream); var GZHeader: TGZHeader; GZFooter: TGZFooter; Zrec: TZStreamRec; TmpStream: TMemoryStream; inBuffer,outBuffer: PChar; n: Integer; // Utilizamos este buffer temporal para eliminar la cabecera y el pie de zlib procedure WriteTempBuffer(Buffer: PChar; Count: Longint); begin // Si es la primera escritura if TmpStream.Size = 0 then begin // Eliminamos la cabecera inc(Buffer,2); dec(count,2); end else Dst.WriteBuffer(TmpStream.Memory^,TmpStream.Size); TmpStream.Clear; TmpStream.WriteBuffer(Buffer^,Count); end; begin // Inicializamos la cabecera with GZHeader do begin ID1:= 31; ID2:= 139; CM:= 8; FLG:= 0; MTIME:= 0; XFL:= 0; OS:= 255; end; // Incializamos el pie GZFooter.CRC32:= 0; GZFooter.ISIZE:= 0; // Creamos un buffer temporal TmpStream:= TMemoryStream.Create; try // Creamos el buffer de entrada GetMem(inBuffer,BUFFERSIZE); try // Creamos el buffer de salida GetMem(outBuffer,BUFFERSIZE); try // Escribimos la cabecera del archivo gz al stream de salida Dst.WriteBuffer(GZHeader,Sizeof(GZHeader)); FillChar(ZRec,Sizeof(ZRec),#0); ZRec.zalloc:= zlibAllocMem; ZRec.zfree:= zlibFreeMem; ZRec.next_out:= outBuffer; ZRec.avail_out:= BUFFERSIZE; // Inicializamos zlib CCheck(deflateInit_(ZRec, Z_DEFAULT_COMPRESSION, zlib_version, sizeof(ZRec))); repeat n:= Src.Read(inBuffer^,BUFFERSIZE); // Ajustamos los valores del pie inc(GZFooter.ISIZE,n); GZFooter.CRC32:= UpdateCRC(GZFooter.CRC32,PByte(inBuffer),n); ZRec.next_in:= inBuffer; ZRec.avail_in:= n; while (ZRec.avail_in > 0) do begin CCheck(deflate(ZRec, 0)); if ZRec.avail_out = 0 then begin WriteTempBuffer(outBuffer, BUFFERSIZE); ZRec.next_out:= outBuffer; ZRec.avail_out:= BUFFERSIZE; end; end; until n < BUFFERSIZE; ZRec.next_in:= nil; ZRec.avail_in:= 0; try while (CCheck(deflate(ZRec, Z_FINISH)) <> Z_STREAM_END) and (ZRec.avail_out = 0) do begin WriteTempBuffer(outBuffer, BUFFERSIZE); ZRec.next_out:= outBuffer; ZRec.avail_out := BUFFERSIZE; end; if ZRec.avail_out < BUFFERSIZE then WriteTempBuffer(outBuffer, BUFFERSIZE - ZRec.avail_out); finally deflateEnd(ZRec); end; // Escribimos los bytes que quedan en el pie menos los 4 bytes que añade zlib Dst.WriteBuffer(TmpStream.Memory^,TmpStream.Size-4); // Escribimos el pie Dst.WriteBuffer(GZFooter,Sizeof(GZFooter)); finally FreeMem(outBuffer); end; finally FreeMem(inBuffer); end; finally TmpStream.Free; end; end; initialization MakeCRCTable; end.
Un ejemplo de como usarla:
var Src, Dst: TFileStream; begin Src:= TFileStream.Create('C:\Origen.txt',fmOpenRead); try Dst:= TFileStream.Create('C:\Origen.txt.gz',fmCreate); try MakeGZ(Src,Dst); finally Dst.Free; end; finally Src.Free; end; end;
Enlaces de interés:
http://www.gzip.org/zlib/rfc-gzip.html
Comentarios
Buenas compañero La verdad es
Buenas compañero
La verdad es que hace años que uso la librería zlib para hacer archivos comprimidos y no sabía que se podía crear archivos gz con ella y que, como bien dices, hace compatible este formato de compresión con los descompresores más estándares del mercado.
De hecho, hasta me llegué a hacer un pequeño des/compresor de archivos zlib, muy sencillo y rústico, pero que me servía para ver el contenido de los archivos generados por mi aplicación y poder descomprimirlos de forma rápida.
En fin, que no me enrollo más, que muchas gracias por compartir este código con todos.
Nos leemos
cadetill
Gracias Cadetill, Si te
Gracias Cadetill,
Si te interesa el tema no te pierdas mi ultimo post, donde explico como hacer un archivo tar.gz, para así poder añadir mas de un fichero, e incluso toda la estructura de directorios, a un fichero comprimido
http://delphi.jmrds.com/node/63
Nos leemos
hola seoane.
Hola Seoane.
una duda enorme. como se puede agregar un progressbar al usar zlib.pas. Con el código uno comprime y solo espera a que termine, pero esta muy de moda el poder ver el progreso de la compresión.
Saludos..
En ese caso ya tendrías que
En ese caso ya tendrías que integrarlo todo dentro de tu aplicación poniendo un progressbar en el formulario y actualizándolo desde el la función MakeGZ.
Concretamente dentro del bucle
Ahí tienes que introducir una instrucción para incrementar el progressbar, y una llamada a Application.Processmessage para que se actualice.
Saludos
Me podrían ayudar por favor
Me podrían ayudar por favor necesito crear el gzip pero poder seleccionar el archivo que quiero comprimir como lo puedo hacer gracias
¿A que te refieres con
¿A que te refieres con "seleccionarlo"?.
En el código se puede ver claramente donde indicamos que archivo queremos comprimir:
he estado trantando de usar
he estado trantando de usar la Unit pero me da error en esta asignación
ZRec.next_out:= outBuffer;
me dice que son imconpatibles los tipos char y ansichar
Cambiando
Cambiando
Por esto, debería funcionar:
En las versiones anteriores de Delphi PChar y PAnsiChar eran equivalente, en las nuevas ya no.
Saludos