blob.getBinaryStream独立关闭

blob.getBinaryStream independently closes

我有一个 java 方法,它从 oracle 数据库中获取 blob(我正在使用 ojdbc8.jar)。此方法将 blob 写入 OutputStream(对于客户端):

// OutputStream os = response.getOutputStream();  // earlier
// Blob blob = resultSet.getBlob("data_binary");  // earlier

    int length;
    int bufSize = 4096;
    byte buffer[] = new byte[bufSize];
    try (InputStream is = blob.getBinaryStream()) {
        while ((length = is.read(buffer, 0, bufSize)) != -1) {
            out.write(buffer, 0, length);
            out.flush();
        }
        out.flush();
    }

和returns InputStream(用于缓存):

return blob.getBinaryStream();

当我尝试(在另一个 class 上)将此 InputStream 写入磁盘时:

    try (OutputStream os = new FileOutputStream(fileOnTheDisk)) {
        IOUtils.copy(blobIS, os);
    } catch (IOException e) {
        e.printStackTrace();
    }

我遇到异常:

java.io.IOException: Closed Connection
at oracle.jdbc.driver.OracleBlobInputStream.needBytes(OracleBlobInputStream.java:204)
at oracle.jdbc.driver.OracleBufferedStream.readInternal(OracleBufferedStream.java:169)
at oracle.jdbc.driver.OracleBufferedStream.read(OracleBufferedStream.java:143)
at oracle.jdbc.driver.OracleBufferedStream.read(OracleBufferedStream.java:132)
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1025)
at org.apache.commons.io.IOUtils.copy(IOUtils.java:999)
at ru.miit.databasereader.OracleDatabaseReader.getbo(OracleDatabaseReader.java:262)
at ru.miit.contentservlet.ContentGetter.getObject(ContentGetter.java:85)
at ru.miit.contentservlet.ContentServlet.doGet(ContentServlet.java:131)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:409)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Caused by: java.sql.SQLRecoverableException: Closed Connection
at oracle.sql.BLOB.getDBAccess(BLOB.java:1122)
at oracle.sql.BLOB.getBytes(BLOB.java:348)
at oracle.jdbc.driver.OracleBlobInputStream.needBytes(OracleBlobInputStream.java:181)
... 31 more

我需要一个解决方案,如何将此 blob.getBinaryStream() 写入另一个 class 上的磁盘,并了解问题所在。

我试图用这种(获取)方法将这个 blob 流写入磁盘,但没有得到异常。只有当我 return 这个流到另一个 class 时,我才有例外。

已更新

完整代码。磁盘缓存:

public class DiskCache {

public void put(final String location, final String name, final InputStream blobIS, OutpuStream servletOS) {

    DatabaseReader databaseReader = new DatabaseReader ();

    BinaryObject binaryobject = databaseReader.getBinaryDataByImageId(imageId, servletOS);

    File fileOnTheDisk = new File(location + name);

    try (OutputStream os = new FileOutputStream(fileOnTheDisk)) {
        IOUtils.copy(binaryobject.getIS(), os);
    } catch (IOException e) {
        e.printStackTrace();
    }

}

数据库读取器:

public class DatabaseReader{

public Connection getConnection() throws NamingException, SQLException {

    Context initialContext = null;
    try {
        initialContext = new InitialContext();
        DataSource dataSource = (DataSource) initialContext.lookup(DATASOURCE_NAME);
        OracleConnection connection = (OracleConnection) dataSource.getConnection().unwrap(OracleConnection.class);
        return connection;
    } finally {
        if (initialContext != null){
            initialContext.close();
        }
    }

}
    public BinaryObject getBinaryDataByImageId(final String imageId, OutputStream osServlet) throws SQLException, NamingException, IOException{

     String sqlQuery = "***";

     BinaryObject binaryObject = null;
     try (Connection connection = getConnection();
            OraclePreparedStatement preparedStatement = (OraclePreparedStatement) connection
                    .prepareStatement(sqlQuery)) {
        try (ResultSet resultSet = preparedStatement.executeQuery()) {

            resultSet.next();
            Blob blob = resultSet.getBlob("blob_data");
            writeToStream(blob, osServlet); 
            binaryObject = new BinaryObject(imageId, blob.getBinaryStream());

        }
      }
      return binaryObject;
    }


    public void writeToStream(Blob blobData, OutputStream out) throws IOException, SQLException {

    int length;
    int bufSize = 4096;
    byte buffer[] = new byte[bufSize];
    try (InputStream is = blobData.getBinaryStream()) {
        while ((length = is.read(buffer, 0, bufSize)) != -1) {
            out.write(buffer, 0, length);
            out.flush();
        }
        out.flush();

    }

}
}

JNDI:

    <Resource name="jdbc/database" auth="Container"
    type="javax.sql.DataSource" maxActive="100" 
    maxIdle="30" maxWait="1000"
    username="***" 
    password="***"
    driverClassName="oracle.jdbc.OracleDriver"
    url="jdbc:oracle:thin***"/>
    BinaryObject binaryObject = null;
    try (ResultSet resultSet = preparedStatement.executeQuery()) {
        resultSet.next();
        Blob blob = resultSet.getBlob("data_binary");
        writeToStream(blob, osServlet); 
        binaryObject = new BinaryObject(imageId, blob.getBinaryStream());

    }
    return binaryObject;

通过在该临时文件上使用 try-with-resources 您的 ResultSet gets closed as soon as the scope was left, so trying to read from the BLOB's InputStream leads to an error. You're writing the data to some other stream anyway, so in case this is a temporary file somewhere, you might use a FileInputStream

一个 blob 应该在每个 ResultSet.next 步骤中只被读取一次。在结果集关闭(它应该关闭)之后,以及在读取 blob 的输入流之后(也关闭流),你就不走运了。

如果您想快速为客户端提供服务,并在缓存中存储副本,您需要同时执行这两项操作:

// OutputStream os = response.getOutputStream();  // earlier
// Blob blob = resultSet.getBlob("data_binary");  // earlier
// OutputStream cacheOut
final int bufSize = 4096;
byte[] buffer = new byte[bufSize];
int length;
try (InputStream is = blob.getBinaryStream()) {
    while ((length = is.read(buffer, 0, bufSize)) != -1) {
        out.write(buffer, 0, length);
        out.flush();
        cacheOut.write(buffer, 0, length);
    }
    out.flush();
    cacheOut.close();
}

其他任何事情都是内存密集型任务。