java中处理blob应优先用setbinarystream流式读写,避免setbytes导致oom;oracle需事务内操作并用empty_blob()占位,且驱动与jdk版本须匹配。

Java 中用 PreparedStatement 存储二进制数据(如图片、PDF、音频等),核心是使用 setBytes() 或 setBinaryStream() 方法,配合数据库中对应的二进制类型字段(如 MySQL 的 BLOB、PostgreSQL 的 BYTEA、Oracle 的 BLOB)。
确认数据库字段类型支持二进制存储
插入前必须确保目标列是二进制类型:
- MySQL:用
BLOB(小文件)、MEDIUMBLOB(最大 16MB)、LONGBLOB(最大 4GB) - PostgreSQL:推荐
BYTEA;大对象也可用LO(需额外处理) - Oracle:用
BLOB类型,且通常需先插入空EMPTY_BLOB()占位再获取流写入 - H2 / HSQLDB:支持
BLOB,行为接近 MySQL
用 setBytes() 直接传 byte[](适合小到中等数据)
适用于内存能容纳的二进制内容(如头像、小图标、短音频):
String sql = "INSERT INTO documents (name, content) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "report.pdf");
ps.setBytes(2, Files.readAllBytes(Paths.get("report.pdf"))); // 自动转为 byte[]
ps.executeUpdate();
}
注意:不要手动 new byte[] 再循环读取——Files.readAllBytes() 更安全简洁;但文件过大(如 >10MB)时容易 OOM,此时应改用流式写入。
用 setBinaryStream() 流式写入(推荐用于大文件)
避免一次性加载全部字节到内存,适合上传大附件:
String sql = "INSERT INTO documents (name, content) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql);
FileInputStream fis = new FileInputStream("large-video.mp4")) {
ps.setString(1, "large-video.mp4");
// 传入 InputStream 和可选长度(设 -1 表示长度未知,驱动会自动探测)
ps.setBinaryStream(2, fis, fis.getChannel().size()); // 推荐提供准确长度
ps.executeUpdate();
}
关键点:
-
setBinaryStream(int paramIndex, InputStream in, long length)是最明确的方式(JDBC 4.0+) - setBinaryStream(int, InputStream)(JDBC 4.2+),但部分老驱动可能不支持
- BLOB,有时需先执行 INSERT 获取 LOB 定位器,再用
getBLOB().setBinaryStream()写入——这是特例,非通用流程
读取时用 getBytes() 或 getBinaryStream()
查询后还原二进制内容:
String sql = "SELECT content FROM documents WHERE name = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "report.pdf");
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
byte[] data = rs.getBytes("content"); // 小数据直接取
// 或流式读取:
try (InputStream is = rs.getBinaryStream("content")) {
Files.copy(is, Paths.get("downloaded.pdf"));
}
}
}
}
提醒:getBytes() 仍会把整列加载进内存,大数据务必优先选 getBinaryStream() 配合缓冲读取。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











