关于在 FileInputStream 上使用 PreparedStatement.setBlob 到具有 Bytea 类型的 postgres 数据库的问题
Posted
技术标签:
【中文标题】关于在 FileInputStream 上使用 PreparedStatement.setBlob 到具有 Bytea 类型的 postgres 数据库的问题【英文标题】:Issue about using PreparedStatement.setBlob on a FileInputStream to a postgres database with Bytea type 【发布时间】:2018-06-20 21:39:17 【问题描述】:正如标题所暗示的,我正在尝试使用 .setBlob pstm 将文件直接上传到我的 postgresql 数据库到数据类型 Bytea。但是 JBDC 不喜欢它并给我以下错误:
java.sql.SQLFeatureNotSupportedException: Method org.postgresql.jdbc4.Jdbc4PreparedStatement.setBlob(int, InputStream) is not yet implemented.
at org.postgresql.Driver.notImplemented(Driver.java:727)
我尝试过 .setBytes() 方法,但不确定如何正确使用可用数据。
这是我使用的代码:
private void writeToDB(Connection conn, String fileName, InputStream is, String description) throws SQLException
String sql = "Insert into Attachment(Id,File_Name,File_Data,Description) " //
+ " values (?,?,?,?) ";
PreparedStatement pstm = conn.prepareStatement(sql);
Long id = this.getMaxAttachmentId(conn) + 1;
pstm.setLong(1, id);
pstm.setString(2, fileName);
pstm.setBlob(3, is);
pstm.setString(4, description);
pstm.executeUpdate();
如果我是使用*** 的新手,请告诉我是否可以做些什么来改进这篇文章。
【问题讨论】:
documentation page on storing binary data 的示例代码可能对您有用。基本上,它使用pstm.setBinaryStream(is, theLengthOfTheFile)
适应了您的示例代码。
@MickMnemonic 我试过这个但得到以下错误:错误:列“file_data”是位类型但表达式是字节类型提示:您需要重写或转换表达式。
所以,Attachment.file_data
不是实际上是bytea
类型,而是bit
。听起来您对 Attachment
的表定义有误,但我们无法确定,因为您尚未披露。
@MickMnemonic 真的很抱歉,我忘记了我之前重新制作了桌子以尝试另一种方法!您的答案就是解决方案,现在我只需要制作它,以便我可以自动获取输入文件的长度。另外,我如何给您提供解决方案的功劳?我只在实际回复中看到该选项,而不是在 cmets 中。
不用担心。我认为您实际上可以省略setBinaryStream()
的第三个参数(文件长度)。这只会读取流,直到达到end-of-file
。
【参考方案1】:
根据PostgreSQL tutorial on storing binary data,您可以使用pstm.setBinaryStream()
将二进制数据流式传输到数据库。使用您的示例代码,这将按如下方式工作:
private void writeToDB(Connection conn, String fileName, InputStream is, String description)
throws SQLException
String sql = "Insert into Attachment(Id,File_Name,File_Data,Description) "
+ " values (?,?,?,?)";
try (PreparedStatement pstm = conn.prepareStatement(sql))
Long id = this.getMaxAttachmentId(conn) + 1;
pstm.setLong(1, id);
pstm.setString(2, fileName);
pstm.setBinaryStream(3, is);
pstm.setString(4, description);
pstm.executeUpdate();
请注意,我已将 PreparedStatement
包装在 try-with-resources statement 中,这是自 Java 7 以来的首选方式,因为它确保即使发生异常也能正确关闭 JDBC 资源。
如果 PostgreSQL JDBC 驱动程序不接受没有显式长度参数的pstm.setBinaryStream()
,则需要将用于创建输入流的File
对象的length()
传递给方法。另一种方法是在方法内read the stream fully into a byte array并使用
pstm.setBytes(3, myByteArray);
改为。
【讨论】:
【参考方案2】:对我有用的(Java 8 和 PG 9.4)是将 InputStream 转换为字节数组。
import com.google.common.io.ByteStreams;
// prepare your statement
byte [] binaries = null;
try
binaries = ByteStreams.toByteArray(inputStream);
catch (Exception e)
...
// bind your parameters
【讨论】:
ByteStreams 无法解决,我找不到快速解决此问题的方法。 这可能是指Guava的ByteStreams
,需要你导入并包含该库。
啊,对不起,我以为 ByteStreams 也直接来自 java,但是 import 语句说: import com.google.common.io.ByteStreams;我会调整我的答案。以上是关于关于在 FileInputStream 上使用 PreparedStatement.setBlob 到具有 Bytea 类型的 postgres 数据库的问题的主要内容,如果未能解决你的问题,请参考以下文章
java 关于FileReader和FileInputStream里的read()方法