View PDF File on APEX (Simple approach)

PDF (Portable Document Format) is a popular file format to store digital documents, many applications require to upload PDF documents. Displaying the uploaded document in the same app could be useful for the user because he can review the PDF file without downloading it. I'll show you a simple approach to display the PDF file on an APEX application. This is simple, but it has a problem, it will use the Browser's PDF viewer, it could be not correctly configured on some users and it will not work on mobiles. However, I hope this be useful for you.

This is the DEMO app where you can upload a PDF file and view it on the page, if this is not working, check your browser properties. This has been tested on Firefox 73 , Chrome 80, Opera 66, Safari 12 , Not working on Internet Explorer 11 or Edge.

WARNING!!!! This will fail with Large PDF Files. If you need to work with large PDF Files, try Daniel's Approach in the Comments Section.

PDF Viewer Demo APP

If this is what you are looking for, check the next code used to display the PDF:
DECLARE
   v_blob    BLOB := empty_blob(); --Blob to store the file content
   l_step    number := 22500;      --Steps in bytes to print the file content
BEGIN
--Validate if the Item is not null
if (:P3_ID_PDF is not null) then
  --Get the blob content of the file. (this could be a function instead)
    select blob_content
    into v_blob
    from apex_application_temp_files
    where ID = :P3_ID_PDF;
    
 -- Open temporary lob
    DBMS_LOB.OPEN(v_blob, DBMS_LOB.LOB_READONLY);

  --Write  the code to display the PDF
    htp.p( '<embed src="data:application/pdf;base64,' );
  --Loop through the blob content
  for i in 0 .. trunc((dbms_lob.getlength(v_blob) - 1 )/l_step) loop
       --Get a substring of the blob, encode it to Base64 and cast from RAW to varchar
        htp.p( utl_raw.cast_to_varchar2(utl_encode.base64_encode(dbms_lob.substr(v_blob, l_step, i * l_step + 1))));
    end loop;
  --Close the HTML tag and add some properties
    htp.p('" height="1000" width="1000" >');

 -- Close lob objects
    DBMS_LOB.CLOSE(v_blob);
else
 htp.p('Upload a PDF file and Select it in the report'||:P3_ID_PDF);
end if;

EXCEPTION WHEN OTHERS THEN
    htp.p('There was an error displaying the PDF file, Sorry');
END;
Line 8: First we get the Blob content of the file, here you can get the blob content from other sources like reading a file, selecting a blob content, a Web Service, etc.

Line 17: We open an embed tag, normally you have to use a URL as source, but in this case, we will print the Data on base64 codification.

Lines 19-22: We loop through the Blob content, in line 21 we make the following:
  • Take a substring of the blob
  • Encode the substring to base64, it returns a Raw type data
  • Then we cast it to varchar2
  • Finally, Print the base64 substring
Line 24: This closes the src parameter, adds other parameters and closes the embed tag. 

Line 27: Finally close the blob, don't forget this

With this you can display a PDF File on you APEX Apps, I hope was be useful for you. If you have a better way to display it, let me now, I want to learn other methods.


Sources:

Comments

  1. Hi Diego,

    thanks for your easy to understand blog post!
    Thus you asked about other ways to display a PDF file inline, here's how I would do it :)

    1) Create a download blob procedure like so: https://github.com/OraOpenSource/oos-utils/blob/b6217292bc744211eaee9e1d11ca7a0b495bc44d/source/packages/oos_util_web.pkb#L89

    2) Create an Application Process which calls that download procedure (lets call it DOWNLOAD_FILE), e.g.

    begin
    download_file(p_file_id => apex_application.g_x01);
    end;

    Application Processes can later be called via URL which is important, so we now have some kind of download URL for files...

    3) Create a PL/SQL region like you did, but only with this code:

    DECLARE
    l_download_url VARCHAR2(4000);
    BEGIN
    l_download_url := apex_util.prepare_url(p_url => 'f?p=' || :app_id || ':' || :app_page_id || ':' || :app_session ||
    ':APPLICATION_PROCESS=DOWNLOAD_FILE&X01=' || :p3_id_pdf);
    htp.p('');
    END;

    That should be it! :)

    This solution has multiple benefits:
    a) You save server CPU, thus the whole looping and base64 encoding isn't needed
    b) Faster page load thus the main HTML document is smaller (no large base64 encoded string)
    c) The file is fetched via a separate GET request
    d) The download procedure is now reusable on multiple places in your app

    Keep on blogging! ;)

    All the best
    Daniel

    ReplyDelete

Post a Comment

Popular posts from this blog

Set Custom APP Icon on APEX

ID Basado en trigger y secuencia para todas las tablas - Oracle