Search notes:
Oracle: PL/SQL package ZIPPER to create ZIP files
The PL/SQL package
zipper
allows to create
zip files .
create or replace package zipper as -- {
--
-- Implementation by Anton Scheffer
-- See https://community.oracle.com/message/4510157#4510157
--
procedure addFile(zip in out blob, filename in varchar2, content in blob);
procedure finish (zip in out blob);
end zipper; -- }
/
create or replace package body zipper as -- {
--
-- Implementation by Anton Scheffer
-- See https://community.oracle.com/message/4510157#4510157
--
function littleEndian(p_big in number, p_bytes in pls_integer := 4) return raw is -- {
begin
return utl_raw.substr(utl_raw.cast_from_binary_integer(p_big, utl_raw.little_endian), 1, p_bytes);
end littleEndian; -- }
procedure addFile(zip in out blob, filename in varchar2, content in blob) is -- {
v_now date;
lz_blob blob;
lz_len integer;
cn_len integer;
blob_temp blob;
begin
v_now := sysdate;
lz_blob := utl_compress.lz_compress(content);
lz_len := dbms_lob.getlength(lz_blob);
cn_len := dbms_lob.getlength(content);
dbms_lob.append(
zip,
utl_raw.concat(
hextoraw('504B0304'), -- Local file header signature
hextoraw('1400' ), -- version 2.0
hextoraw('0000' ), -- no General purpose bits
hextoraw('0800' ), -- deflate
--
-- File last modification time
--
littleEndian(
to_number(to_char(v_now, 'ss' )) / 2 +
to_number(to_char(v_now, 'mi' )) * 32 +
to_number(to_char(v_now, 'hh24')) * 2048,
2
),
--
-- File last modification date
--
littleEndian(
to_number(to_char(v_now, 'dd' )) +
to_number(to_char(v_now, 'mm' )) * 32 +
(to_number(to_char(v_now, 'yyyy')) - 1980 ) * 512,
2
),
--
--
--
dbms_lob.substr(lz_blob, 4, lz_len - 7), -- CRC-32
littleEndian(lz_len - 18), -- compressed size
littleEndian(dbms_lob.getlength(content)), -- uncompressed size
littleEndian(length(filename), 2), -- File name length
hextoraw('0000'), -- Extra field length
utl_raw.cast_to_raw(filename) -- File name
)
);
blob_temp := blob_wrapper.substr(lz_blob, lz_len-18, 11);
dbms_lob.append(zip, blob_temp);
dbms_lob.freetemporary(blob_temp);
end addFile; -- }
procedure finish(zip in out blob) -- {
is
v_cnt pls_integer := 0;
v_offs integer;
v_offs_dir_header integer;
v_offs_end_header integer;
v_comment raw(32767) := utl_raw.cast_to_raw('Implementation by Anton Scheffer');
begin
v_offs_dir_header := dbms_lob.getlength(zip);
v_offs := dbms_lob.instr(zip, hextoraw('504B0304'), 1);
while v_offs > 0 loop
v_cnt := v_cnt + 1;
dbms_lob.append(
zip,
utl_raw.concat(
hextoraw('504B0102'), -- Central directory file header signature
hextoraw('1400' ), -- version 2.0
dbms_lob.substr(zip, 26, v_offs + 4),
hextoraw('0000' ), -- File comment length
hextoraw('0000' ), -- Disk number where file starts
hextoraw('0100' ), -- Internal file attributes
hextoraw('2000B681'), -- External file attributes
littleEndian(v_offs - 1), -- Relative offset of local file header
--
-- File name
--
dbms_lob.substr(
zip,
utl_raw.cast_to_binary_integer(
dbms_lob.substr(zip, 2, v_offs + 26 ),
utl_raw.little_endian
),
v_offs + 30
)
)
);
v_offs := dbms_lob.instr(zip, hextoraw('504B0304'), v_offs + 32);
end loop;
v_offs_end_header := dbms_lob.getlength(zip);
dbms_lob.append(
zip,
utl_raw.concat(
hextoraw('504B0506' ), -- End of central directory signature
hextoraw('0000' ), -- Number of this disk
hextoraw('0000' ), -- Disk where central directory starts
littleEndian(v_cnt, 2 ), -- Number of central directory records on this disk
littleEndian(v_cnt, 2 ), -- Total number of central directory records
littleEndian(v_offs_end_header - v_offs_dir_header), -- Size of central directory
littleEndian(v_offs_dir_header), -- Relative offset of local file header
littleEndian(nvl(utl_raw.length(v_comment), 0), 2), -- ZIP file comment length
v_comment
)
);
end finish; -- }
end zipper; -- }
/
show errors
$ del c:\temp\tq84.zip 2>nul
create directory zip_dir as 'c:\temp';
declare
zip blob;
file1 blob := utl_raw.cast_to_raw('Hello world!' );
file2 blob := utl_raw.cast_to_raw('foo bar baz' );
file3 blob := utl_raw.cast_to_raw('one two three' );
file4 blob := utl_raw.cast_to_raw('file four' || chr(13) || chr(10) || 'second line');
large_file blob;
i number := 1;
begin
dbms_lob.createTemporary(zip , true);
dbms_lob.createTemporary(large_file, true);
while i < 1721057 loop
dbms_lob.append(large_file, utl_raw.cast_to_raw(to_char(to_date(i, 'j'),'jsp') || chr(13) || chr(10)));
i := i + 1;
end loop;
zipper.addFile(zip, 'hi-world.txt' , file1 );
zipper.addFile(zip, 'file_2.txt' , file2 );
zipper.addFile(zip, 'subdir1/file_3.txt' , file3 );
zipper.addFile(zip, 'subdir1/subdir2/four_lines.txt', file4 );
zipper.addFile(zip, 'subdir1/large/file.txt' , large_file );
zipper.finish (zip);
-- ../blob_wrapper/
blob_wrapper.to_file('ZIP_DIR', 'tq84.zip', zip);
dbms_lob.freeTemporary(large_file);
dbms_lob.freeTemporary(zip);
end;
/
drop directory zip_dir;
$ c:\temp\tq84.zip