Postgresql、JDBC和流式BLOBs

21

我正在尝试使用JDBC驱动程序从Postgres数据库检索二进制大对象(Blob)。它太大了,内存无法一次性加载,所以我想将其作为下载流式传输。我尝试在ResultSet上使用getBinaryStream方法,但事实证明该方法实际上会将其全部读入内存,因此无法处理大文件。

据说可以在结果集上使用getBlob方法,然后假定从Blob获取输入流并从那里继续处理,但这就是我遇到问题的地方。

PreparedStatement ps = con.prepareStatement("select data from file_data WHERE ID = ?");
ps.setLong(1,file.fileData.id)
ResultSet rs = ps.executeQuery()
if(rs.next()){
        rs.getBlob("data")

这是我正在运行的代码。当它执行到最后一行时,会抛出一个错误,但我无法理解...

  

org.postgresql.util.PSQLException:类型为long的值不正确:xxxxxx

“xxxxxx”是文件的内容。您可以想象它会很长,但这不是重点。

我在这里卡住了。有人对发生了什么事情有任何想法吗?该死,我甚至会采用将大型BLOB作为下载进行流式传输的替代方法。


在我的情况下,应该使用 rs.getBlob(...) 而不是 rs.getBinaryStream(...)。学习 JDBC 永远不会太晚。 - Boris Treukhov
3个回答

19

我的猜测是,你把OID和BYTEA样式的二进制大对象混淆了。在Postgres中,大型二进制对象使用OID列间接存储。实际的文件数据由Postgres存储在数据库表之外的某个地方。该列只包含与Blob关联的对象标识符。例如:

janko=# CREATE TABLE blobtest1 (name CHAR(30), image OID);
CREATE TABLE                                              
janko=# INSERT INTO blobtest1 VALUES ('stackoverflow', lo_import('/tmp/stackoverflow-logo.png'));
INSERT 0 1
janko=# SELECT * FROM blobtest1;
              name              | image
--------------------------------+-------
 stackoverflow                  | 16389
(1 row)

如果你使用ResultSet#getBlob(String)方法,那么期望的是一个OID样式的列。getBlob从该列读取数据并将其转换为Long,然后尝试从其内部存储中读取相关联的二进制数据。

另一方面,使用BYTEA可以直接将小片段的二进制数据放入数据库中。例如:

janko=# CREATE TABLE blobtest2 (name CHAR(30), image BYTEA);
CREATE TABLE
janko=# INSERT INTO blobtest2 VALUES ('somebinary', E'\\336\\255\\276\\357\\336\\255\\276\\357');
INSERT 0 1
janko=# SELECT * FROM blobtest2;
              name              |              image
--------------------------------+----------------------------------
 somebinary                     | \336\255\276\357\336\255\276\357
(1 row)

在这里,数据列直接包含了二进制数据。如果你尝试在这样的列上使用getBlob,数据仍将被解释为OID,但显然无法适应Long。让我们在我们刚创建的数据库上试试:

groovy:000> import java.sql.*
===> [import java.sql.*]
groovy:000> Class.forName("org.postgresql.Driver");
===> class org.postgresql.Driver
groovy:000> db = DriverManager.getConnection("jdbc:postgresql:janko", "janko", "qwertz");
===> org.postgresql.jdbc4.Jdbc4Connection@3a0b2c64
groovy:000> ps = db.prepareStatement("SELECT image FROM blobtest2 WHERE name = ?");
===> SELECT image FROM blobtest2 WHERE name = ?
groovy:000> ps.setString(1, "somebinary")
===> null
groovy:000> rs = ps.executeQuery()
===> org.postgresql.jdbc4.Jdbc4ResultSet@66f9104a
groovy:000> rs.next()
===> true
groovy:000> rs.getBlob("image")
ERROR org.postgresql.util.PSQLException: Bad value for type long : \336\255\276\357\336\255\276\357
        at org.postgresql.jdbc2.AbstractJdbc2ResultSet.toLong (AbstractJdbc2ResultSet.java:2796)
        at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getLong (AbstractJdbc2ResultSet.java:2019)
        at org.postgresql.jdbc4.Jdbc4ResultSet.getBlob (Jdbc4ResultSet.java:52)
        at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getBlob (AbstractJdbc2ResultSet.java:335)
        at groovysh_evaluate.run (groovysh_evaluate:3)
        ...

谢谢!所以rs.getBytes("image")对我来说可以获取字节数组 :-) - Bohne

8

-1
byte [] b = null;
while (m_ResultSet.next()) {
    for (int i = 1; i <= m_ResultSet.getMetaData().getColumnCount(); i++) {
        b =  m_ResultSet.getBytes(i);
    }
}
String str = "";
for (byte i : b){
    str+=(char)i;
}

2
请考虑添加代码的解释,以便说明其功能或与其他代码的不同之处。 - Studocwho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接