Unzipping in Oracle APEX has changed - Examples Galore

With around 20 years on the job, Matt is one of the most experienced software developers at Pretius. He likes meeting new people, traveling to conferences, and working on different projects.
He’s also a big sports fan (regularly watches Leeds United, Formula 1, and boxing), and not just as a spectator – he often starts his days on a mountain bike, to tune his mind.
I wanted to unzip a file using APEX_ZIP today, and found that the old method was deprecated and now there is a completely different method. Lets unpack it all with examples aplenty.
Preparation
For this demonstration, I’ve decided to zip up the apex\utilities folder, from the APEX download site which contains these files:

Giving a ZIP file looking like this:

I’ve uploaded the zip it to this table
CREATE TABLE "MY_FILES"
( "ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
"MY_FILE" BLOB,
"MY_FILE_FILENAME" VARCHAR2(255 CHAR),
"MY_FILE_MIMETYPE" VARCHAR2(255 CHAR),
"MY_FILE_CHARSET" VARCHAR2(255 CHAR),
"MY_FILE_LASTUPD" DATE,
CONSTRAINT "MY_FILES_ID_PK" PRIMARY KEY ("ID")
USING INDEX ENABLE
) ;
Great. We’re all set.
Documentation
Notice that the APEX 23.2 Documentation shows this:

However the APEX 24.1 Documentation shows this:

What these pictures are telling me is that the usual way of getting the list of files (GET_FILES) AND the method of getting a particular file (GET_FILE_CONTENT signature 1) are both deprecated!
No we are not doomed. There is a new method in town.
Unzipping files
Browsing the zip?
If you are browsing the zip, perhaps you are looking for particular files matching a regex or similar, then consider this code:
DECLARE
l_zip_file blob;
l_unzipped_file blob;
l_dir apex_zip.t_dir_entries;
l_file_path varchar2(32767);
l_current_entry apex_zip.t_dir_entry;
BEGIN
-- Retrieve the zip file from the database
SELECT my_file
INTO l_zip_file
FROM my_files
WHERE my_file_filename = 'utilities.zip';
-- Get all directory entries from the zip file
l_dir := apex_zip.get_dir_entries (
p_zipped_blob => l_zip_file,
p_only_files => FALSE );
-- Print header row for the output table
DBMS_OUTPUT.PUT_LINE(RPAD('Dir', 5) || ' ' ||
RPAD('Size', 5) || ' ' ||
RPAD('Filename', 35) || ' ' ||
RPAD('Index', 35) || ' ' ||
'Uncompressed-Size');
-- Print separator line
DBMS_OUTPUT.PUT_LINE(RPAD('-', 101, '-'));
-- Start with the first entry in the directory
l_file_path := l_dir.first;
-- Loop through all entries in the zip file
WHILE l_file_path IS NOT NULL LOOP
-- Initialize unzipped file as empty BLOB
l_unzipped_file := EMPTY_BLOB();
-- Get current directory entry
l_current_entry := l_dir(l_file_path);
-- If the entry is not a directory, extract its content
IF l_current_entry.is_directory IS NULL THEN
l_unzipped_file := apex_zip.get_file_content (
p_zipped_blob => l_zip_file,
p_dir_entry => l_current_entry );
END IF;
-- Print information about the current entry
DBMS_OUTPUT.PUT_LINE(
apex_string.format( '%0 %1 %2 %3 %4' ,
RPAD(apex_debug.tochar(l_current_entry.is_directory), 5),
LPAD(l_current_entry.uncompressed_length, 5),
RPAD(l_current_entry.file_name, 35),
RPAD(l_file_path, 35),
LPAD(LENGTH(l_unzipped_file), 5)
));
-- Move to the next entry in the directory
l_file_path := l_dir.next(l_file_path);
END LOOP;
END;
this produces:
Dir Size Filename Index Uncompressed-Size
-----------------------------------------------------------------------------------------------------
null 28046 apxpart.sql apxpart.sql 28046
null 1815 apxrekey.sql apxrekey.sql 1815
null 4601 check_lovs_for_errors.sql check_lovs_for_errors.sql 4601
true 0 debug/ debug/ 0
null 12615 debug/activity.sql debug/activity.sql 12615
null 2724 debug/d0.sql debug/d0.sql 2724
null 2870 debug/d1.sql debug/d1.sql 2870
null 3112 debug/d2.sql debug/d2.sql 3112
null 2797 debug/ds.sql debug/ds.sql 2797
null 4683 enable_sso.sql enable_sso.sql 4683
null 15746 report_instance_configuration.sql report_instance_configuration.sql 15746
null 1782 reset_image_prefix.sql reset_image_prefix.sql 1782
null 2772 reset_image_prefix_con.sql reset_image_prefix_con.sql 2772
null 2642 reset_image_prefix_core.sql reset_image_prefix_core.sql 2642
true 0 support/ support/ 0
null 33979 support/apex_verify.sql support/apex_verify.sql 33979
We can observe that:
The presence of the
Uncompressed-Sizemeans that its been unzipped & the fact that its identical to thesizemeans its been correctly unzippedapex_zip.get_dir_entriesreturns an object of typet_dir_entrieswhich is atable of t_dir_entry index by VARCHAR2(32767). What is the index? - its the filename! This is why I can grab the filename by either of these two:l_file_path(index) ← shorter & easier!l_current_entry.file_name(attribute)
If the index was a
BINARY_INTEGERwe could do aFOR..LOOParound it. However its aVARCHAR2, therefore we need to LOOP around it using aWHILE l_file_path IS NOT NULL.If I want to grab the file, I have to set it the whole record type which I fetch on each loop. This is stored as
l_dir(l_file_path)and is used as an in parameter toapex_zip.get_file_contentl_unzipped_file := apex_zip.get_file_content ( p_zipped_blob => l_zip_file, p_dir_entry => l_current_entry );l_current_entry.is_directoryis true whenapex_zip.get_dir_entriesis called withp_only_files => FALSE. This means that I had to useIF l_current_entry.is_directory IS NULL THENto detect files. Alternatively you can omit thep_only_filesas this defaults to TRUE; the issue with this is thatl_current_entry.is_directorywill only ever benull. It cannot be FALSE - source.
Unzipping a particular file?
If you are looking for a particular file it becomes way easier
DECLARE
l_zip_file blob;
l_unzipped_file blob;
l_dir apex_zip.t_dir_entries;
l_file_path varchar2(32767);
l_current_entry apex_zip.t_dir_entry;
BEGIN
-- Retrieve the zip file from the database
SELECT my_file
INTO l_zip_file
FROM my_files
WHERE my_file_filename = 'utilities.zip';
-- Get all directory entries from the zip file
l_dir := apex_zip.get_dir_entries (
p_zipped_blob => l_zip_file );
-- Print header row for the output table
DBMS_OUTPUT.PUT_LINE(RPAD('Dir', 5) || ' ' ||
RPAD('Size', 5) || ' ' ||
RPAD('Filename', 35) || ' ' ||
RPAD('Index', 35) || ' ' ||
'Uncompressed-Size');
-- Print separator line
DBMS_OUTPUT.PUT_LINE(RPAD('-', 101, '-'));
l_file_path := 'support/apex_verify.sql';
l_current_entry := l_dir( l_file_path );
l_unzipped_file := apex_zip.get_file_content (
p_zipped_blob => l_zip_file,
p_dir_entry => l_current_entry );
-- Print information about the current entry
DBMS_OUTPUT.PUT_LINE(
apex_string.format( '%0 %1 %2 %3 %4' ,
RPAD(apex_debug.tochar(l_current_entry.is_directory), 5),
LPAD(l_current_entry.uncompressed_length, 5),
RPAD(l_current_entry.file_name, 35),
RPAD(l_file_path, 35),
LPAD(LENGTH(l_unzipped_file), 5)
));
END;
this produces
Dir Size Filename Index Uncompressed-Size
-----------------------------------------------------------------------------------------------------
null 33979 support/apex_verify.sql support/apex_verify.sql 33979
We can observe that:
No looping! We still need to call
apex_zip.get_dir_entrieswhich gives us our collection. We can then identify which record in the collection by the filename and then pass it intoapex_zip.get_file_contenti.e.l_file_path := 'support/apex_verify.sql'; l_current_entry := l_dir( l_file_path ); l_unzipped_file := apex_zip.get_file_content ( p_zipped_blob => l_zip_file, p_dir_entry => l_current_entry );
ENJOY
What’s the picture? It’s Cross James St. Harrogate. Visit Yorkshire!
Credits
Oracle APEX ZIP API - an Upgrade which is not officially documented - Zoran Tica
apex_zip.get_dir_entries - Oracle Documentation






