首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java JDBC 實作:將圖片檔案寫入 Oracle BLOB 欄位完整範例

     編輯:Java 相關知識

環境需求

  • 資料庫:Oracle 10g XE
  • JDBC 驅動:jdbc12.jar (或 ojdbc14.jar 等較新版本)
  • 資料表結構:blobtest(name VARCHAR2(20), content BLOB)

完整實作代碼

package dbdriver;

import java.sql.*;
import java.io.*;
import oracle.sql.*;

public class WriteBlob {

    public static void main(String[] args) {
        try {
            // 1. 準備檔案路徑
            String fileName = "E:\\jianxin.bmp";
            File f = new File(fileName);
            String fName = f.getName();
            int i = fName.lastIndexOf('.');
            String fName2 = (i > 0 && i < fName.length() - 1) ? fName.substring(0, i) : fName;
            System.out.println("檔案名(無副檔名): " + fName2);

            // 2. 註冊驅動並建立連線
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
            Connection conn = DriverManager.getConnection(
                "jdbc:oracle:thin:@localhost:1521:XE", "system", "duduli");
            conn.setAutoCommit(false);  // 手動控制交易

            // 3. 先插入空 BLOB 佔位符,取得 BLOB 定位器
            BLOB blob = null;
            PreparedStatement pstmt = conn.prepareStatement(
                "INSERT INTO blobtest(name, content) VALUES(?, empty_blob())");
            pstmt.setString(1, fName2);
            pstmt.executeUpdate();
            pstmt.close();

            // 4. 為 UPDATE 鎖定該列,取得可寫入的 BLOB 物件
            pstmt = conn.prepareStatement(
                "SELECT content FROM blobtest WHERE name = ? FOR UPDATE");
            pstmt.setString(1, fName2);
            ResultSet rset = pstmt.executeQuery();
            if (rset.next()) {
                blob = (BLOB) rset.getBlob(1);
            }
            pstmt.close();

            // 5. 讀取檔案並寫入 BLOB
            FileInputStream fin = new FileInputStream(f);
            System.out.println("檔案大小: " + fin.available() + " bytes");
            
            OutputStream out = blob.getBinaryOutputStream();
            byte[] data = new byte[fin.available()];
            fin.read(data);
            out.write(data);
            
            fin.close();
            out.close();

            // 6. 更新資料庫確認寫入
            pstmt = conn.prepareStatement(
                "UPDATE blobtest SET content = ? WHERE name = ?");
            pstmt.setBlob(1, blob);
            pstmt.setString(2, fName2);
            pstmt.executeUpdate();
            pstmt.close();

            // 7. 提交交易並關閉連線
            conn.commit();
            conn.close();
            System.out.println("圖片寫入 BLOB 成功!");

        } catch (SQLException e) {
            System.err.println("SQL 錯誤: " + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("IO 錯誤: " + e.getMessage());
        }
    }
}

關鍵步驟解析

1. 兩階段寫入機制

Oracle BLOB 寫入需分兩步:

  1. INSERT empty_blob(): 建立空 BLOB 定位器,獲得該列的鎖
  2. SELECT ... FOR UPDATE: 取得可寫入的 oracle.sql.BLOB 物件 (含定位器)

直接 INSERT INTO ... VALUES(?, ?) 設定 setBlob 在某些 Oracle 版本/驅動下會失敗,因為 JDBC 驅動無法直接串流寫入。

2. 串流寫入流程

OutputStream out = blob.getBinaryOutputStream();  // 取得 BLOB 輸出串流
byte[] data = new byte[fin.available()];          // 讀取完整檔案到記憶體
fin.read(data);
out.write(data);                                  // 寫入資料庫
out.close();                                      // 關閉串流,完成寫入

注意: 此範例一次性讀取整個檔案到記憶體,大檔案建議分塊讀寫 (如 4KB/8KB 緩衝區循環)。

3. 交易控制

  • conn.setAutoCommit(false):關閉自動提交,確保寫入原子性
  • conn.commit():確認寫入
  • 異常時隱含回滾 (連線關閉會自動回滾未提交交易)

常見問題與解決方案

問題 1:streams type cannot be used in batching

原因: Oracle JDBC 不允許串流操作批次處理。

解決: 先寫入空 BLOB/Clob,flush() 並 refresh() 鎖定列,再寫入實際內容:

user.setImage(Hibernate.createBlob(new byte[1]));
user.setDesc(Hibernate.createClob(" "));
session.save(user);
session.flush();
session.refresh(user, LockMode.UPGRADE);
// 此後可正常寫入 blob/clob 實際內容

問題 2:大檔案記憶體不足

解決: 分塊讀寫,避免一次性載入完整檔案:

FileInputStream fin = new FileInputStream(f);
OutputStream out = blob.getBinaryOutputStream();
byte[] buffer = new byte[8192];  // 8KB 緩衝區
int bytesRead;
while ((bytesRead = fin.read(buffer)) != -1) {
    out.write(buffer, 0, bytesRead);
}
fin.close();
out.close();

問題 3:權限不足

確保資料庫使用者擁有 CREATE TABLE、INSERT、UPDATE、SELECT ... FOR UPDATE 權限。

讀取 BLOB 並存檔

// 1. 取得 BLOB 物件
Blob img = user.getImage();  // 或從 ResultSet 取得
InputStream in = img.getBinaryStream();

// 2. 寫入檔案
FileOutputStream fos = new FileOutputStream("tempimg.jpg");
byte[] buffer = new byte[102400];
int len;
while ((len = in.read(buffer)) != -1) {
    fos.write(buffer, 0, len);
}
fos.close();
in.close();

CLOB 寫入範例 (Oracle 專用)

oracle.sql.Clob clob = user.getDesc();
Writer writer = clob.getCharacterOutputStream();
writer.write("大量文字內容...");
writer.close();
session.save(user);

最佳實踐總結

  1. 使用 PreparedStatement 防止 SQL 注入
  2. 大檔案採用分塊串流讀寫,避免 OOM
  3. 妥善管理連線與交易,確保資料一致性
  4. Oracle 專用 API (oracle.sql.BLOB/CLOB) 功能最完整,但降低可攜性;標準 JDBC java.sql.Blob/Clob 可攜性較好
  5. 生產環境建議使用連線池 (HikariCP、Druid) 管理資料庫連線
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved