|
 |
|
Oracle Java blob graphics retrieval
Oracle Database Tips by Donald Burleson |
Creating a Simple Application Using
Oracle, BLOBs and JAVA
Also see
how to insert a BLOB
image from a client PC .
 |
For immediate expert assistance in Java graphics for
images in Oracle, just call us or e-mail
. |
This simple application will provide a list of
graphics stored in the database to the user on request, allow the
user to enter an index value which is then used to query the
database and pull up the requested graphic for display.
From the description we can see that this
application is actually a combination of two applets, a canned query
return and a BLOB retrieval display. This application combines the
two into a single applet and adds the capability to specify the
graphic returned. Listing 9 shows the complete application.
/*
This applet demonstrates
advanced Lob support in the oci8 driver
*/
import java.awt.Graphics.*;
import java.awt.Image.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.sql.*;
import java.io.*;
import java.util.*;
// Import the Oracle Jdbc
driver package to make the code more readable
import oracle.jdbc.driver.*;
// Define the class as an
extension of the Applet calss
public class DisplayImage2
extends java.applet.Applet
implements ActionListener
{
// Set up global variables
MediaTracker theTracker =
null;
String blob_id;
Image theImage;
Graphics theGraphic;
// The buttons to push for
showing the graphic, cancel
Button show_button;
Button cancel_button;
Button execute_button;
// The text fields for status
and blob id input
Canvas blobCanvas = new
drawCanvas();
TextField theStatus = new
TextField(64);
TextField getblobid = new
TextField(4);
TextArea output = new
TextArea();
Font outputFont = new
Font("Courier", Font.PLAIN, 12);
// The sequence value for the
image file
static String inc_value;
// The global panels and frame
definitions
Frame imageFrame = new
Frame("Blob Retrieval Window");
Panel queryPanel = new Panel
();
Panel statusPanel = new Panel
();
Panel buttonPanel = new Panel
();
Panel blobPanel = new Panel();
// The Applets main section
init() to set up screen
public void init( )
{
// Create the User Interface
// Setup Query Panel
queryPanel.setLayout (new
BorderLayout ( ));
execute_button = new Button
("Get BLOBs");
execute_button.addActionListener(this);
queryPanel.add ("North",
execute_button);
output = new TextArea (22,
40);
output.setFont(outputFont);
queryPanel.add ("Center",
output);
// Setup buttonPanel
buttonPanel.setLayout (new
GridLayout (1,3));
show_button = new Button
("Show");
show_button.addActionListener(this);
buttonPanel.add("North",
show_button);
cancel_button = new Button
("Cancel");
cancel_button.addActionListener(this);
buttonPanel.add("North",
cancel_button);
buttonPanel.add("West", new
Label("Enter Blob ID:", Label.RIGHT));
buttonPanel.add("Center",
getblobid);
// Setup Blob Panel
blobPanel.setLayout (new
BorderLayout());
blobPanel.add("East",
blobCanvas);
blobPanel.add("North",
buttonPanel);
this.validate();
// Setup Status Panel
statusPanel.setLayout (new
BorderLayout ( ));
statusPanel.add("South",
theStatus);
// Set text editable status
theStatus.setEditable(false);
getblobid.setEditable(true);
//Setup Master Layout
imageFrame.setLayout (new
BorderLayout( ));
imageFrame.add("West",
queryPanel);
imageFrame.add("South",
statusPanel);
imageFrame.add("East",
blobPanel);
imageFrame.pack();
imageFrame.setVisible(true);
theTracker = new
MediaTracker(this);
}
// What to do when buttons
pushed
public void
actionPerformed(ActionEvent event)
{
// Get event
Object source =
event.getSource();
// Determine type of event and
take action
if (source == execute_button)
{
try
{
// Clear the output area
output.setText (null);
// open the connection to the
database
Class.forName ("oracle.jdbc.driver.OracleDriver");
// Connect to the database
Connection conn =
DriverManager.getConnection
("jdbc:oracle:oci8:TEST", "graphics_dba", "graphics1");
// Create a Statement
Statement stmt =
conn.createStatement ();
// Create a statement
// Execute the statement and
query
output.append("Blobs Available
to View" + "\n");
ResultSet rset =
stmt.executeQuery ("SELECT rpad(to_char(graphic_id),5)||':
'||rpad(graphic_desc,30)||' : '||rpad(graphic_type,10) FROM
internal_graphics order by graphic_id");
// Dump the result
while (rset.next ())
output.append(rset.getString (1) + "\n");
}
catch (Exception e)
{
handleError(e);
}
getblobid.requestFocus();
}
if (source == show_button)
{
blob_id = getblobid.getText();
theStatus.setText("Looking For
BLOB :" + blob_id);
// Load the Oracle JDBC driver
try
{
theStatus.setText("Logging
On");
Class.forName ("oracle.jdbc.driver.OracleDriver");
// Connect to the database
Connection conn =
DriverManager.getConnection
("jdbc:oracle:oci8:TEST", "graphics_dba", "graphics1");
theStatus.setText("Connection
Established");
// Create a Statement
Statement stmt =
conn.createStatement ();
// Get increment value for
temp file
ResultSet inc =
stmt.executeQuery ("select to_char(image_seq.nextval) from
dual");
if (inc.next ())
inc_value = inc.getString
(1);
// Select the lobs
theStatus.setText("Fetching
BLOB :" + blob_id);
ResultSet rset =
stmt.executeQuery ("select graphic_blob from internal_graphics
where graphic_id=" + blob_id);
//
if (rset.next ())
{
OracleBlob blob = ((OracleResultSet)rset).getBlob
(1);
theStatus.setText("Placing
Blob in Temp File");
dumpBlob(conn,blob);
theStatus.setText("Showing
Image");
getblobid.setText("");
theImage =
getImage(getCodeBase(),"images/theImage" + inc_value + ".gif");
theTracker = new
MediaTracker(this);
theTracker.addImage(theImage,
0);
try {
theTracker.waitForID(0);
}
catch (InterruptedException e)
{ }
blobPanel.setVisible(false);
repaint();
blobPanel.setVisible(true);
getblobid.requestFocus();
}
}
catch (ClassNotFoundException
c)
{
handleError©;
}
catch (SQLException sql)
{
handleError(sql);
}
catch (Exception s)
{
handleError(s);
}
}
if (source == cancel_button)
System.exit(0);
}
// Override of paint() with
canvas to do exactly what we want
class drawCanvas extends
Canvas
{
int iWidth = 480;
int iHeight = 500;
public void paint(Graphics
theGraphic)
{
Dimension minSize =
getPreferredSize();
if (theTracker != null)
{
if (theTracker.isErrorAny())
{
theGraphic.setColor(Color.red);
theGraphic.drawString("Image
Error",60,10);
return;
}
if (theTracker.checkAll(true))
{
iWidth =
theImage.getWidth(this);
iHeight =
theImage.getHeight(this);
if (iHeight>480)
{
iWidth=iWidth/2;
iHeight=iHeight/2;
}
if (iWidth>500)
{
iWidth=iWidth/2;
iHeight=iHeight/2;
}
int xpos = 60;
minSize = getPreferredSize();
theGraphic.drawImage(theImage,
xpos, 20, iWidth, iHeight, this);
}
else
{
theGraphic.setColor(Color.green);
theGraphic.drawString("Image
loading ....", 60,10);
repaint(100);
}
}
}
public Dimension
getPreferredSize()
{
/*System.out.println(iWidth +
" x " + iHeight);*/
return new Dimension (iWidth,iHeight);
}
}
// Since JAVA doesn't clean up
graphics context, do it in an over ride
// of destroy()
public void destroy()
{
theGraphic.dispose();
System.gc();
}
// Utility to handle
exceptions cleanly
public void
handleError(Throwable t)
{
theStatus.setText("Error: " +
t.getMessage( ));
t.printStackTrace( );
}
// Utility function to dump
Blob contents
static void dumpBlob
(Connection conn, OracleBlob blob)
throws Exception
{
OracleCallableStatement cstmt1
=
(OracleCallableStatement)
conn.prepareCall ("begin - :=
dbms_lob.getLength (?); end;");
OracleCallableStatement cstmt2
=
(OracleCallableStatement)
conn.prepareCall ("begin
dbms_lob.read (?, ?, ?, ?); end;");
cstmt1.registerOutParameter
(1, Types.NUMERIC);
cstmt1.setBlob (2, blob);
cstmt1.execute ();
long length = cstmt1.getLong
(1);
long i=0;
int chunk=100;
FileOutputStream file = new
FileOutputStream ("images/theImage" + inc_value+ ".gif");
while (i < length)
{
cstmt2.setBlob (1, blob);
cstmt2.setLong (2, chunk);
cstmt2.registerOutParameter
(2, Types.NUMERIC);
cstmt2.setLong (3, i+1);
cstmt2.registerOutParameter
(4, Types.VARBINARY);
cstmt2.execute ();
long read_this_time =
cstmt2.getLong (2);
byte [] bytes_this_time =
cstmt2.getBytes (4);
{
// Put into file
file.write (bytes_this_time);
i += read_this_time;
/* System.out.print
("Read " + i + " bytes of " + length + "\n");*/
if ((i + read_this_time)>length)
chunk = (int) (length-i);
}
}
file.close();
cstmt1.close ();
cstmt2.close ();
}
}
Listing 9: Full Code for Graphics Retrieval
Applet
To give you an idea what the different sections do,
look at Figure 5. Figure 5 shows the Blob Retrieval application as
it looks when it is first started. Notice we have five main panels,
these being the two top button panels, the central query and BLOB
display panels and the bottom status display panel. All of these
panels are attached to a main frame component.
Figure 6 shows the results of a mouse click of the
ìGet BLOBsî button. The click of the button institutes a query of
the internal_graphics table showing the index value, description and
type of each BLOB currently stored in the database. Notice how the
awt panel component automatically adds scroll bars to text regions
as needed. Also look up in the ìEnter BLOB ID:î text box, we have
entered a BLOB Id from the display on the right but have not clicked
on the ëShowî button, yet.
Figure 7 shows the results from clicking on the
ìShowî button. Clicking on the ìShowî button initiates the query to
get the BLOB locator from the database, use the BLOB locator to
retrieve the actual image data from the database using the DBMS_LOB
package and place the result in one of a rotating pool of temporary
files. Finally, the image is displayed on the graphic canvas, a part
of the right panel.
So, how is all of this accomplished? We first
initialize the classes we need iin the start of the applet, then in
the init() method we set global variables and components. A look at
the global variables shows we are initializing a new class called
MediaTracker, but more about this in a minute. We are also
initializing buttons and panels. Another new item I mentioned in the
description of the applets actions is the Canvas, a canvas is used
to draw on from within an applet or application. A canvas is usually
attached to another object, or container if you prefer, such as
panels, frames, windows or even buttons. Lets look at the globals:
// Set up global variables
MediaTracker theTracker =
null; ( Note new Class, MediaTracker
String blob_id;
Image theImage;
Graphics theGraphic;
// The buttons to push for
showing the graphic, cancel
Button show_button;
Button cancel_button;
Button execute_button;
// The text fields for status
and blob id input
Canvas blobCanvas = new
drawCanvas(); ( Note new object, a Canvas
TextField theStatus = new
TextField(64);
TextField getblobid = new
TextField(4);
TextArea output = new
TextArea();
Font outputFont = new
Font("Courier", Font.PLAIN, 12);
// The sequence value for the
image file
static String inc_value;
// The global panels and frame
definitions
Frame imageFrame = new
Frame("Blob Retrieval Window");
Panel queryPanel = new Panel
();
Panel statusPanel = new Panel
();
Panel buttonPanel = new Panel
();
Panel blobPanel = new Panel();
These initial definitions are
not all of the components we will be using, these are only the
ones which are used in multiple methods within
DisplayImage2.java. The init() method defines the rest of the
user interface components.
public void init( )
{
// Create the User Interface
// Setup Query Panel
queryPanel.setLayout (new
BorderLayout ( ));
execute_button = new Button
("Get BLOBs");
execute_button.addActionListener(this);
queryPanel.add ("North",
execute_button);
output = new TextArea (22,
40);
output.setFont(outputFont);
queryPanel.add ("Center",
output);
// Setup buttonPanel
buttonPanel.setLayout (new
GridLayout (1,3));
show_button = new Button
("Show");
show_button.addActionListener(this);
buttonPanel.add("North",
show_button);
cancel_button = new Button
("Cancel");
cancel_button.addActionListener(this);
buttonPanel.add("North",
cancel_button);
buttonPanel.add("West", new
Label("Enter Blob ID:", Label.RIGHT));
buttonPanel.add("Center",
getblobid);
// Setup Blob Panel
blobPanel.setLayout (new
BorderLayout());
blobPanel.add("East",
blobCanvas);
blobPanel.add("North",
buttonPanel);
this.validate();
// Setup Status Panel
statusPanel.setLayout (new
BorderLayout ( ));
statusPanel.add("South",
theStatus);
// Set text editable status
theStatus.setEditable(false);
getblobid.setEditable(true);
//Setup Master Layout
imageFrame.setLayout (new
BorderLayout( ));
imageFrame.add("West",
queryPanel);
imageFrame.add("South",
statusPanel);
imageFrame.add("East",
blobPanel);
imageFrame.pack();
imageFrame.setVisible(true);
theTracker = new
MediaTracker(this);
}
Notice the use of the media tracker with the
theTracker = new MediaTracker(this); line of code. This line
attaches the media listener to the imageFrame components blobPanel.
The blobPanel contains the blobCanvas drawing component. Another
method we have used before but not commented on is the pack()
method. The pack() method makes a component just big enough to hold
its components. The pack() method can cause problems with graphic
elements if the graphic element isnít initialized, this is
especially true of the Canvas class of objects, more on this later.
The next section of code determines what actions
are taken when the buttons are clicked using the mouse buttons.
public void
actionPerformed(ActionEvent event)
{
// Get event
Object source =
event.getSource();
// Determine type of event and
take action
if (source == execute_button)
{
try
{
// Clear the output area
output.setText (null);
// open the connection to the
database
Class.forName
("oracle.jdbc.driver.OracleDriver");
// Connect to the database
Connection conn =
DriverManager.getConnection
("jdbc:oracle:oci8:TEST", "graphics_dba", "graphics1");
// Create a Statement
Statement stmt =
conn.createStatement ();
// Create a statement
// Execute the statement and
query
output.append("Blobs Available
to View" + "\n");
ResultSet rset =
stmt.executeQuery ("SELECT rpad(to_char(graphic_id),5)||':
'||rpad(graphic_desc,30)||' : '||rpad(graphic_type,10) FROM
internal_graphics order by graphic_id");
// Dump the result
while (rset.next ())
output.append(rset.getString (1) + "\n");
}
catch (Exception e)
{
handleError(e);
}
getblobid.requestFocus();
}
if (source == show_button)
{
blob_id = getblobid.getText();
theStatus.setText("Looking For
BLOB :" + blob_id);
// Load the Oracle JDBC driver
try
{
theStatus.setText("Logging
On");
Class.forName
("oracle.jdbc.driver.OracleDriver");
// Connect to the database
Connection conn =
DriverManager.getConnection
("jdbc:oracle:oci8:TEST", "graphics_dba", "graphics1");
theStatus.setText("Connection
Established");
// Create a Statement
Statement stmt =
conn.createStatement ();
// Get increment value for
temp file
ResultSet inc =
stmt.executeQuery ("select to_char(image_seq.nextval) from
dual");
if (inc.next ())
inc_value = inc.getString
(1);
// Select the lobs
theStatus.setText("Fetching
BLOB :" + blob_id);
ResultSet rset =
stmt.executeQuery ("select graphic_blob from internal_graphics
where graphic_id=" + blob_id);
//
if (rset.next ())
{
OracleBlob blob =
((OracleResultSet)rset).getBlob (1);
theStatus.setText("Placing
Blob in Temp File");
dumpBlob(conn,blob);
theStatus.setText("Showing
Image");
getblobid.setText("");
theImage =
getImage(getCodeBase(),"images/theImage" + inc_value + ".gif");
theTracker = new
MediaTracker(this);
theTracker.addImage(theImage,
0);
try {
theTracker.waitForID(0);
}
catch (InterruptedException e)
{ }
blobPanel.setVisible(false);
repaint();
blobPanel.setVisible(true);
getblobid.requestFocus();
}
}
catch (ClassNotFoundException
c)
{
handleError©;
}
catch (SQLException sql)
{
handleError(sql);
}
catch (Exception s)
{
handleError(s);
}
}
if (source == cancel_button)
System.exit(0);
}
Rather than use a more modular approach (which by
the way I tried but it ended up more complex than I liked) I decided
to integrate the operations into the actionPerformed(ActionEvent
event) module.
In this case if the execute_button (Get BLOBs) is
clicked, the code to retrieve the contents of the internal_graphics
table is activated. Notice that at the end of the process of
retrieving the BLOB data I place the focus of input into the
getblobid text area so the user doesnít have to manually select the
area using a mouse action.
The next action item is the show_button (Show). If
the show_button is clicked, the value from the getblobid region is
scraped from the screen using a getText() method call. This
getblobid value will be used in a query to get the BLOB of choice.
First, we reconnect (if needed) to the database and get the next
sequence value from a database maintained sequence called image_seq.
This sequence is set to forever cycle between 1 and 32 and allows us
to randomly associate our graphic with one of the 32 temporary image
files.
I found that if I attempted to use a single
temporary image file the graphic context would not remap (would not
display). I assume that since JAVA saw I was using the same file
name it meant I wanted the same graphic. This behavior even
persisted if I used a dispose() method and a System.gc() call (the
dispose is supposed to destroy the graphic context and the System
call gc() initiates a garbage cleanup). Use of multiple temp files
corrected this rather annoying feature of JAVA. Once we have the
temporary file we can proceed to SELECT the BLOB id from the
internal_graphics table and pass this information to the dumpBlob()
method. Also notice that you must create a new instance of tracker
at the start of the run or any error returned is carried into the
next cycle of operation adfinitum.
Once control returns from the dumpBlob() method the
image is retrieved from the temporary file and the MediaTracker
object is attached to the process retrieving the image from the
file. The MediaTracker object follows the progress of the image load
into memory and if there is a problem, reports it back to the
process. The MediaTracker is useful in situations where images are
being transferred from one net location to another to prevent draw
and paint routines from attempting to draw incomplete graphics. Once
a graphics context is assigned, the control is returned immediately,
without the call to the MediaTracker the image might not be complete
before operations using it begin.
The next set of commands kept me up several nights
figuring out. When I first wrote the application I could get the
graphics to display only by collapsing and reinitializing the window
using the frame interface with mouse clicks, hardly what I was
wanting.
blobPanel.setVisible(false);
repaint();
blobPanel.setVisible(true);
getblobid.requestFocus();
The act of making the blobPanel invisible, then
doing the call to repaint() and making the panel visible fixed the
problem. As a final step in the process I return focus to the
getblobid text area, again for user convenience. Once a graphic
context is initialized, it will remain in memory cluttering things
up until a dispose() is issued against it. Using appletviewer in a
MS-DOS window on the NT4.0 I was only able to display about 2-3
images before the process locked and required a destructive drop
using the task manager. With addition of the dispose method call in
the overridden destroy() method call this increased to 12-13 and
with a final call to garbage collection (System.gc()) I was finally
able to alternatively retrieve and display an unlimited number of
images.
public void destroy()
{
theGraphic.dispose();
System.gc();
}
In this applet the paint() routine is encapsulated
in an overridden Canvas class definition. The paint() routine has
been expanded to include call to the getPreferredSize() overridden
method to get an initialization set of dimensions set by globally
declaring the iWidth and iHeight integer values and using these as
a return value set whenever the getPreferredSize() routine is
called. By tying the values returned to iWidth and iHeight whenwe
reset these to the actual and adjusted width and height of the
graphic any calls to getPreferredSize() returns the proper value.
Notice we use a new class, the Dimension class, to set up our iWidth
and iHeight values into the minSize() calls. Notice how we use the
value of theTracker (the MediaTracker object) to verify if the image
is properly loaded.
class drawCanvas extends
Canvas
{
int iWidth = 480;
int iHeight = 500;
public void paint(Graphics
theGraphic)
{
Dimension minSize =
getPreferredSize();
if (theTracker != null)
{
if (theTracker.isErrorAny())
{
theGraphic.setColor(Color.red);
theGraphic.drawString("Image
Error",60,10);
return;
}
if (theTracker.checkAll(true))
{
iWidth =
theImage.getWidth(this);
iHeight =
theImage.getHeight(this);
if (iHeight>480)
{
iWidth=iWidth/2;
iHeight=iHeight/2;
}
if (iWidth>500)
{
iWidth=iWidth/2;
iHeight=iHeight/2;
}
int xpos = 60;
minSize = getPreferredSize();
theGraphic.drawImage(theImage,
xpos, 20, iWidth, iHeight, this);
}
else
{
theGraphic.setColor(Color.green);
theGraphic.drawString("Image
loading ....", 60,10);
repaint(100);
}
}
}
public Dimension
getPreferredSize()
{
/*System.out.println(iWidth +
" x " + iHeight);*/
return new Dimension
(iWidth,iHeight);
}
The last section we will
discuss is the getBlob() method. The getBlob() method takes the
blob identifier and a connection value and uses the Oracle8
stored procedure from the DBMS_LOB package to actually read the
BLOB and place the image data into a holding file:
// Utility function to dump
Blob contents
static void dumpBlob
(Connection conn, OracleBlob blob)
throws Exception
{
OracleCallableStatement cstmt1
=
(OracleCallableStatement)
conn.prepareCall ("begin - :=
dbms_lob.getLength (?); end;");
OracleCallableStatement cstmt2
=
(OracleCallableStatement)
conn.prepareCall ("begin
dbms_lob.read (?, ?, ?, ?); end;");
cstmt1.registerOutParameter
(1, Types.NUMERIC);
cstmt1.setBlob (2, blob);
cstmt1.execute ();
long length = cstmt1.getLong
(1);
long i=0;
int chunk=100;
FileOutputStream file = new
FileOutputStream ("images/theImage" + inc_value+ ".gif");
while (i < length)
{
cstmt2.setBlob (1, blob);
cstmt2.setLong (2, chunk);
cstmt2.registerOutParameter
(2, Types.NUMERIC);
cstmt2.setLong (3, i+1);
cstmt2.registerOutParameter
(4, Types.VARBINARY);
cstmt2.execute ();
long read_this_time =
cstmt2.getLong (2);
byte [] bytes_this_time =
cstmt2.getBytes (4);
{
// Put into file
file.write (bytes_this_time);
i += read_this_time;
/* System.out.print
("Read " + i + " bytes of " + length + "\n");*/
if ((i + read_this_time)>length)
chunk = (int) (length-i);
}
}
file.close();
cstmt1.close ();
cstmt2.close ();
}
}
The dumpBlob() method uses the JDBC Oracle
implementation to prepare and execute the calls to the database. The
following code snippet shows the Oracle specific calls:
OracleCallableStatement cstmt1 =
(OracleCallableStatement)
conn.prepareCall ("begin - :=
dbms_lob.getLength (?); end;");
OracleCallableStatement cstmt2 =
(OracleCallableStatement)
conn.prepareCall ("begin dbms_lob.read (?,
?, ?, ?); end;");
The question marks in the calls to prepareCall()
method mark locations where variables are written. These variables
are written using code similar to the following code lines:
cstmt1.registerOutParameter (1, Types.NUMERIC);
cstmt1.setBlob (2, blob);
cstmt1.execute ();
The first call to the registerOutParameter for
cstmt1 registers the first question mark in the line: ("begin - :=
dbms_lob.getLength (?); end;"); as a value that is returned (dbms_lob.getlength()
is a function and must return a value) and sets its type as NUMERIC.
The call to setBlob() registers the second (as shown by the 2)
question mark as an input to the function in cstmt1, setting it to
the actual BLOB identifier. The last statement in the snippet
executes the statement cstmt1 after making the substitutions. This
call to dbms_lob.getlength() returns the length of the BLOB. This
length must be known before we can retrieve the BLOB using
dbms_lob.read() as over runs of the BLOB bounderies are not
permitted and will result in an error if they happen.
The next section in dumpBlob() uses repeated calls
to the dbms_lob.read() Oracle8 procedure to read the BLOB values
piecewise. Once the pieces are placed in memory using the getBytes()
method, calls to the write() method of the JAVA File class place the
bytes into the designated file. To create a file rather than use an
existing temporary file, more advanced streaming methods as
demonstrated in "Graphic JAVA 1.1 - Mastering the AWT, 2nd Edition",
David M. Geary, The SunSoft Press, 1997, should be used.
long length =
cstmt1.getLong (1);
long i=0;
int chunk=100;
FileOutputStream file =
new FileOutputStream ("images/theImage" + inc_value+ ".gif");
while (i < length)
{
cstmt2.setBlob (1,
blob);
cstmt2.setLong (2,
chunk);
cstmt2.registerOutParameter (2, Types.NUMERIC);
cstmt2.setLong (3, i+1);
cstmt2.registerOutParameter (4, Types.VARBINARY);
cstmt2.execute ();
long read_this_time =
cstmt2.getLong (2);
byte [] bytes_this_time
= cstmt2.getBytes (4);
{
// Put into file
file.write (bytes_this_time);
i += read_this_time;
/* System.out.print
("Read " + i + " bytes of " + length + "\n");*/
if ((i + read_this_time)>length)
chunk = (int) (length-i);
}
}
One technique to take note of in this code snippet
is the calculation:
if ((i + read_this_time)>length) chunk = (int)
(length-i);
This calculation prevents the loop from reading
past the end of the BLOB and generating an exception which would
cause the transaction to be backed out resulting in only a partial
(if any) image transferred into our file.
Once the image file is created and the BLOB has
been completely transferred, the file and the Oracle statements are
closed:
file.close();
cstmt1.close ();
cstmt2.close ();
}
|