alexandria-plsql-utils/ora/zip_util_pkg.pkb
2015-02-20 19:43:42 +01:00

529 lines
18 KiB
Plaintext
Executable File

CREATE OR REPLACE package body zip_util_pkg
is
/*
Purpose: Package handles zipping and unzipping of files
Remarks: by Anton Scheffer, see http://forums.oracle.com/forums/thread.jspa?messageID=9289744#9289744
for unzipping, see http://technology.amis.nl/blog/8090/parsing-a-microsoft-word-docx-and-unzip-zipfiles-with-plsql
for zipping, see http://forums.oracle.com/forums/thread.jspa?threadID=1115748&tstart=0
Who Date Description
------ ---------- --------------------------------
MBR 09.01.2011 Created
MBR 21.05.2012 Fixed a bug related to use of dbms_lob.substr in get_file (use dbms_lob.copy instead)
*/
function raw2num(
p_value in raw
)
return number
is
begin -- note: FFFFFFFF => -1
return utl_raw.cast_to_binary_integer( p_value
, utl_raw.little_endian
);
end;
--
function file2blob(
p_dir in varchar2
, p_file_name in varchar2
)
return blob
is
file_lob bfile;
file_blob blob;
begin
file_lob := bfilename( p_dir
, p_file_name
);
dbms_lob.open( file_lob
, dbms_lob.file_readonly
);
dbms_lob.createtemporary( file_blob
, true
);
dbms_lob.loadfromfile( file_blob
, file_lob
, dbms_lob.lobmaxsize
);
dbms_lob.close( file_lob );
return file_blob;
exception
when others
then
if dbms_lob.isopen( file_lob ) = 1
then
dbms_lob.close( file_lob );
end if;
if dbms_lob.istemporary( file_blob ) = 1
then
dbms_lob.freetemporary( file_blob );
end if;
raise;
end;
--
function raw2varchar2(
p_raw in raw
, p_encoding in varchar2
)
return varchar2
is
begin
return nvl
( utl_i18n.raw_to_char( p_raw
, p_encoding
)
, utl_i18n.raw_to_char
( p_raw
, utl_i18n.map_charset( p_encoding
, utl_i18n.generic_context
, utl_i18n.iana_to_oracle
)
)
);
end;
function get_file_list(
p_dir in varchar2
, p_zip_file in varchar2
, p_encoding in varchar2 := null
)
return t_file_list
is
begin
return get_file_list( file2blob( p_dir
, p_zip_file
)
, p_encoding
);
end;
--
function get_file_list(
p_zipped_blob in blob
, p_encoding in varchar2 := null
)
return t_file_list
is
t_ind integer;
t_hd_ind integer;
t_rv t_file_list;
begin
t_ind := dbms_lob.getlength( p_zipped_blob ) - 21;
loop
exit when dbms_lob.substr( p_zipped_blob
, 4
, t_ind
) = hextoraw( '504B0506' )
or t_ind < 1;
t_ind := t_ind - 1;
end loop;
--
if t_ind <= 0
then
return null;
end if;
--
t_hd_ind := raw2num( dbms_lob.substr( p_zipped_blob
, 4
, t_ind + 16
) ) + 1;
t_rv := t_file_list( );
t_rv.extend( raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_ind + 10
) ) );
for i in 1 .. raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_ind + 8
) )
loop
t_rv( i ) :=
raw2varchar2
( dbms_lob.substr( p_zipped_blob
, raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 28
) )
, t_hd_ind + 46
)
, p_encoding
);
t_hd_ind :=
t_hd_ind
+ 46
+ raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 28
) )
+ raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 30
) )
+ raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 32
) );
end loop;
--
return t_rv;
end;
--
function get_file(
p_dir in varchar2
, p_zip_file in varchar2
, p_file_name in varchar2
, p_encoding in varchar2 := null
)
return blob
is
begin
return get_file( file2blob( p_dir
, p_zip_file
)
, p_file_name
, p_encoding
);
end;
--
function get_file(
p_zipped_blob in blob
, p_file_name in varchar2
, p_encoding in varchar2 := null
)
return blob
is
t_tmp blob;
t_ind integer;
t_hd_ind integer;
t_fl_ind integer;
begin
t_ind := dbms_lob.getlength( p_zipped_blob ) - 21;
loop
exit when dbms_lob.substr( p_zipped_blob
, 4
, t_ind
) = hextoraw( '504B0506' )
or t_ind < 1;
t_ind := t_ind - 1;
end loop;
--
if t_ind <= 0
then
return null;
end if;
--
t_hd_ind := raw2num( dbms_lob.substr( p_zipped_blob
, 4
, t_ind + 16
) ) + 1;
for i in 1 .. raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_ind + 8
) )
loop
if p_file_name =
raw2varchar2
( dbms_lob.substr( p_zipped_blob
, raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 28
) )
, t_hd_ind + 46
)
, p_encoding
)
then
if dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 10
) = hextoraw( '0800' ) -- deflate
then
t_fl_ind :=
raw2num( dbms_lob.substr( p_zipped_blob
, 4
, t_hd_ind + 42
) );
t_tmp := hextoraw( '1F8B0800000000000003' ); -- gzip header
dbms_lob.copy( t_tmp
, p_zipped_blob
, raw2num( dbms_lob.substr( p_zipped_blob
, 4
, t_fl_ind + 19
) )
, 11
, t_fl_ind
+ 31
+ raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_fl_ind + 27
) )
+ raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_fl_ind + 29
) )
);
dbms_lob.append( t_tmp
, dbms_lob.substr( p_zipped_blob
, 4
, t_fl_ind + 15
)
);
dbms_lob.append( t_tmp
, dbms_lob.substr( p_zipped_blob, 4, t_fl_ind + 23 )
);
return utl_compress.lz_uncompress( t_tmp );
end if;
--
if dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 10
) =
hextoraw( '0000' )
-- The file is stored (no compression)
then
t_fl_ind :=
raw2num( dbms_lob.substr( p_zipped_blob
, 4
, t_hd_ind + 42
) );
dbms_lob.createtemporary(t_tmp, cache => true);
dbms_lob.copy(dest_lob => t_tmp,
src_lob => p_zipped_blob,
amount => raw2num( dbms_lob.substr( p_zipped_blob, 4, t_fl_ind + 19)),
dest_offset => 1,
src_offset => t_fl_ind + 31 + raw2num(dbms_lob.substr(p_zipped_blob, 2, t_fl_ind + 27)) + raw2num(dbms_lob.substr( p_zipped_blob, 2, t_fl_ind + 29))
);
return t_tmp;
end if;
end if;
t_hd_ind :=
t_hd_ind
+ 46
+ raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 28
) )
+ raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 30
) )
+ raw2num( dbms_lob.substr( p_zipped_blob
, 2
, t_hd_ind + 32
) );
end loop;
--
return null;
end;
--
function little_endian(
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;
--
procedure add_file(
p_zipped_blob in out blob
, p_name in varchar2
, p_content in blob
)
is
t_now date;
t_blob blob;
t_clen integer;
begin
t_now := sysdate;
t_blob := utl_compress.lz_compress( p_content );
t_clen := dbms_lob.getlength( t_blob );
if p_zipped_blob is null
then
dbms_lob.createtemporary( p_zipped_blob
, true
);
end if;
dbms_lob.append
( p_zipped_blob
, utl_raw.concat
( hextoraw( '504B0304' ) -- Local file header signature
, hextoraw( '1400' ) -- version 2.0
, hextoraw( '0000' ) -- no General purpose bits
, hextoraw( '0800' ) -- deflate
, little_endian
( to_number( to_char( t_now
, 'ss'
) ) / 2
+ to_number( to_char( t_now
, 'mi'
) ) * 32
+ to_number( to_char( t_now
, 'hh24'
) ) * 2048
, 2
) -- File last modification time
, little_endian
( to_number( to_char( t_now
, 'dd'
) )
+ to_number( to_char( t_now
, 'mm'
) ) * 32
+ ( to_number( to_char( t_now
, 'yyyy'
) ) - 1980 ) * 512
, 2
) -- File last modification date
, dbms_lob.substr( t_blob
, 4
, t_clen - 7
) -- CRC-32
, little_endian( t_clen - 18 ) -- compressed size
, little_endian( dbms_lob.getlength( p_content ) )
-- uncompressed size
, little_endian( length( p_name )
, 2
) -- File name length
, hextoraw( '0000' ) -- Extra field length
, utl_raw.cast_to_raw( p_name ) -- File name
)
);
dbms_lob.copy( p_zipped_blob
, t_blob
, t_clen - 18
, dbms_lob.getlength( p_zipped_blob ) + 1
, 11
); -- compressed content
dbms_lob.freetemporary( t_blob );
end;
--
procedure finish_zip(
p_zipped_blob in out blob
)
is
t_cnt pls_integer := 0;
t_offs integer;
t_offs_dir_header integer;
t_offs_end_header integer;
t_comment raw( 32767 )
:= utl_raw.cast_to_raw( 'Implementation by Anton Scheffer' );
begin
t_offs_dir_header := dbms_lob.getlength( p_zipped_blob );
t_offs := dbms_lob.instr( p_zipped_blob
, hextoraw( '504B0304' )
, 1
);
while t_offs > 0
loop
t_cnt := t_cnt + 1;
dbms_lob.append
( p_zipped_blob
, utl_raw.concat
( hextoraw( '504B0102' )
-- Central directory file header signature
, hextoraw( '1400' ) -- version 2.0
, dbms_lob.substr( p_zipped_blob
, 26
, t_offs + 4
)
, hextoraw( '0000' ) -- File comment length
, hextoraw( '0000' ) -- Disk number where file starts
, hextoraw( '0100' ) -- Internal file attributes
, hextoraw( '2000B681' ) -- External file attributes
, little_endian( t_offs - 1 )
-- Relative offset of local file header
, dbms_lob.substr
( p_zipped_blob
, utl_raw.cast_to_binary_integer
( dbms_lob.substr( p_zipped_blob
, 2
, t_offs + 26
)
, utl_raw.little_endian
)
, t_offs + 30
) -- File name
)
);
t_offs :=
dbms_lob.instr( p_zipped_blob
, hextoraw( '504B0304' )
, t_offs + 32
);
end loop;
t_offs_end_header := dbms_lob.getlength( p_zipped_blob );
dbms_lob.append
( p_zipped_blob
, utl_raw.concat
( hextoraw( '504B0506' ) -- End of central directory signature
, hextoraw( '0000' ) -- Number of this disk
, hextoraw( '0000' ) -- Disk where central directory starts
, little_endian
( t_cnt
, 2
) -- Number of central directory records on this disk
, little_endian( t_cnt
, 2
) -- Total number of central directory records
, little_endian( t_offs_end_header - t_offs_dir_header )
-- Size of central directory
, little_endian
( t_offs_dir_header )
-- Relative offset of local file header
, little_endian
( nvl( utl_raw.length( t_comment )
, 0
)
, 2
) -- ZIP file comment length
, t_comment
)
);
end;
--
procedure save_zip(
p_zipped_blob in blob
, p_dir in varchar2
, p_filename in varchar2
)
is
t_fh utl_file.file_type;
t_len pls_integer := 32767;
begin
t_fh := utl_file.fopen( p_dir
, p_filename
, 'wb'
);
for i in 0 .. trunc( ( dbms_lob.getlength( p_zipped_blob ) - 1 ) / t_len )
loop
utl_file.put_raw( t_fh
, dbms_lob.substr( p_zipped_blob
, t_len
, i * t_len + 1
)
);
end loop;
utl_file.fclose( t_fh );
end;
--
end zip_util_pkg;
/