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:

ColumnContainValue
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 Pets in the database

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&reg; 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 ...