Você está na página 1de 9

HTML with Embedded Images from PL/SQL

 Background
 Setup
 Encoding Image Data

o Encoding Images from the File System


o Encoding Images from HTTP
o Encoding Images from a BLOB Column
 Test It

Background
The IMG tag allows you to include images in HTML. Typically you see this with the
source containing a URL, as shown below.

<img src="http://oracle-base.com/images/site_logo.gif" />

Using a URL in the tag means the browser (or email client) must make a separate
HTTP request to get the image. An alternative is to actually embed the image data into
the IMG tag. The basic format of the tag contents is shown below, where "mimetype" is
the mime type of the image ("image/png", "image/gif" etc.) and "data" is the base64
encoded data that makes up the image.

<img src="data:<mimetype>;base64,<data>" />

There are a couple of reasons why you might prefer this method:

 Reducing the total number of HTTP requests against your app server may
improve performance, provided you are not constantly embedding large images.
 When HTML may be viewed offline, like HTML emails, embedding the images
rather than using URLs can be advantageous.

This article illustrates how to build an embedded image in HTML using PL/SQL.

Setup
The example code in this article requires a number of objects to be created.
First, create a directory object pointing to a physical directory on the database server
that the "oracle" user has read/write permissions on. This will be used by the file system
examples.

CONN / AS SYSDBA
CREATE OR REPLACE DIRECTORY images AS '/host/';
GRANT READ, WRITE ON DIRECTORY images TO test;

Next, create and populate a table to hold images for the database example. In this case
I'm just using a single image called "site_logo.gif" that is loaded into the table from the
'IMAGES' directory created previously.

CONN test/test

CREATE TABLE images (


id NUMBER(10) NOT NULL,
name VARCHAR2(50) NOT NULL,
image BLOB NOT NULL,
CONSTRAINT images_pk PRIMARY KEY (id),
CONSTRAINT images_uk UNIQUE (name)
);

CREATE SEQUENCE images_seq;

DECLARE
l_dir VARCHAR2(10) := 'IMAGES';
l_file VARCHAR2(20) := 'site_logo.gif';
l_bfile BFILE;
l_blob BLOB;

l_dest_offset INTEGER := 1;
l_src_offset INTEGER := 1;
BEGIN
INSERT INTO images (id, name, image)
VALUES (images_seq.NEXTVAL, l_file, empty_blob())
RETURN image INTO l_blob;
l_bfile := BFILENAME(l_dir, l_file);
DBMS_LOB.fileopen(l_bfile, DBMS_LOB.file_readonly);
-- loadfromfile deprecated.
-- DBMS_LOB.loadfromfile(l_blob, l_bfile, DBMS_LOB.getlength(l_bfile));
DBMS_LOB.loadblobfromfile (
dest_lob => l_blob,
src_bfile => l_bfile,
amount => DBMS_LOB.lobmaxsize,
dest_offset => l_dest_offset,
src_offset => l_src_offset);
DBMS_LOB.fileclose(l_bfile);

COMMIT;
END;
/

The HTTP example needs access to the internet (or some other HTTP server). If you
are using Oracle 11g, you will need to make sure an appropriate ACL is present to allow
network access from the database. You can see how this is done here.
We need a way of checking that the HTML is actually generated correctly. To do that we
will write it out to the file system using the following procedure.

CREATE OR REPLACE PROCEDURE create_file_from_clob (p_dir IN VARCHAR2,


p_file IN VARCHAR2,
p_clob IN OUT NOCOPY CL
OB)
AS
l_file UTL_FILE.file_type;
l_step PLS_INTEGER := 12000;
BEGIN
l_file := UTL_FILE.fopen(p_dir, p_file, 'w', 32767);
FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(p_clob) - 1 )/l_step) LOOP
UTL_FILE.put(l_file, DBMS_LOB.substr(p_clob, l_step, i * l_step + 1));
UTL_FILE.fflush(l_file);
END LOOP;
UTL_FILE.fclose(l_file);
END;
/

The code in first draft of the article was a little verbose. Anton Scheffer suggested using
the following function to do the base64 encoding, so I've switched across to it where
possible, or used a similar approach where a straight substitution doesn't fit.

CREATE OR REPLACE FUNCTION base64encode(p_blob IN BLOB)


RETURN CLOB
IS
l_clob CLOB;
l_step PLS_INTEGER := 12000; -- make sure you set a multiple of 3 not hi
gher than 24573
BEGIN
FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(p_blob) - 1 )/l_step) LOOP
l_clob := l_clob || UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(
DBMS_LOB.substr(p_blob, l_step, i * l_step + 1)));
END LOOP;
RETURN l_clob;
END;
/

Encoding Image Data


The original image could come from the file system, a BLOB column in a table in the
database or a HTTP request from an app server. We shall deal with each of these
below.
Although I am using stored procedures in these examples, in a real system the code
would be placed in packages.

 Encoding Images from the File System


 Encoding Images from HTTP
 Encoding Images from a BLOB Column

Encoding Images from the File System


The following procedure uses the DBMS_LOB package to read chunks of data from
a BFILE pointing to the image on the filesystem. The UTL_ENCODE and UTL_RAW packages
are used to encode the data and convert it to a string suitable for inclusion into the
HTML.

CREATE OR REPLACE PROCEDURE get_enc_img_from_fs (p_dir IN VARCHAR2,


p_file IN VARCHAR2,
p_clob IN OUT NOCOPY CLOB
)
AS
l_bfile BFILE;
l_step PLS_INTEGER := 12000;
BEGIN
l_bfile := BFILENAME(p_dir, p_file);
DBMS_LOB.fileopen(l_bfile, DBMS_LOB.file_readonly);

FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(l_bfile) - 1 )/l_step) LOOP


p_clob := p_clob || UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(
DBMS_LOB.substr(l_bfile, l_step, i * l_step + 1)));
END LOOP;

DBMS_LOB.fileclose(l_bfile);
END;
/

Encoding Images from HTTP


The following procedure is similar to the previous one, except it reads data from a HTTP
request, rather than from the filesystem.

CREATE OR REPLACE PROCEDURE get_enc_img_from_http (p_url IN VARCHAR2,


p_clob IN OUT NOCOPY CL
OB)
AS
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
l_raw RAW(32767);
BEGIN
l_http_request := UTL_HTTP.begin_request(p_url);
l_http_response := UTL_HTTP.get_response(l_http_request);

BEGIN
LOOP
UTL_HTTP.read_raw(l_http_response, l_raw, 12000);
p_clob := p_clob || UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encod
e(l_raw));
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
UTL_HTTP.end_response(l_http_response);
END;
END;
/

Alternatively, you could achieve the same result using the HTTPURITYPE subtype of
the URITYPE. Thanks to Marco Gralike for reminding me about this.

CREATE OR REPLACE PROCEDURE get_enc_img_from_http (p_url IN VARCHAR2,


p_clob IN OUT NOCOPY CL
OB)
AS
BEGIN
p_clob := p_clob || base64encode(HTTPURITYPE.createuri(p_url).getblob())
;
END;
/

Encoding Images from a BLOB Column


The following procedure uses an image stored in a BLOB column of table.

CREATE OR REPLACE PROCEDURE get_enc_img_from_tab (p_image_name IN VARCHAR2


,
p_clob IN OUT NOCO
PY CLOB)
AS
BEGIN
SELECT p_clob || base64encode(image)
INTO p_clob
FROM images
WHERE name = p_image_name;
END;
/

Test It
We now have three procedures to retrieve image data and encode it so it can be added
to a HTML document. Next, we need to write the code to generate some HTML and call
one of the procedures to embed the image data. The following code creates a HTML
document in a temporary CLOB. Examples of all three procedure calls are present, so
try each of them out in turn.
Once the HTML is in the temporary CLOB it could be published as an email, a web
page (using the embedded PL/SQL gateway or a mod_plsql enabled application server)
or written to the file system. For simplicity I will just write it to the filesystem, but
examples of the other technologies are available from the links at the end of the article.
DECLARE
l_clob CLOB;
BEGIN
DBMS_LOB.createtemporary(l_clob, FALSE);

-- Build the start of the HTML document, including the start of the IMG
tag
-- and place it in a CLOB.
l_clob := '<html>
<head>
<title>Test HTML with Embedded Image</title>
</head>
<body>
<h1>Test HTML with Embedded Image</h1>
<p>And here it is:</p>
<img src="data:image/gif;base64,';

get_enc_img_from_fs (p_dir => 'IMAGES',


p_file => 'site_logo.gif',
p_clob => l_clob);

--get_enc_img_from_http (p_url => 'http://oracle-base.com/images/site_l


ogo.gif',
-- p_clob => l_clob);

--get_enc_img_from_tab (p_image_name => 'site_logo.gif',


-- p_clob => l_clob);

-- Close off the IMG tag and complete the HTML document.
l_clob := l_clob || '" alt="Site Logo" />
<p>The end.</p>
</body>
</html>';

-- The CLOB now contains the complete HTML with the embedded image, so d
o something with it.
-- In this case I'm going to write it to the file system.
create_file_from_clob (p_dir => 'IMAGES',
p_file => 'EmbeddedImageTest.htm',
p_clob => l_clob);

DBMS_LOB.freetemporary(l_clob);
EXCEPTION
WHEN OTHERS THEN
DBMS_LOB.freetemporary(l_clob);
RAISE;
END;
/

The output from all three calls should look exactly the same. You can see the output
from the code here. Remember to view the page source to prove the image has been
embedded into the HTML.
For more information see:

Você também pode gostar