主題:使用JAVA讀取ORACLE BLOB欄位實現上傳下載
作者:蔡毅(caiyi0903@hotmail.com)
時間:2005-6-22
一 BLOB概述
大物件類型BLOB全稱為Binary Large Objects,即二進位大對象。可以把BLOB區別為三種形式:聲像資料、位元據和大文本資料。因此,最常見的應用就是儲存圖形、聲音等對象,此外大二進位對象、OLE對象也可以通過BLOB類型存入資料庫,如果文字物件過大,超出了文本類型的規定長度,則必須用BLOB欄位進行儲存。我們在經常使用的編程環境中並不能直接支援BLOB欄位,因此需要調用相應的函數完成BLOB的使用。
二 實際Struts項目的處理流程
1 插入BLOB欄位的流程
展示層:
上傳使用struts的<html:file property="drawingFile"/>標籤,提交給指定處理的Action,在ActionForm中使用struts內建的FormFile
來儲存檔案。
核心代碼:
<html:form action="/DrawingInputMultiAction" enctype="multipart/form-data">
<html:file property="drawingFile"/>
....省略
</html:form>
控制層:
在Action中將傳入的ActionForm中的檔案欄位賦給VO值對象,並調用業務代理類的上傳方法。
核心代碼:
//新增
if(actionType.equals("insert")) {
//得到檔案類型
int iFileType = this.getFileType(drawingInputForm.getFileExtendName());
if(iFileType == 0) {
//不支援檔案類型
this.addError(request, "drawing.errors.upload.UnSupportedFileType");
} else {
DrawingVO objDrawingVO = new DrawingVO();
//圖紙基本屬性
objDrawingVO.setDrawingName(drawingInputForm.getDrawingName());
...省略其他set方法
//執行新增(上傳)
int iRt = objDrawingMan.insertDrawing(objDrawingVO);
...省略
}
Facade門面:
通過業務代理類調用DAO中的上傳方法,對用戶端完全透明。
public int insertDrawing(DrawingVO drawingVO) throws ComtopModuleException {
try {
DrawingDAO drawingDAO = new DrawingDAO();
return drawingDAO.insertDrawing(drawingVO);
} catch(DrawingException ex) {
throw new ComtopModuleException("drawing.errors.insert", ex);
}
}
持久層:
DAO中實現和ORACLE資料庫的底層交涉,完成真正的檔案上傳。
需要先插入一個空BLOB對象,然後Update這個Null 物件。
public int insertDrawing(DrawingVO drawingVO) throws DrawingException {
PreparedStatement pstmt = null;
Statement stmt = null;
Connection conn = null;
int iKey = 0;
ResultSet rs = null;
//定義SQL語句
String strSQLInsert = null;
String strSQLUpdate = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);
//插入空BLOB,empty_blob(),其中表中的Content是BLOC類型欄位
strSQLInsert =
"insert into PROD_DRAWING (DRAWING_ID, DRAWING_NAME, 省略..." +
"CONTENT)" +
"values (?, ?, 省略..., empty_blob())";
//得到待處理檔案
FormFile drawingFile = drawingVO.getDrawingFile();
//插入普通欄位
pstmt = conn.prepareStatement(strSQLInsert);
//得到主鍵
iKey = Toolkit.getInstance().getNextKey(DrawingInfo.ID_STORE_KEY_DRAWING);
pstmt.setInt(1, iKey);
....省略其他set方法
pstmt.executeUpdate();
stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
strSQLUpdate =
"SELECT CONTENT FROM PROD_DRAWING WHERE DRAWING_ID ='" +
iKey + "'" + " FOR UPDATE";
//讀出記錄以增加圖片Blob欄位
rs = stmt.executeQuery(strSQLUpdate);
if(rs.next()) {
logger.debug("開始寫入BLOB");
//這裡不能用oracle.sql.BLOB,會報ClassCast異常
weblogic.jdbc.vendor.oracle.OracleThinBlob blob = (weblogic.jdbc.vendor.
oracle.
OracleThinBlob)rs.getBlob(1);
logger.debug("得到輸出資料流");
OutputStream outStream = blob.getBinaryOutputStream();
InputStream fin = drawingFile.getInputStream();
logger.debug("開始分配緩衝");
byte[] b = new byte[blob.getBufferSize()];
int len = 0;
while((len = fin.read(b)) != -1) {
logger.debug("正在寫入BLOB流");
outStream.write(b, 0, len);
}
logger.debug("關閉所有流");
fin.close();
outStream.flush();
outStream.close();
}
rs.close();
conn.commit();
} catch(Exception ex) {
...省略
}finally {
DBUtil.destroyDB(rs, pstmt, conn);
}
return iKey;
}
2 讀取BLOB欄位的流程
從資料庫中讀出BLOB資料沒有上述由於串連池的不同帶來的差異,程式流程同插入BLOB欄位,但是讀BLOB就不用那麼複雜了,只需要J2SE的標準類java.sql.Blob就可以取得輸出資料流。
DAO中的核心代碼:
public DrawingVO readDrawing(int drawingId) throws DrawingException {
PreparedStatement pstmt = null;
Connection conn = null;
DrawingVO objDrawingVO = null;
ResultSet rs = null;
//定義SQL語句
String strSQL = "SELECT * FROM PROD_DRAWING WHERE DRAWING_ID=?";
try {
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(strSQL);
//設定參數
pstmt.setInt(1, drawingId);
//執行查詢
rs = pstmt.executeQuery();
while(rs.next()) {
objDrawingVO = new DrawingVO();
objDrawingVO.setDrawingId(rs.getInt("DRAWING_ID"));
objDrawingVO.setDrawingName(rs.getString("DRAWING_NAME"));
...省略其他set方法
//set BLOB到VO中
objDrawingVO.setContent(rs.getBlob("CONTENT"));
}
} catch(Exception ex) {
...省略
}finally {
DBUtil.destroyDB(rs, pstmt, conn);
}
return objDrawingVO;
}
這樣,傳到Action中VO對象就包含這個BLOB對象了,然後需要在Action中對該對象轉為輸入資料流,可以選擇檔案輸出資料流或Servlet輸出資料流,根據具體情況定,這裡選擇檔案輸出資料流。
核心代碼:
private String getBlobToFile(Blob blob, DrawingVO objDrawingVO) throws Exception {
InputStream ins = blob.getBinaryStream();
//用檔案類比輸出資料流
String strFileName = objDrawingVO.getDrawingName() + "." +
objDrawingVO.getFileExtendName();
String strRootFilePath = this.getServlet().getServletContext().getRealPath("");
String strFilePath = "/temp/" + strFileName;
String contextFilePath = strRootFilePath + strFilePath;
//定義檔案對象
File file = new File(this.getServlet().getServletContext().getRealPath("") + "/temp");
if(!file.exists()) {
file.mkdir();
}
//定義輸出資料流
OutputStream fout = new FileOutputStream(contextFilePath, true);
//下面將BLOB資料寫入檔案
byte[] b = new byte[1024];
int len = 0;
while((len = ins.read(b)) != -1) {
fout.write(b, 0, len);
}
//依次關閉
fout.close();
ins.close();
return strFilePath;
}
最後,在Action中調用這個私人方法,完成讀取操作。