Oracle的Blob字段比较特殊,他比long字段的性能要好很多,可以用来保存例如图片之类的二进制数据。 

写入Blob字段和写入其它类型字段的方式非常不同,因为Blob自身有一个cursor,你必须使用cursor对 

blob进行操作,因而你在写入Blob之前,必须获得cursor才能进行写入,那么如何获得Blob的cursor呢? 

这需要你先插入一个empty的blob,这将创建一个blob的cursor,然后你再把这个empty的blob的cursor 

用select查询出来,这样通过两步操作,你就获得了blob的cursor,可以真正地写入blob数据了。 

【处理流程】

[sql] view plaincopy
--Oracle中的Lob类型示例表  
  
create table user_info   
(  
  user_id number(10) primary key,  
  name varchar2(20),  
  image blob  
);  
  
--1. 插入空blob: (如果在数据库中采用默认值方式对Blob字段赋值, 此步可省略)  
  
   insert into user_info values (1, 'Jacky', empty_blob());  
  
--2. 获得blob的cursor:  
    
   select image from user_info where user_id = ? for update;  
  
--3. 用cursor往数据库写数据:  
  
   update user_info set image = ? where user_id = ?;  

 

//读取Blob数据   
package demo;   
  
import java.sql.*;   
import java.io.*;   
  
public class ReadBlob   
{   
    //加载驱动程序   
    static    
    {   
           
[java] view plaincopy
//读取Blob数据  
package demo;  
  
import java.sql.*;  
import java.io.*;  
  
public class ReadBlob  
{  
    //加载驱动程序  
    static   
    {  
          
        try  
        {  
            Class.forName("oracle.jdbc.driver.OracleDriver");  
        } catch (ClassNotFoundException e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
      
    public static void main(String[] args)  
    {  
        try  
        {  
            //1. 建立连接  
            String url = "jdbc:oracle:thin:@localhost:1521:OracleDB";  
            Connection conn = DriverManager.getConnection(url,"scott","tiger");  
            conn.setAutoCommit(false);  
              
            //2. 查询数据  
            String sql = "select image from user_info where user_id = 1";  
            Statement stmt = conn.createStatement();  
            ResultSet rs = stmt.executeQuery(sql);  
              
            //3. 读取Blob类型数据  
            Blob blob = null;  
            if(rs.next())  
            {  
                blob = rs.getBlob(1);  
            }  
            byte[] temp = new byte[(int)blob.length()];  
            InputStream in = blob.getBinaryStream();  
            in.read(temp)s  
[java] view plaincopy
            <strong>//保证文件名唯一,你可以用主键+时间啊等等方法</strong>                 
            File file = new File("D://img.bmp");  
            FileOutputStream fout = new FileOutputStream(file);  
            fout.write(temp);  
            in.close();  
            fout.close();  
        } catch (Exception e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
}  



 

//写Blob数据   
package demo;   
  
import java.sql.*;   
import oracle.sql.BLOB;//▲此处的BLOB类全大写, 而java.sql.Blob中的Blob非全大写   
import java.io.*;   
  
public class WriteBlob   
{   
    //加载驱动程序   
    static    
    {   
        try  
        {   
            Class.forName("oracle.jdbc.driver.OracleDriver");   
        }   
        catch(Exception e)   
        {   
            e.printStackTrace();   
        }   
    }   
    public static void main(String[] args)   
    {   
        try  
        {   
            //1. 建立与数据库服务器的连接   
            String url = "jdbc:oracle:thin:@localhost:1521:OracleDB";   
            Connection conn = DriverManager.getConnection(url,"scott","tiger");   
            conn.setAutoCommit(false);   
               
            //2. 首先向表中插入空的Blob   
            //★注意: 对于empty_blob()应放在SQL语句中直接赋值, 使用预置语句的方式赋值无法实现.   
            String sql = "insert into user_info values(?,?,empty_blob())";   
            PreparedStatement ps = conn.prepareStatement(sql);   
            ps.setInt(1, 1);   
            ps.setString(2, "Lucy");   
            ps.executeUpdate();   
               
            //3. 查询Blob, 获得Blob的Cursor   
            sql = "select image from user_info where user_id = ?";   
            ps = conn.prepareStatement(sql);   
            ps.setInt(1, 1);   
            ResultSet rs = ps.executeQuery();   
            BLOB blob = null;   
            if(rs.next())   
            {   
                blob = (BLOB)rs.getBlob(1);   
            }   
               
            //4. 使用字节流将待入库的文件写入到blob中   
            File file = new File("D://iriver//sample1.bmp");   
            FileInputStream fin = new FileInputStream(file);   
            byte[] temp = new byte[fin.available()];   
            fin.read(temp);   
            OutputStream out = blob.getBinaryOutputStream();   
            out.write(temp);   
            fin.close();   
            out.close();   
               
            //5. 向数据库中写入数据   
            sql = "update user_info set image = ? where user_id = ?";   
            ps = conn.prepareStatement(sql);   
            ps.setBlob(1, blob);   
            ps.setInt(2, 1);   
            ps.executeUpdate();   
               
            conn.commit();   
        } catch (Exception e)   
        {   
            e.printStackTrace();   
        }   
    }   
}  

Java存取OracleBlob字段,图片存储,Blob和BLOB的问题,Clob,oracle

1、Blob和BLOB的问题

java.sql.Blob 
oracle.sql.BLOB 
这两个blob仅仅是大小写不同,但是差异很大,java.sql.Blob是一个接口,而oracle.sql.BLOB是一个实现java.sql.Blob的类,并且还有很多扩展的属性和方法,注意不要搞混了。 

2、JDBC2.0和JDBC3.0的问题 

classes12.zip实现了JDBC2.0(JDK1.3),而JDBC2.0对于Blob的操作只有读,没有写,所以classes12.zip只好自己扩展了一套对Blob进行写的API,我的例子就是用了这套API。 
ojdbc14.jar实现了JDBC3.0(JDK1.4),JDBC3.0已经包括了对Blob写的操作,而ojdbc14.jar也实现了该API。 

总之注意这些问题。 

你用的是什么容器?是不是weblogic?如果是weblogic,并且通过datasourse获取connection,取出来的就不是oracle.sql.BLOB,而是weblogic封装过的OracleThinBlob,所以cast的时候肯定会出错,但是如果在程序里面cast成OracleThinBlob,就会造成对weblogic的依赖,所以我在dao的impl中用反射解耦。并且这个只是在使用oracle的thin驱动的时候才会出现的问题

写CLOB的代码片段:
Java代码  
File blobFile = new File("image.jpg");  
int file_length = (int)blobFile.length();  
FileInputStream blobInStream = new FileInputStream(blobFile);  
PreparedStatement insert_prepared = null;  
insert_prepared = m_conn.prepareStatement(query);  
insert_prepared.setString(1, id);  
insert_prepared.setBinaryStream(2, blobInStream, file_length);  
insert_prepared.setString(3, tel);  
insert_prepared.executeUpdate();  
m_conn.commit();  
 
String SqlStr = "update TextInfo set Content=empty_clob() where TextInfoKey=" + Integer.toString(textinfokey);   
myConn.setAutoCommit(false);   
java.sql.PreparedStatement pstmt = myConn.prepareStatement(SqlStr);   
pstmt.executeUpdate();   
  
SqlStr = "select Content from TextInfo where TextInfoKey=" + Integer.toString(textinfokey) + " for update";   
java.sql.Statement st = myConn.createStatement();   
java.sql.ResultSet rs = st.executeQuery(SqlStr);   
java.sql.Clob clob ;   
if (rs.next()) {   
clob = rs.getClob("Content");   
weblogic.jdbc.rmi.SerialOracleClob cast1 =(weblogic.jdbc.rmi.SerialOracleClob)clob;   
weblogic.jdbc.rmi.internal.OracleTClobImpl cast2 =(weblogic.jdbc.rmi.internal.OracleTClobImpl)cast1.getTheRealClob();   
CLOB myClob = (oracle.sql.CLOB)cast2.getTheRealClob();   
java.io.Writer out=myClob.getCharacterOutputStream();   
out.write(content);   
out.flush();   
out.close();   
  
}   
 -------------------------------------------------------------------------------------------------

存取BLOB出现这么多问题,我认为大半是由数据库开发商、应用服务器商在JDBC驱动上的不兼容性带来的。而实际应用中,每个人的开发运行环境不同,使得某个网友的solution没有办法在别人的应用中重现,以至于骂声一片。至于为什么会不兼容、有哪些问题,我没有时间去弄清,这里只说说我们怎样解决了问题的。

基于上述原因,先列出我们的开发环境,免得有人配不出来,招人唾骂。

数据库 Oracle 9i
应用服务器 BEA Weblogic 8.11
开发工具 JBuilder X

在JSP实现文件Upload/Download可以分成这样几块:文件提交到形成InputSteam;InputSteam以BLOB格式入库;数据从库中读出为InputSteam;InputStream输出到页面形成下载文件。先说BLOB吧。

1.  BLOB入库

(1)       直接获得数据库连接的情况

这是Oracle提供的标准方式,先插入一个空BLOB对象,然后Update这个空对象。代码如下:
Java代码  
//得到数据库连接(驱动包是weblogic的,没有下载任何新版本)  
  
Class.forName("oracle.jdbc.driver.OracleDriver");  
  
Connection con = DriverManager.getConnection(  
  
          "jdbc:oracle:thin:@localhost:1521:testdb", "test", "test");  
  
//处理事务  
  
con.setAutoCommit(false);  
  
Statement st = con.createStatement();  
  
//插入一个空对象  
  
st.executeUpdate("insert into BLOBIMG  values(103,empty_blob())");  
  
//用for update方式锁定数据行  
  
ResultSet rs = st.executeQuery(  
  
          "select contents from  BLOBIMG  where  id=103 for update");  
  
if (rs.next()) {  
  
   //得到java.sql.Blob对象,然后Cast为oracle.sql.BLOB  
  
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob(1).;  
  
   //到数据库的输出流  
  
OutputStream outStream = blob.getBinaryOutputStream();  
  
   //这里用一个文件模拟输入流  
  
File file = new File("d:\proxy.txt");  
  
  InputStream fin = new FileInputStream(file);  
  
//将输入流写到输出流  
  
byte[] b = new byte[blob.getBufferSize()];  
  
        int len = 0;  
  
        while ( (len = fin.read(b)) != -1) {  
  
          outStream.write(b, 0, len);  
  
          //blob.putBytes(1,b);  
  }  

   //依次关闭(注意顺序)  
  
fin.close();  
  
   outStream.flush();  
  
   outStream.close();  
  
   con.commit();  
  
   con.close();  

(2)       通过JNDI获得数据库连接

在Weblogic中配置到Oracle的JDBC Connection Pool和DataSource,绑定到Context中,假定绑定名为”orads”。

为了得到数据库连接,做一个连接工厂,主要代码如下:

Context context = new InitialContext();

ds = (DataSource) context.lookup("orads");

return ds.getConnection();

以下是BLOB写入数据库的代码:


Java代码  
Connection con = ConnectionFactory.getConnection();  
  
con.setAutoCommit(false);  
  
Statement st = con.createStatement();  
  
st.executeUpdate("insert into BLOBIMG values(103,empty_blob())");  
  
ResultSet rs = st.executeQuery(  
  
          "select contents from  BLOBIMG  where  id=103 for update");  
  
if (rs.next()) {  
  
    //上面代码不变  
  
//这里不能用oracle.sql.BLOB,会报ClassCast 异常  
  
weblogic.jdbc.vendor.oracle.OracleThinBlob blob = (weblogic.jdbc.vendor.oracle.OracleThinBlob) rs.getBlob(1);  
  
    //以后代码也不变  
  
OutputStream outStream = blob.getBinaryOutputStream();  
  
File file = new File("d:\proxy.txt");  
  
  InputStream fin = new FileInputStream(file);  
  
byte[] b = new byte[blob.getBufferSize()];  
  
        int len = 0;  
  
        while ( (len = fin.read(b)) != -1) {  
  
          outStream.write(b, 0, len);  
  
        }
  
fin.close();  
  
   outStream.flush();  
  
   outStream.close();  
  
   con.commit();  
  
   con.close();

2.  BLOB出库

从数据库中读出BLOB数据没有上述由于连接池的不同带来的差异,只需要J2SE的标准类java.sql.Blob就可以取得输出流(注意区别java.sql.Blob和oracle.sql.BLOB)。代码如下:
Java代码  
Connection con = ConnectionFactory.getConnection();  
  
con.setAutoCommit(false);  
  
Statement st = con.createStatement();  
  
//这里的SQL语句不再需要”for update”  
  
ResultSet rs = st.executeQuery(  
  
          "select contents from  BLOBIMG  where  id=103 ");  
  
if (rs.next()) {  
  
   java.sql.Blob blob = rs.getBlob(1);  
  
  
  
   InputStream ins = blob.getBinaryStream();  
    //用文件模拟输出流  
  
File file = new File("d:\output.txt");  
   OutputStream fout = new FileOutputStream(file);  
    //下面将BLOB数据写入文件  
  
    byte[] b = new byte[1024];  
  
    int len = 0;  
  
        while ( (len = ins.read(b)) != -1) {  
  
          fout.write(b, 0, len);  
  
        }  
  
  //依次关闭  
  
  fout.close();  
  
  ins.close();  
  
  con.commit();  
  
  con.close();  
 


3.  从JSP页面提交文件到数据库
(1)       提交页面的代码如下:
Html代码  
<form action="handle.jsp" enctype="multipart/form-data" method="post" >  
  
<input type="hidden" name="id" value="103"/>  
  
<input type="file"  name="fileToUpload">  
  
<input type="submit"  value="Upload">  
  
</form>  
 


(2)       由于JSP没有提供文件上传的处理能力,只有使用第三方的开发包。网络上开源的包有很多,我们这里选择Apache Jakarta的FileUpload,在http://jakarta.apache.org/commons/fileupload/index.html 可以得到下载包和完整的API文档。法奥为adajspException

处理页面(handle.jsp)的代码如下
Html代码  
<%  
  
boolean isMultipart = FileUpload.isMultipartContent(request);  
  
    if (isMultipart) {  
  
      // 建立一个新的Upload对象  
  
      DiskFileUpload upload = new DiskFileUpload();  
  
  
  
    // 设置上载文件的参数  
  
    //upload.setSizeThreshold(yourMaxMemorySize);  
  
    //upload.setSizeMax(yourMaxRequestSize);  
  
    String rootPath = getServletConfig().getServletContext().getRealPath("/") ;  
  
    upload.setRepositoryPath(rootPath+"\uploads");  
  
  
  
     // 分析request中的传来的文件流,返回Item的集合,  
  
     // 轮询Items,如果不是表单域,就是一个文件对象。  
  
      List items = upload.parseRequest(request);  
  
      Iterator iter = items.iterator();  
  
      while (iter.hasNext()) {  
  
        FileItem item = (FileItem) iter.next();  
  
        //如果是文件对象  
  
if (!item.isFormField()) {  
  
  
  
          //如果是文本文件,可以直接显示  
  
          //out.println(item.getString());  
  
  
  
          //将上载的文件写到服务器的WEB-INFwebstart下,文件名为test.txt  
  
          //File uploadedFile = new File(rootPath+"\uploads\test.txt");  
  
          //item.write(uploadedFile);  
        //下面的代码是将文件入库(略):  
        //注意输入流的获取  
InputStream uploadedStream = item.getInputStream();  
        }  

Oracle数据库BLOB字段的存取  

首先建立测试数据表
 drop table filelist;
 commit;

 CREATE TABLE SYSTEM.FILELIST (

 "FILENAME" VARCHAR2(50) NOT NULL,

 "FILESIZE" NUMBER(20)  NULL,

 "FILEBODY" BLOB  NULL, 

 PRIMARY KEY("FILENAME"), UNIQUE("FILENAME")) ;

 commit;

     测试过程,首先将硬盘文件读入数据库,然后再读出到硬盘的另一个新文件里,原码如下:


import java.io.*;

import java.util.*;

import java.sql.*;

import oracle.sql.*;

import oracle.jdbc.driver.*;

import java.text.*;

public class test

{
 public static void main(String args[]) throws java.io.IOException,java.sql.SQLException
 {
  dbBean db1=new dbBean();
 

  byte a[]=null;//**将测试文件test.doc读入此字节数组

  java.io.FileInputStream fin=null;

  java.io.FileOutputStream fout=null;

  oracle.jdbc.OracleResultSet ors=null;//**这里rs一定要用Oracle提供的

  oracle.jdbc.driver.OraclePreparedStatement   opst=null;//**PreparedStatement用

//Oracle提供的

  try

  {
   java.io.File f1=new java.io.File("c:/temp/test.doc");

   java.io.File f2=new java.io.File("c:/temp/testout.doc");//**从BLOB读出的信息写

//入该文件,和源文件对比测试用

   fin=new java.io.FileInputStream(f1);

   fout=new java.io.FileOutputStream(f2);

   int flength=(int)f1.length();//**读入文件的字节长度

   System.out.println("file length::"+flength);

   a=new byte[flength];

   int i=0;int itotal=0;

   /**将文件读入字节数组

   for (;itotal

   {
    i=fin.read(a,itotal,flength-itotal);
   }

   fin.close();
   System.out.println("read itotal::"+itotal);

  /**注意Oracle的 BLOB一定要用EMPTY_BLOB()初始化 

  String mysql="insert into filelist (FileName,FileSize,FileBody) values (?,?,EMPTY_BLOB())";

  opst=(oracle.jdbc.driver.OraclePreparedStatement)db1.conn.prepareStatement(mysql);

         opst.setString(1,"wordtemplate");

         opst.setInt (2,flength);

         opst.executeUpdate();

         opst.clearParameters();

         /**插入其它数据后,定位BLOB字段

           mysql="select filebody from filelist where filename=?";

           opst=(oracle.jdbc.driver.OraclePreparedStatement)db1.conn.prepareStatement(mysql);

           opst.setString(1,"wordtemplate");

           ors=(oracle.jdbc.OracleResultSet)opst.executeQuery();

           if (ors.next())

           {
           oracle.sql.BLOB blob=ors.getBLOB(1);/**得到BLOB字段          

           int j=blob.putBytes(1,a);/**将字节数组写入BLOB字段

           System.out.println("j:"+j);

           db1.conn.commit();

           ors.close();

           }         
    System.out.println("insert into ok");
    byte b[]=null;/**保存从BLOB读出的字节

    opst.clearParameters();

           mysql="select filebody from filelist where filename=?";

           opst=(oracle.jdbc.driver.OraclePreparedStatement)db1.conn.prepareStatement(mysql);

           opst.setString(1,"wordtemplate");

           ors=(oracle.jdbc.OracleResultSet)opst.executeQuery();

           if (ors.next())

           {
           oracle.sql.BLOB blob2=ors.getBLOB(1);   

           System.out.println("blob2 length:"+blob2.length());

           b=blob2.getBytes(1,flength);/**从BLOB取出字节流数据

           System.out.println("b length::"+b.length);

           db1.conn.commit();

           

           ors.close();

           /**将从BLOB读出的字节写入文件

           fout.write(b,0,b.length);

           fout.close();  
     System.out.println("write itotal::"+b.length);
  }

  catch(Exception e)

  {

   System.out.println("errror :"+e.toString() );

   e.printStackTrace();

   

  }

  finally

  { /**关闭所有数据联接

   stmt.close();

   db1.closeConn();

  }

  }

}

    编译运行在TomCat下调试通过。

    需要注意的是Blob存取的过程,一般先存入和BLOB相关的控制数据,如文件的名字,然后查询定位BLOB字段,利用OracleBlob提供的方法:

    public int putBytes(long pos,byte bytes[])

    public byte[]  getBytes(long pos,byte bytes[])

    或者利用

    public OutputStream getBinaryOutputStream() throws SQLException

    public InputStream  getBinaryStream() throws SQLException

  因为利用输入输出流总归还是利用到字节数组缓冲流,所以就不举例子了。
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐