preparedstatement.setbinarystream写不进数据库的主因是流被提前关闭或未触发实际写入,mysql需配置useserverprepstmts=true并传精确长度,postgresql要求事务内操作且字段为bytea,oracle不支持带长度参数的重载。
preparedstatement.setbinarystream 为什么写不进数据库?
常见现象是执行完 executeupdate() 没报错,但查数据库发现 blob 字段为 null 或空字节。根本原因不是 api 用错了,而是流被提前关闭或未触发实际写入。
- 必须确保传入的
InputStream在executeUpdate()调用前仍处于可读状态(比如文件没被 close,网络流没超时) - MySQL 驱动默认把大对象当“客户端临时数据”处理,如果连接配置没开
useServerPrepStmts=true,setBinaryStream可能被降级成setBytes,悄悄把整个流加载进内存再转 byte[] —— 大文件直接 OOM - PostgreSQL 的
setBinaryStream要求字段类型明确是bytea,且不能在事务外调用(否则抛PGSQLException: Large Objects may not be used outside a transaction)
MySQL 和 PostgreSQL 的 setBinaryStream 参数差异
两个主流驱动对第二个参数(长度)的处理逻辑完全不同,填错就静默失败。
- MySQL(Connector/J 8.0+):第二个参数是「精确字节数」,必须传真实长度;传 -1 会回退到
setBytes,传 0 直接写空 blob - PostgreSQL(pgjdbc 42.6+):第二个参数是「最大预期长度」,可以略大于实际值(如传 10 * 1024 * 1024 写 5MB 图片),但不能为 0 或负数;传 -1 会抛异常
- Oracle(ojdbc8):不支持
setBinaryStream(int, InputStream, long)重载,只能用setBinaryStream(int, InputStream)(无长度参数),靠流 EOF 判断结束
示例(MySQL 安全写法):
File file = new File("/tmp/photo.jpg");
try (FileInputStream fis = new FileInputStream(file)) {
ps.setBinaryStream(1, fis, file.length()); // 必须是 file.length()
ps.executeUpdate();
}
上传大文件时连接和事务怎么配才不崩
BLOB 写入本质是长耗时 I/O,不调优的话,要么连不上,要么事务超时,要么驱动自己 abort。
- MySQL 连接串加
maxAllowedPacket=256M&connectTimeout=30000&socketTimeout=600000,否则 16MB 以上文件直接被服务端拒收 - PostgreSQL 必须显式开启事务:
conn.setAutoCommit(false); ps = conn.prepareStatement("INSERT INTO docs(name, content) VALUES (?, ?)"); ps.setBinaryStream(2, fis, fileSize); ps.executeUpdate(); conn.commit(); // 不 commit,LOB 资源不释放 - 别在 Spring
@Transactional方法里直接传request.getInputStream()—— HTTP 请求流只读一次,第二次调用setBinaryStream会读到空流
为什么图片存进去了却打不开?
不是编码问题,是内容被意外截断或污染。最常踩的三个坑:
- 前端用
FormData上传时,后端没用MultipartFile(Spring)或Part(Servlet 4.0+)正确提取原始二进制,而是调了getInputStream().readAllBytes()再转流 —— 中间多一次内存拷贝,大文件易 OOM 且可能丢尾部字节 - 数据库字段定义太小:MySQL 的
BLOB最大 64KB,要存照片得用MEDIUMBLOB(16MB)或LONGBLOB(4GB) - 读取时没设响应头:
response.setContentType("image/jpeg")缺失会导致浏览器下载而不是预览,误以为“损坏”
真正可靠的路径只有两条:要么确认 file.length() == SELECT LENGTH(content) 查询结果一致;要么用 ps.getBinaryStream(2) 读出来和原文件做 SHA-256 校验。
流操作没银弹,每一步都得盯住源头是否完整、中间是否被缓冲、终点是否落盘。尤其是跨框架传递流的时候,别信“自动处理”,先看它关没关流。










