关于在 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()方法

Java连载95-流的继承结构图FileInputStream举例

java.io.FileInputStream

FileInputStream

文件类练习题(FileInputStream类)

37使用FileInputStream和FileOutputStream读取和写入