Oracle的Blob字段比较特殊,他比long字段的性能要好很多,可以用来保存例如图片之类的二进制数据。
user_id number(10) primary key,
name varchar2(20),
image blob
insert into user_info values (1, 'Jacky', empty_blob());
select image from user_info where user_id = ? for update;
update user_info set image = ? where user_id = ?;
//加载驱动程序
static
{
//加载驱动程序
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
<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();
}
}
//加载驱动程序
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();
}
}
-------------------------------------------------------------------------------------------------
"jdbc:oracle:thin:@localhost:1521:testdb", "test", "test");
"select contents from
BLOBIMG
where
id=103 for update");
//得到java.sql.Blob对象,然后Cast为oracle.sql.BLOB
//到数据库的输出流
//这里用一个文件模拟输入流
InputStream fin = new FileInputStream(file);
int len = 0;
while ( (len = fin.read(b)) != -1) {
outStream.write(b, 0, len);
//blob.putBytes(1,b);
}
//依次关闭(注意顺序)
outStream.flush();
outStream.close();
con.commit();
con.close();
"select contents from
BLOBIMG
where
id=103 for update");
//上面代码不变
//以后代码也不变
InputStream fin = new FileInputStream(file);
int len = 0;
while ( (len = fin.read(b)) != -1) {
outStream.write(b, 0, len);
}
outStream.flush();
outStream.close();
con.commit();
con.close();
"select contents from
BLOBIMG
where
id=103 ");
java.sql.Blob blob = rs.getBlob(1);
InputStream ins = blob.getBinaryStream();
//用文件模拟输出流
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();
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();
//如果是文件对象
//如果是文本文件,可以直接显示
//out.println(item.getString());
//将上载的文件写到服务器的WEB-INFwebstart下,文件名为test.txt
//File uploadedFile = new File(rootPath+"\uploads\test.txt");
//item.write(uploadedFile);
//下面的代码是将文件入库(略):
//注意输入流的获取
}
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;
测试过程,首先将硬盘文件读入数据库,然后再读出到硬盘的另一个新文件里,原码如下:
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用
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
写入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
(
);
--1. 插入空blob: (如果在数据库中采用默认值方式对Blob字段赋值, 此步可省略)
--2. 获得blob的cursor:
--3. 用cursor往数据库写数据:
//读取Blob数据
package demo;
import java.sql.*;
import java.io.*;
public class ReadBlob
{
[java] view plaincopy
//读取Blob数据
package demo;
import java.sql.*;
import java.io.*;
public class ReadBlob
{
[java] view plaincopy
}
//写Blob数据
package demo;
import java.sql.*;
import oracle.sql.BLOB;//▲此处的BLOB类全大写, 而java.sql.Blob中的Blob非全大写
import java.io.*;
public class WriteBlob
{
}
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(
//处理事务
con.setAutoCommit(false);
Statement st = con.createStatement();
//插入一个空对象
st.executeUpdate("insert into BLOBIMG
values(103,empty_blob())");
//用for update方式锁定数据行
ResultSet rs = st.executeQuery(
if (rs.next()) {
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob(1).;
OutputStream outStream = blob.getBinaryOutputStream();
File file = new File("d:\proxy.txt");
//将输入流写到输出流
byte[] b = new byte[blob.getBufferSize()];
fin.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(
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");
byte[] b = new byte[blob.getBufferSize()];
fin.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(
if (rs.next()) {
File file = new File("d:\output.txt");
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 (!item.isFormField()) {
InputStream uploadedStream = item.getInputStream();
Oracle数据库BLOB字段的存取
首先建立测试数据表
import java.io.*;
import java.util.*;
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.driver.*;
import java.text.*;
public class test
{
//Oracle提供的
//入该文件,和源文件对比测试用
}
因为利用输入输出流总归还是利用到字节数组缓冲流,所以就不举例子了。