最近做一個J2EE項目,需要在JSP頁面實現對檔案的上傳和下載。很早以前就知道JDBC支援大對象(LOB)的存取,以為很容易,做起來才發現問題多多,讀了一大堆文章,反而沒有什麼頭緒了。正如一位網友文章所講:“…網路上的教程99%都是行不通的,連SUN自己的文檔都一直錯誤……”,實際情況大致如此了。
存取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這個Null 物件。代碼如下:
//得到資料庫連接(驅動包是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();
//插入一個Null 物件
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寫入資料庫的代碼:
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.OracleThinBlobblob = (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)。代碼如下:
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) 提交頁面的代碼如下:
<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">