Jason Bennett's Developer Corner

 






Click to see the XML version of this web page.

>


View David Jason Bennett's profile on LinkedIn

 

 

A Little About Jason Bennett ...

I've had an interest in publishing technical articles and HELPFUL code for a few years.  I am (by trade and hobby) a developer who specializes in Oracle technologies and web based architectures.  I have been an employee of both TUSC and Oracle Corporation.  My intent here is to share my ideas and coding experiences with the developer community as a whole.  As with all developers some of my ideas are great and some of them are ....  well you know.  Anyway, I hope you find something here that will aid in your endeavor, or spark a new idea. 

I am more than happy to assist with technical issues and will even write a little code if need be. If you find something on the site that is really useful and you'd like to make a contribution (absolutely up to you and absolutely not required), just click the "Make a Donation" button on the left!

Good luck and good coding !




  Saturday, February 05, 2005


Uploading Binary Files (BLOBS) to An Oracle Table Using a Servlet

 

The purpose of this entry is to provide the reader with the actual code required to upload a binary file (image, excel spreadsheet, Word Doc, PDF, etc) from a web browser and store it in the BLOB column of database table.  After searching high and low for a specific example (I did find one that showed how to upload the file and save it to disk, which helped quite a bit), I vowed to publish my code for anyone else searching for a similar example. Some of this code comes from an actual application, and may not seem to 'flow', as I have omitted non-relevant portions.  The goal here is to impart the method for the upload and storage of BLOB data from the browser to the database.

 

Multipart Form-Data ... A brief explanation

It is important at the start to realize that binary data is uploaded as multipart/form-data  data from a form on a web page.  This means that the parameter values passed up from the form cannot be retrieved from the receiving servlet using the traditional request.getParameter method.  The form tag will look something like:

 

<FORM action="/uploadservlet/imageUpload.do"

               enctype="multipart/form-data"

               method="POST" >

 

The file upload component of the form uses an input tag of the format:

 

<INPUT type="file" name="my_image">

 

The rest of the form input parameter are just like any other:

 

<INPUT type="text" name="p_user">

 

All of the data is sent up in a specific format.  The passed parameters are strings of the form:

 

Content-Disposition: form-data; name="p_user"

Joe smith

--------------AaB03x

 

In the example above, p_user is a form parameter name and Joe Smith is the value.  NOTICE that there is a blank line between the parameter string and the actual value. Each of the parameter string value pairs is separated by a delimiter or boundary. The boundary line separating each individual data element looks something like:

 

--------------AaB03x

 

The value of this boundary varies. The first line in the stream will contain this value. It is important to grab and store this value, as the code will later demonstrate.

 

The binary content, or file, will be the last parameter string value pair in the input stream (basically the rest of the input stream).  The parameter string for the file looks like this:

 

content-disposition: form-data; name="my_image"; filename="test.jpeg"

Content-Type: image/pjpeg

 

... FILE CONTENT ...

 

 

The next string or line after the file parameter string is the content indicator string.  The actual content of the file follows content type string. NOTICE that the file data is separated from the content string by a blank line. For more info see

(http://www.faqs.org/rfcs/rfc1867.html).

 

The Code .....

 

The code for uploading the image into the data consists of a PL/SQL function on the data base side that takes the BLOB passed from the servlet and stores it in a new table, and the Java Servlet and related classes that handle the upload request.  First, lets look at the PL/SQL.  The following code comes from an application used to upload juvenile mugshots into a database in a juvenile arrest system:

 

   FUNCTION pre_load_image_table(p_juvmast_id JUVENILE_MASTERS.ID%TYPE       := NULL,

                                 p_label      JUVENILE_PHOTOS.label%TYPE     := NULL,

                                 p_user         JUVENILE_PHOTOS.created_by%TYPE   := NULL,

                                 p_mime_type  JUVENILE_PHOTOS.MIME_TYPE%TYPE := NULL) RETURN NUMBER

   IS

 

     v_return NUMBER := NULL;

 

   BEGIN

 

 

     EXECUTE IMMEDIATE 'SELECT jpo_seq.nextval FROM dual' INTO v_return;

     

     EXECUTE IMMEDIATE 'INSERT INTO juvenile_photos(id,juvmast_id,label,lineup_order,mime_type,juv_image,created_by,date_created) '||

                       ' values (:id,:p_juvmast_id,:p_label,:p_lineup,:p_mime_type,empty_blob(),:created_by,:date_created)'

     USING

           v_return,

           p_juvmast_id,

           p_label,

           getNextLineupOrder(p_juvmast_id),

           p_mime_type,

           p_user,

           SYSDATE;

 

     RETURN v_return;

 

   END;

 

   FUNCTION externalSaveImage(p_juvmast_id JUVENILE_MASTERS.ID%TYPE := NULL,

                              p_label      JUVENILE_PHOTOS.label%TYPE := NULL,

                              p_user         JUVENILE_PHOTOS.created_by%TYPE   := NULL,

                              p_mimetype   JUVENILE_PHOTOS.MIME_TYPE%TYPE := NULL) RETURN t_refcursor

   IS

 

     v_refcur kbcJuvenileImages.t_refcursor;

    

     v_pk     NUMBER(12) := 0;

 

   BEGIN

 

--Creates a new row in the image table with an empty_blob() and returns new PK.

      v_pk := pre_load_image_table(p_juvmast_id,UPPER(p_label),p_user,p_mimetype);

 

      OPEN v_refcur FOR 'SELECT juv_image FROM juvenile_photos WHERE id = :id FOR UPDATE' USING v_pk;

 

      RETURN v_refcur;

 

   END;

 

 

The first function is called by the second to create a new row in the image table containing the data passed from the webpage describing the image.  Please note that an EMPTY BLOB is inserted into the blob column of this table.  The second function selects the BLOB column of the new record for update and passes this value back to the calling Java application as a refcursor.  This creates an input stream that allows the blob data to be streamed back to the database. 

 

The image upload Java class is our next piece of code.  The class extends another class that I have not included.  The base class is merely a JDBC helper class containing code to obtain a connection object from a connection factory.  The important code here is the code that illustrates how to stream the binary data back to the database:

 

 

package imageupload;

 

import java.io.*;

import java.sql.*;

import javax.sql.*;

import oracle.sql.*;

import oracle.jdbc.*;

import javax.servlet.ServletInputStream;

 

/**

 * The purpose of this class is to take the remaining portion

 * of the input stream containing the body of the uploaded file

 * and pass it to a database stored proc that saves it to a

 * BLOB column in a table.

 * This class extends a JDBC helper class called

 * JDBC connection base.

 */

public class ImageLoader extends JDBCConnectionBase

{

 

  public void insertImageData(ServletInputStream p_image_stream,

                              int    p_id,

                              String p_label,

                              String p_user,

                              String p_mime_type,

                              String p_delimiter){

                                  

       try{

      

           BLOB    v_image_blob = null;

           String  lineCheck;

           setPooledConnection();

  

           //Using the stored procedure removes some of the complexity from

           //the Java application.

           CallableStatement cs =

                conn.prepareCall("begin ? := kbcJuvenileImages.externalSaveImage(?,?,?,?); end;");

       

           cs.registerOutParameter(1,OracleTypes.CURSOR);

           cs.setInt(2,p_id);

           cs.setString(3,p_label);

           cs.setString(4,p_user);

           cs.setString(5,p_mime_type);

           cs.execute();

       

           rset = (ResultSet)cs.getObject(1);

          

           if (rset.next()){

            v_image_blob = ((OracleResultSet)rset).getBLOB(1);

           }

          

           OutputStream outstream = v_image_blob.getBinaryOutputStream();

        

           byte[] buffer = new byte[256];

           int length = -1; 

          

           while ((length = p_image_stream.readLine(buffer,0,256)) != -1)

           {

             lineCheck = new String(buffer,0,length);

            

             if (lineCheck.indexOf(p_delimiter)== -1) {

               outstream.write(buffer,0,length);

             }

           }

           outstream.close();

           cs.close();

          

           conn.commit();

 

      

      

     }catch(SQLException e)

     {

       System.err.println("Error creating image record: "+e.getMessage());

       v_error.append("Error creating image record: ").append(e.getMessage());

     }catch(Exception e)

     {

       System.err.println("Error creating image record: "+e.getMessage());

       v_error.append("Error creating image record.").append(e.getMessage());

       e.printStackTrace();        

     }finally

     {

       closeConnections();    

     }

  }

}

 

The next bit of Java code is the actual servlet that handles the request for upload.  I think I have sufficiently commented the code, but if you have questions just shoot me an email.

 

package imageupload;

 

import oracle.sql.BLOB;

import java.io.ByteArrayInputStream;

import java.util.HashMap;

import javax.servlet.*;

import javax.servlet.http.*;

import javax.servlet.ServletInputStream;

import java.io.PrintWriter;

import java.io.IOException;

 

public class uploadServlet extends HttpServlet

{

 

 //The following six variables represent tokens

 //used to parse the content submitted in the

 //multi-part form.

  private static final String CONTENT_TYPE         

                                                   = "text/html; charset=windows-1252";

 

  private static final String DATA_INDICATOR       

                                                 = "Content-Disposition: form-data; name=\"";

 

  private static final String FIELD_INDICATOR       = "name=\"";

  private static final String FILE_INDICATOR        = "filename=\"";

  private static final String FILE_MIME_INDICATOR   = "Content-Type:";

  private String              REQUEST_DELIMITER     = null;

 

  //HashMap to store field names and values

  //passed from the calling form.

  private HashMap fields = new HashMap();

 

  /**

   * Check to see if the line passed in

   * contains a field type indicator.

  */

  private boolean checkField(String p_value)

  { 

     boolean v_return = false;

 

     if(p_value.indexOf(FIELD_INDICATOR) != -1)

     {

       v_return = true;

     }

    return v_return;

  }

 

  /**

   * Check to see if the line passed in

   * contains a file type indicator.

  */

  private boolean checkFile(String p_value)

  { 

     boolean v_return = false;

 

     if(p_value.indexOf(FILE_INDICATOR) != -1)

     {

       v_return = true;

     }

    return v_return;   

  }

 

  /**

   * Check to see if the line passed in

   * is a delimiter line. Delimiters

   * separate the different sections

   * of multipart form.

  */

  private boolean checkDelimiter(String p_value)

  { 

     boolean v_return = false;

 

     if(p_value.indexOf(REQUEST_DELIMITER) != -1)

     {

       v_return = true;

     }

    return v_return;   

  }

 

  /**

   * Check to see if the line passed in

   * contains the file content type indicator.

  */

  private boolean checkContentIdicator(String p_value)

  { 

     boolean v_return = false;

 

     if(p_value.indexOf(FILE_MIME_INDICATOR) != -1)

     {

       v_return = true;

     }

    return v_return;   

  }

 

  /**

   * Check to see if the line passed in

   * contains a data value type indicator.

  */

  private boolean checkDataIndicator(String p_value)

  { 

     boolean v_return = false;

 

     if(p_value.indexOf(DATA_INDICATOR) != -1)

     {

       v_return = true;

     }

     return v_return;  

  }

 

 

  /**

   * Returns the value of field stored in the

   * 'fields' HashMap based upon the key passed

   * in to the method.  The key relates directly

   * to the parameter names in the passing form.

  */

  private String getFieldValue(String p_name)

  {

     String v_value = new String();

 

     try{

       v_value =(String)fields.get(p_name);

     }catch(Exception e)

     {

       v_value = "";

     }

    return v_value.trim();

  }

 

 

  /**

   * Parse the value of the parameter passed in

   * on line containing parameter/field data.

  */ 

  private String parseFieldValue(ServletInputStream in) throws Exception

  {

     byte[] line             = new byte[128];

     int i                   = 0;

     StringBuffer fieldValue = new StringBuffer(128);

     String       newline;

    

    newline = new String();

   

    while (i != -1 && !checkDelimiter(newline)) {

       i = in.readLine(line, 0, 128);

       newline = new String(line, 0, i);

       if (!checkDelimiter(newline)){

          fieldValue.append(newline);

       }

    }

    return fieldValue.toString();

  }

 

  /**

   * Parse the value of the parameter name in

   * on line containing parameter/field data.

  */ 

  private String parseFieldName(String p_name)

  {

      int     pos = 0;

      String  v_content = new String();

 

      pos = p_name.indexOf(FIELD_INDICATOR);

     

      if (pos > 0){

        v_content = p_name.substring(pos+6, p_name.length()-3);

       

       }

      

    return v_content;

  }

 

    /**

   * Parse the value of the content type passed in

   * on line containing file content data.

  */ 

  private String getContentType(String p_name)

  {

      int     pos = 0;

      String  v_content = new String();

 

      pos = p_name.indexOf(":");

     

      if (pos != -1){

        v_content = p_name.substring(pos+1,p_name.length());

       

       }

      

    return v_content;

  }

 

  /**

   * Place field name and value in the "fields" HashMap

   * for easy retrieval later.  The field (or parameter)

   * name becomes the hashmap key.

  */ 

 

  private void setFieldValue(String p_name,

                             String p_value)

 {

 

      fields.put(p_name,p_value);                     

 }

 

  /**

   * This method takes the input stream containing the

   * multipart form data and processes it.  Processing

   * means separating out the different items such as

   * parameter values and passes off the binary data

   * to a handler object that loads the data into a

   * database field.  The method returns a success or

   * failure value in the form of a String.

  */ 

 

 private String processInputStream(ServletInputStream    in) throws Exception

 {

   String      v_return_value = new String();

   String      v_content      = new String();

   String      newline        = new String();

  

   ImageLoader imageLoader    = new ImageLoader();

   try{

              

       byte[] line = new byte[256];

       int i = in.readLine(line, 0, 256);

 

       //First line is delimiter/boundary

       REQUEST_DELIMITER = new String(line, 0, i);

      

          while (i != -1) {

         

              newline = new String(line, 0, i);

              //System.out.println(newline);

              if (checkDataIndicator(newline)) {

                if (checkFile(newline)) {

               

                  i = in.readLine(line, 0, 256);

                  newline = new String(line, 0, i);

                  //System.out.println(newline);

                 

                  while (!checkContentIdicator(newline))

                  { 

                     i = in.readLine(line, 0, 256);

                     newline = new String(line, 0, i);

                  }

                 

                  if (checkContentIdicator(newline)){

                     v_content = getContentType(newline);

                  }

                 

                  //Consume extra line

                  i = in.readLine(line, 0, 256);

                  //newline = new String(line, 0, i);

                  //System.out.println(newline);

                  if (getFieldValue(ApplicationConstants.PARAM_NAME).length() > 0){

                    imageLoader.insertImageData(in,

                                                Integer.parseInt(getFieldValue("p_id")),

                                                getFieldValue("p_label"),

                                                getFieldValue("p_user"),

                                                v_content,

                                               REQUEST_DELIMITER);

                  }else

                 {

                    v_return_value="Photo Upload failed: ID is required.";

                    break;

                 }

 

               }else{

 

               String fieldName =parseFieldName(newline);

              //System.out.println("fieldName:" + fieldName);

              // blank line

              i = in.readLine(line, 0, 128);        

              setFieldValue(fieldName,parseFieldValue(in));

           }

         }

        

          //read the next line.

          i = in.readLine(line, 0, 128);

 

        }

         

        if (imageLoader.existErrors())

        {

           v_return_value = "Photo Upload failed: "+imageLoader.getErrors();

        }

    }catch(Exception e)

     {

         v_return_value = "Photo Upload failed: "+e.getMessage();     

     }finally

    {

         in.close();

    } 

   

    if (v_return_value.length() < 1)

    {

      v_return_value = "Photo Uploaded Successfully.";

    }

   

    return v_return_value;

 }

 

 private void displayResponse(PrintWriter out,

                              String      p_text)

 {

 

   int v_error = p_text.indexOf("failed");

  

   out.println("<HTML>");

   out.println("<BODY BGCOLOR=\"C0C0C0">");

   out.println("<STYLE>");

   out.println(".errorText  {color:       red;");

   out.println("             font-style:  italic;");

   out.println("             font-weight: bold;}");

   out.println(" .greenText  {color:#006600;}");

   out.println("</STYLE>");

   out.println("<big>Upload Juvenile Master Photo</big><br>");

  

   if (v_error >= 0)

   {

     out.println("<div class=\"errorText">");

   }else

   {

     out.println("<div class=\"greenText">");

   }

  

   out.println(p_text);

   out.println("</div>");

  

   out.println("</BODY>");

   out.println("</HTML>");

 }

 

  public void init(ServletConfig config) throws ServletException

  {

    super.init(config);

  }

 

  public void service(HttpServletRequest request, HttpServletResponse response)

  throws ServletException, IOException

  {

 

    String              v_upload_result = new String();

    PrintWriter         out             = response.getWriter();

    ServletInputStream  in              = null;

   

    response.setContentType(CONTENT_TYPE);

 

   

   try{

    

          in = request.getInputStream();

          v_upload_result = processInputStream(in);

 

    }catch(Exception e)

     {

         v_upload_result="Image Upload failed: "+e.getMessage();     

     }finally

    {

         in.close();

    }

 

    displayResponse(out,v_upload_result);

   

    out.close();

  }

}

 


4:08:45 PM    

Click here to visit the Radio UserLand website. © Copyright 2008Jason Bennett.
Last update: 8/28/2008; 9:47:56 PM.

February 2005
Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28          
Nov   Mar