使用JDBC将大文件从Postgres数据库流式传输到文件系统

时间:2019-07-18 10:21:00

标签: java postgresql jdbc blob

我将文件存储在我的postgres数据库中的类型为bytea的列中,其大小可能超过分配的Java堆空间,因此当尝试将这些文件写入文件系统时,我很快会遇到内存不足的问题。

我正在使用JDBC执行查询,然后将内容提取为二进制流。

这是我的代码的简化版本:

    public File readContent(String contentId) {
        PreparedStatement statement = jdbcTemplate.getDataSource().getConnection().prepareStatement("SELECT content from table.entry WHERE id=?");
        statement.setString(1, contentId);
        ResultSet resultSet = statement.executeQuery();
        resultSet.next();
        File file = writeToFileSystem(resultSet.getBinaryStream(1));
        resultSet.close();
        return file;
    }


    private File writeToFileSystem(InputStream inputStream) {
        File dir = createDirectories(Paths.get(properties.getTempFolder(), UUID.randomUUID().toString())).toFile();
        File file = new File(dir, "content.zip");
        FileUtils.copyInputStreamToFile(inputStream, file);
        return file;
    }

我的期望是,这将使我能够将数据从数据库流式传输到文件中,而不必完全将其加载到内存中。但是这种方法行不通,因为执行查询后我仍然会得到OutOfMemoryErrors

Caused by: java.lang.OutOfMemoryError: Java heap space
    at org.postgresql.core.PGStream.receiveTupleV3(PGStream.java:395)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2118)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:288)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:430)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:356)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:168)
    at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:116)
    at sun.reflect.GeneratedMethodAccessor201.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114)
    at com.sun.proxy.$Proxy149.executeQuery(Unknown Source)
    at [...].ContentRepository.readContent(ContentRepository.java:111)  

有什么方法可以将数据库中的数据流传输到文件中,而不必增加Java VM的可用内存?

1 个答案:

答案 0 :(得分:0)

根据this mail group discussion,在这种使用情况下,您不应该使用bytea

  

有两种在pg中存储二进制数据的方法,它们具有不同的方法   访问方法和性能特征。 Bytea数据有望   较短,并且由服务器整体返回ResultSet。对于   您想要使用较大的数据,这些较大的对象将返回一个指针(oid)   您可以随意从服务器流式传输的实际数据。

     

此页面介绍了两者之间的一些区别   演示了使用pg特定的api访问大型对象,但是   getBlob / setBlob可以正常工作。

请参见Chapter 7. Storing Binary Data(其中显示了示例代码)和Chapter 35. Large Objects(其详细信息):

  

PostgreSQL具有大型对象工具,该工具提供对存储在特殊大型对象结构中的用户数据的流式访问。在处理太大而无法方便地整体操作的数据值时,流访问很有用。