(100分新手求教)把图片保存到Oracle的BLOB字段中的问题
现在有一数据库(Oracle)如下
表 Person
ID Name Picture
1 aaaaa (blob)
请问如何用jsp写添加一条记录,编辑一条记录,显示一条记录.
主要是图片的Blob字段.如何添加,编辑,显示.
请高手帮忙
[解决办法]
要转成2进制
jdbc中如何处理Oracle BLOB字段
为什么我要写这篇文章?
在前段时间我所在的项目中,就碰到了这个问题,我花了2天的时间才将BLOB的问题搞定。我也尝试过网上所介绍的各种方法,那些方法所
使用的原理都一致,但都写得不完整,我也按照网上介绍的方法做了,但都因为其中一些没有提到的小的细节而失败。希望看到这篇文章的人
都不再走弯路。
一般人会走哪些弯路?
1.使用jdk中的方法进行传输。在ResultSet 中有getBlob()方法,在PreparedStatement中有setBlob()方法,所以大多数人都会尝试setBlob
(),getBlob() 进行读写,或者两个数据库之间BLOB的传输。这种方法实际上是行不通的,据网上的一些资料介绍,说sun官方的文档有些方法
都是错误的。
2.使用ResultSet.getBinaryStream 和PreparedStatement.setBinaryStream对BLOB进行读写或两个数据库间的传输。这种方法我自己尝试过,
发现,如果BLOB中存储的是文本文件的话,就没问题,如果是二进制文件,传输就会有问题。
根据自己的经验,以及查阅了Oracle的官方文档,都是使用如下处理方法:
1.新建记录,插入BLOB数据
1.1首先新建记录的时候,使用oracle的函数插入一个空的BLOB,假设字段A是BLOB类型的:
insert xxxtable(A,B,C) values(empty_blob(), 'xxx ', 'yyyy ')
1.2后面再查询刚才插入的记录,然后更新BLOB,在查询前,注意设置Connection的一个属性:
conn.setAutoCommit(false);如果缺少这一步,可能导致fetch out of sequence等异常.
1.3 查询刚才插入的记录,后面要加“ for update ”,如下:
select A from xxxtable where xxx=999 for update ,如果缺少for update,可能出现row containing the LOB value is not locked
的异常
1.4 从查询到的 BLOB字段中,获取blob并进行更新,代码如下:
BLOB blob = (BLOB) rs.getBlob( "A ");
OutputStream os = blob.getBinaryOutputStream();
BufferedOutputStream output = new BufferedOutputStream(os);
后面再使用output.write方法将需要写入的内容写到output中就可以了。例如我们将一个文件写入这个字段中:
BufferedInputStream input = new BufferedInputStream(new File( "c:\\hpWave.log ").toURL().openStream());
byte[] buff = new byte[2048]; //用做文件写入的缓冲
int bytesRead;
while(-1 != (bytesRead = input.read(buff, 0, buff.length))) {
output.write(buff, 0, bytesRead);
System.out.println(bytesRead);
}
上面的代码就是从input里2k地读取,然后写入到output中。
1.5上面执行完毕后,记得关闭output,input,以及关闭查询到的ResultSet
1.6最后执行conn.commit();将更新的内容提交,以及执行conn.setAutoCommit(true); 改回Connction的属性
2.修改记录,方法与上面的方法类似,
2.1首先更新BLOB以外的其他字段
2.2 使用1.3中类似的方法获取记录
2.3 修改的过程中,注意以下:a 需要更新的记录中,BLOB有可能为NULL,这样在执行blob.getBinaryOutputStream()获取的值可能为
null,那么就关闭刚才select的记录,再执行一次update xxxtable set A = empty_blob() where xxx, 这样就先写入了一个空的BLOB(不是null),然后再
使用1.3,1.4中的方法执行更新记录.b 注意别忘了先执行setAutoCommit(false),以及 "for update ",以及后面的conn.commit();等。
3.读取BLOB字段中的数据.
3.1 读取记录不需要setAutoCommit(),以及 select ....for update.
3.2 使用普通的select 方法查询出记录
3.3 从ResultSet中获取BLOB并读取,如下:
BLOB b_to = (BLOB) rs.getBlob( "A ");
InputStream is = b_from.getBinaryStream();
BufferedInputStream input = new BufferedInputStream(is);
byte[] buff = new byte[2048];
while(-1 != (bytesRead = input.read(buff, 0, buff.length))) {
//在这里执行写入,如写入到文件的BufferedOutputStream里
System.out.println(bytesRead);
}
通过循环取出blob中的数据,写到buff里,再将buff的内容写入到需要的地方
4.两个数据库间blob字段的传输
类似上面1和3的方法,一边获取BufferedOutputStream,另外一边获取BufferedInputStream,然后读出写入,需要注意的是写入所用的
Connection要执行conn.setAutoCommit(false);以及获取记录时添加“ for update ”以及最后的commit();
总结以上方法,其根本就是先创建空的BLOB,再获取其BufferedOutputStream进行写入,或获取BufferedInputStream进行读取
[解决办法]
我是新手,有些措辞可能不太专业~
首先,如果你是用jndi获取连接的话,驱动支持不支持blob操作
因为,我就遇到过,根本没有实现blob操作,害我查了半天
一、增加
1、先用 insert into person (name,picture) values ( 'picname ',empty_blob())
2、再 select * from person where name= 'picname ' for update
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob( "picture "); // 得到BLOB对象
File file = new File( "C:\\...... ");// 得到file对象
if(file.exists()&&file.isFile()){//如果文件存在,并且文件为file
OutputStream out = blob.getBinaryOutputStream(); // 建立输出流
InputStream in = new FileInputStream(file); // 建立输入流
int size = blob.getBufferSize();
byte[] buffer = new byte[size]; // 建立缓冲区
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
file.delete();
二、修改
和新增里面的方法一样
三、查看
看二楼的方法,因为查看的方法很多,不知道你要哪种? 下载?预览?
[解决办法]
存路径不是更好,省得那么麻烦,实现也容易得多.何必庸人自扰呢?
[解决办法]
强烈推荐更改数据库字段! 存路径吧!
曾经.
我和楼主一样,也是设计了一个 blob字段 .可以实现存储.但是到后来读取图片的时候就不知道怎么吧图片放到合适的地方.
后来我发现用存路径的方法方便实用得多!!
先用jspsmartupload包.将图片存到服务器上,再改名字.这样就避免了重名.
然后将路径存到数据库中.
我把代码给你吧.
<%@ page contentType= "text/html; charset=gb2312 " language= "java " import= "java.sql.*,com.jspsmart.upload.*,java.text.SimpleDateFormat " errorPage= " " %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN " "http://www.w3.org/TR/html4/loose.dtd ">
<html>
<head>
<meta http-equiv= "Content-Type " content= "text/html; charset=gb2312 ">
<title> 文件上传 </title>
</head>
<jsp:useBean id= "db " scope= "page " class= "duban.DataBean "/>
<jsp:useBean id= "tb " scope= "page " class= "duban.ToolBean "/>
<body>
<%
String icode=(String)session.getAttribute( "code ");
String iname=(String)session.getAttribute( "name ");
if(icode==null || iname==null)
{
out.println( " <script language=\ "javascript\ "> window.parent.location.href=\ "nologin.jsp\ "; </script> ");
return;
}
String fzrcode=(String)session.getAttribute( "fzrcode ");
%>
<%
String root=getServletContext().getRealPath( "/ ");
root=root+ "image\\qianming\\ ";
String src= " ";
try{
SmartUpload su = new SmartUpload();
su.initialize(pageContext);
//su.setMaxFileSize(10000);
//su.setAllowedFilesList( "jpg,gif,bmp,dib,jpeg ");
//su.setAllowedFilesList( "doc,txt ");
su.upload();
//int count = su.save( "C:/Tomcat/webapps/duban/image/qianming ",su.SAVE_PHYSICAL);
com.jspsmart.upload.File file = su.getFiles().getFile(0);
String strExt = su.getFiles().getFile(0).getFileExt();
java.text.SimpleDateFormat simpleDateFormat = new java.text.SimpleDateFormat( "yyyyMMddHHmmssSSS ");//取系统时间,精确到毫秒。
java.util.Date date = new java.util.Date();
String strTime = simpleDateFormat.format(date);
String strFileName = strTime + ". " + strExt;
//String FilePath = "C:/Tomcat/webapps/duban/image/qianming "+strFileName;//uploadpath为保存目录名。
//判断服务器使用的操作系统,处理文件的存放目录
if(root.indexOf( "\\ ",0)> = 0){//windown
root=root.substring(root.lastIndexOf( "/ ")+1);
src= "..\\image\\qianming\\ "+strFileName;
}else{//linux
root=root.substring(root.lastIndexOf( "\\ ")+1);
src= "../image/qianming/ "+strFileName;
}
file.saveAs(root+strFileName,su.SAVE_PHYSICAL);
}
catch(Exception e){
out.println( " <script language=javascript> alert(\ "请确认图片大小和类型是否合适\ "); </script> ");
}
%>
<%
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
String sql= " ";
try{
conn=db.GetConnection();
conn.setAutoCommit(false);
stmt=conn.createStatement();
sql= "update db_fuzeren set qianming= ' "+src+ " ' where code= ' "+fzrcode+ " ' ";
stmt.execute(sql);
conn.commit();
response.sendRedirect( "fuzeren_index.jsp ");
}
catch(Exception e){
conn.rollback();
out.println( " <div align=center> 添加负责人失败! "+sql+ " </div> ");
out.println(e);
out.println( " <p> <p> <div align=center> <a href=javascript:history.back();> 返回前页 </a> </div> ");
}
finally{
if(rs!=null) rs.close();
if(stmt!=null) stmt.close();
if(conn!=null && !conn.isClosed()) conn.close();
}
%>
</body>
</html>
[解决办法]
将图片保存到数据库中客户端用<img src="/aImgReadServlet?pictureId=<%=pictureId%>"></img>
在aImgReadServlet 中
1. 获得pictureId,再用这个pictureId查询blob字段,获得其输出流;
2. 再获得response输出流;
3. 将图片blob的输出流写到response输出流;
4. 执行收尾工作:关闭数据库资源,关闭response输出流,等等。