Oracle® mod_plsql Upload and Download Procedures Example
This site describes a complete example for uploading and downloading documents directly from the database using mod_plsql.
It asumes, your Oracle® Database is up and running and you've an user with name Scott which is identified by tiger.
First a Database Access Descriptor (DAD) must be created. Add the following entry to your $ORACLE_HOME/Apache/modplsql/conf/dads.conf file:
<Location /pls/scott>
  SetHandler pls_handler
  Order deny,allow
  Allow from all
  AllowOverride None
  PlsqlDatabaseUsername       scott
  PlsqlDatabasePassword       tiger
  PlsqlDatabaseConnectString  tns_name
  PlsqlAuthenticationMode     Basic
  PlsqlDocumentTablename      scott.documents
  PlsqlDocumentPath           docs
  PlsqlDocumentProcedure      docs_api.download_doc
</Location<>
Take care that the value of PlsqlDatabaseConnectString points to an existing entry in the %ORACLE_HOME%/network/admin/tnsnames.ora file.
Having made changes to $ORACLE_HOME/Apache/modplsql/conf/dads.conf the OHS has to be restarted with the following command:
%ORACLE_HOME%/opmn/bin/opmnctl restartproc type=ohs
Create a document table with the following structure in Scott's schema (we are going to use a BLOB column only):
create table documents (
 name          varchar2(256) unique not null,
 mime_type     varchar2(128),
 doc_size      number,
 dad_charset   varchar2(128),
 last_updated  date,
 content_type  varchar2(128),
 blob_content  blob )
/
An example record after a successful upload would look like this:
| Column | Contain | Value | 
| name | file name | F18645/pets.gif | 
| mime_type | mime type | image/gif | 
| doc_size | size in bytes | 13542 | 
| dad_charset | DAD charset | ascii | 
| last_updated | last update | 26.02.2014 | 
| content_type | type of content | BLOB | 
| blob_content | content | ![]()  | 
As we've specified the PlsqlDocumentProcedure poiting to docs_api.download_doc, we now need a package called docs_api
with the upload and download procedures:
create or replace package docs_api as
procedure upload_doc;
procedure document_uti ( insert_doc in varchar2 default null, delete_doc in varchar2 default null );
procedure download_doc;
procedure download_doc ( doc in varchar2 );
end;
/
create or replace package body docs_api as
-- PAGEHEAD --
procedure pageHead( p_title in varchar2 default 'mod_plsql Document Management' ) is
begin
htp.p ( '<!DOCTYPE HTML>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
 body { font-family: Verdana, Arial, Helvetice, "Sans Serif" }
 table { border-collapse: collapse; }
 thead { background-color: #ededed; }
 th, td { border-bottom: 1px solid #777; padding: 12px; }
 img { max-width: 139px; }
 .btn-link { padding: 7px 10px; background: #4479cb; color: #ededed;
             -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; }
</style>
<title>' || p_title || '</title>
</head>' );
end pagehead;
-- UPLOAD_DOC --
procedure upload_doc as
begin
pagehead( 'Document Upload via mod_plsql' );
htp.p( '<body>
<h1>Document Upload via MOD_PLSQL</h1>
<div>
  <form enctype="multipart/form-data" action="docs_api.document_uti" method="post">
    <p>File to upload: <input type="file" name="insert_doc"></p>
    <p><input type="submit" value="start upload"></p>
  </form>
</div>
</body>
</html>' );
end upload_doc;
-- DOCUMENT_UTI --
procedure document_uti ( insert_doc in varchar2 default null, delete_doc in varchar2 default null ) is
l_doc_size varchar2(20);
begin
pagehead();
htp.p ( '<body>
<h1>Oracle® mod_plsql Document Utility</h1>
<p><a class="btn-link" href="docs_api.upload_doc">back to Upload page</a></p>' );
if
  insert_doc is not null
then
  begin
  for l_rec in ( select to_char ( doc_size ) doc_size from documents where name = insert_doc ) loop
    l_doc_size := l_rec.doc_size;
  end loop;
  if
    l_doc_size is not null
  then
    htp.print( '<p>Document upload of <strong>' || insert_doc || '</strong> was successful. Filesize is ' || l_doc_size || ' bytes.</p>' );
  else
    htp.print( '<p>Document upload of <strong>' || insert_doc || '</strong> was NOT successful. The document could <strong>NOT</strong> be found.</p>' );
  end if;
  exception
  when others
  then htp.print( 'Document Upload of ' || insert_doc || ' failed.' );
       htp.print( sqlerrm );
  end;
elsif
  delete_doc is not null
then
  begin
  delete from documents
  where  name = delete_doc;
  if
    sql%rowcount > 0
  then
    htp.print( '<p>Document <strong>' || delete_doc || '</strong> was successfully deleted.</p>' );
  else
    htp.print( '<p>Document <strong>' || delete_doc || '</strong> could not be deleted.</p><p>Either it was not found or there was another issue.</p>' );
  end if;
  exception
  when others
  then htp.print( 'Delete of document ' || delete_doc || ' failed.');
       htp.print( sqlerrm );
  end;
end if;
htp.p ( '<h3>Contents of Document table:</h3>
<table>
  <thead>
    <tr>
      <th>name</th>
      <th>mime type</th>
      <th>size</th>
      <th>charset</th>
      <th>updated</th>
      <th>content type</th>
      <th>content</th>
      <th>delete ?</th>
    </tr>
  </thead>' );
for cur_rec in ( select doc.name, doc.mime_type, doc.doc_size, doc.dad_charset,
                        to_char ( doc.last_updated, 'dd.mm.yyyy hh24:mi' ) last_updated, doc.content_type
                 from   documents doc
                 order by doc.last_updated ) loop
  htp.p ( '<tr>' );
  htp.p ( '<td><a href="docs/' || cur_rec.name || '" title="open with document access path">' || cur_rec.name || '</a></td>' );
  htp.p ( '<td>' || cur_rec.mime_type    || '</td>' );
  htp.p ( '<td>' || cur_rec.doc_size     || '</td>' );
  htp.p ( '<td>' || cur_rec.dad_charset  || '</td>' );
  htp.p ( '<td>' || cur_rec.last_updated || '</td>' );
  htp.p ( '<td>' || cur_rec.content_type || '</td>' );
  if
    lower ( cur_rec.mime_type ) like 'image%'
  then
    htp.p ( '<td><a href="docs_api.download_doc?doc=' || cur_rec.name || '" title="open with document access procedure"><img src="docs/' || cur_rec.name || '" /></a></td>' );
  else
    htp.p ( '<td><a href="docs_api.download_doc?doc=' || cur_rec.name || '" title="open with document access procedure">click to open</a></td>' );
  end if;
  htp.p ( '<td><a class="btn-link" href="docs_api.document_uti?insert_doc=&delete_doc=' || cur_rec.name || '">delete</a></td>' );
  htp.p ( '</tr>' );
end loop;
htp.p ( '</table>
</body>
</html>' );
end document_uti;
-- DOWNLOAD_DOC --
procedure download_doc is
l_doc_name constant varchar2(255) := substr( owa_util.get_cgi_env( 'path_info' ), 2 );
begin
wpg_docload.download_file( l_doc_name );
exception when others then
 pagehead( 'mod_plsql Download failure' );
 htp.p( '<body>
 <h1>mod_plsql Download failure</h1>' );
 htp.p( sqlerrm );
 htp.p ( '</body></html>' );
end download_doc;
-- DOWNLOAD_DOC overload --
procedure download_doc ( doc in varchar2 ) is
cursor get_document is
select d.blob_content, lower ( d.mime_type ) mime_type
from   documents d
where  d.name = doc;
l_document get_document%rowtype;
begin
open  get_document;
fetch get_document into l_document;
if
  get_document%notfound
then
  close get_document;
  raise_application_error( -20000, 'Document ' || doc || ' not found in documents table.' );
end if;
close get_document;
owa_util.mime_header( l_document.mime_type, false );
htp.print ( 'Content-Length: ' || to_char ( dbms_lob.getlength( l_document.blob_content ) ) );
if
  l_document.mime_type not like 'image%'
then
  htp.print ( 'Content-Disposition: attachment; filename=' || doc );
end if;
owa_util.http_header_close;
wpg_docload.download_file( l_document.blob_content );
exception when others then
 pagehead( 'mod_plsql Download failure' );
 htp.p( '<body>
 <h1>mod_plsql Download failure</h1>' );
 htp.p( sqlerrm );
 htp.p ( '</body></html>' );
end download_doc;
end;
/
To be continued ...
