承接上文,本文提供一個完整可運行的單檔案程式,演示 SQL Server VARBINARY(MAX) 欄位的圖片寫入、讀取、二進制完整性驗證全流程。代碼採用 jTDS 驅動、ODBC 數據源配置,適合快速驗證、教學演示、原型開發。
import java.io.*;
import java.sql.*;
public class BlobImageDemo {
// 配置區
private static final String JDBC_URL = "jdbc:odbc:LW"; // 系統 ODBC DSN 名稱
private static final String TABLE = "Ss";
private static final String ID_COL = "id";
private static final String DATA_COL = "data";
private static final String TEST_ID = "0012";
private static final File SOURCE_IMG = new File("D:\\test1.jpg");
private static final File TARGET_IMG = new File("D:\\test2.jpg");
private static final int BUFFER_SIZE = 50000; // 50KB
public static void main(String[] args) {
BlobImageDemo demo = new BlobImageDemo();
demo.insertImage();
demo.extractAndVerify();
}
// 寫入圖片
public void insertImage() {
String sql = "INSERT INTO " + TABLE + " (" + ID_COL + ", " + DATA_COL + ") VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection(JDBC_URL);
PreparedStatement pstmt = conn.prepareStatement(sql);
InputStream fin = new FileInputStream(SOURCE_IMG)) {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // JDK 8 內建
pstmt.setString(1, TEST_ID);
pstmt.setBinaryStream(2, fin, (int) SOURCE_IMG.length());
pstmt.executeUpdate();
System.out.println("✅ 圖片寫入成功: " + SOURCE_IMG.length() + " bytes");
} catch (Exception e) {
System.err.println("❌ 寫入失敗: " + e.getMessage());
e.printStackTrace();
}
}
// 讀取並驗證
public void extractAndVerify() {
String sql = "SELECT " + DATA_COL + " FROM " + TABLE + " WHERE " + ID_COL + " = ?";
try (Connection conn = DriverManager.getConnection(JDBC_URL);
PreparedStatement pstmt = conn.prepareStatement(sql);
OutputStream out = new BufferedOutputStream(new FileOutputStream(TARGET_IMG), BUFFER_SIZE)) {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
pstmt.setString(1, TEST_ID);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
try (InputStream in = rs.getBinaryStream(DATA_COL)) {
if (in == null) {
System.err.println("❌ 數據為 NULL");
return;
}
byte[] buffer = new byte[BUFFER_SIZE];
int len;
long total = 0;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
total += len;
}
out.flush();
System.out.println("✅ 圖片讀取成功: " + total + " bytes");
// 二進制完整性驗證
verifyBinaryEqual(SOURCE_IMG, TARGET_IMG);
}
} else {
System.err.println("❌ 未找到記錄: " + TEST_ID);
}
}
} catch (Exception e) {
System.err.println("❌ 讀取失敗: " + e.getMessage());
e.printStackTrace();
}
}
// 位元組級比對驗證
private void verifyBinaryEqual(File f1, File f2) throws IOException {
if (f1.length() != f2.length()) {
System.err.println("❌ 文件大小不一致: " + f1.length() + " vs " + f2.length());
return;
}
try (InputStream in1 = new FileInputStream(f1);
InputStream in2 = new FileInputStream(f2)) {
int b1, b2;
long pos = 0;
while ((b1 = in1.read()) != -1) {
b2 = in2.read();
if (b1 != b2) {
System.err.println("❌ 位元組差異 at position " + pos + ": " + b1 + " vs " + b2);
return;
}
pos++;
}
System.out.println("✅ 二進制內容完全一致,驗證通過!");
}
}
}
LW (與代碼 jdbc:odbc:LW 對應)localhost 或 IPCREATE TABLE Ss (
id CHAR(4) PRIMARY KEY,
data VARBINARY(MAX) NOT NULL
);
D:\test1.jpg 存在javac BlobImageDemo.javajava BlobImageDemoD:\test2.jpg 是否可正常開啟、與原圖一致| 環節 | 關鍵 API | 作用 |
|---|---|---|
| 寫入 | pstmt.setBinaryStream(2, fin, length) | 流式傳輸,避免 byte[] 全量入堆 |
| 讀取 | rs.getBinaryStream("data") | 獲取資料庫游標流 |
| 緩衝 | BufferedOutputStream + byte[50000] | 減少系統調用,提升寫入吞吐 |
| 資源管理 | try-with-resources (Java 7+) | 自動關閉 Connection/Statement/ResultSet/Stream |
| 驗證 | 位元組級比對 | 證明寫入/讀取無損、無截斷、無編碼轉換 |
executeUpdate() 返回值、事務是否提交 (ODBC 連線預設 auto-commit=true)FileInputStream 路徑錯誤、文件鎖定、長度超過 int 範圍 (>2GB)flush()、流關閉順序錯誤、資料庫欄位類型非 VARBINARY/IMAGEJdbcOdbcDriver → jTDS / mssql-jdbcJdbcTemplate / MyBatis @Insert / JPA @Lob@Transactional本文提供了最小可驗證閉環:從 ODBC 配置、建表、寫入、讀取、到二進制完整性驗證。掌握 setBinaryStream/getBinaryStream + 緩衝區分塊 + try-with-resources 這套核心模式,即可應對生產環境 99% 的大對象讀寫場景。驗證通過後,按遷移清單逐步引入企業級基礎設施,實現從原型到生產的平滑演進。