【发布时间】:2016-05-07 17:43:27
【问题描述】:
我正在使用此代码从 PostgreSQL 下载 bytea 对象:
public void initFileDBData() throws SQLException, IOException
{
if (ds == null)
{
throw new SQLException("Can't get data source");
}
Connection conn = ds.getConnection();
if (conn == null)
{
throw new SQLException("Can't get database connection");
}
PreparedStatement ps = null;
try
{
conn.setAutoCommit(false);
ps = conn.prepareStatement("SELECT * FROM PROCEDURE_FILES WHERE ID = ?");
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
while (rs.next())
{
String file_name = rs.getString("FILE_NAME");
InputStream binaryStreasm = rs.getBinaryStream("FILE");
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset();
ec.setResponseContentLength(binaryStreasm.available());
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + file_name + "\"");
byte[] buf;
buf = new byte[binaryStreasm.available()];
int offset = 0;
int numRead = 0;
while ((offset < buf.length) && ((numRead = binaryStreasm.read(buf, offset, buf.length - offset)) >= 0))
{
offset += numRead;
}
HttpServletResponse response
= (HttpServletResponse) FacesContext.getCurrentInstance()
.getExternalContext().getResponse();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + file_name);
response.getOutputStream().write(buf);
response.getOutputStream().flush();
response.getOutputStream().close();
FacesContext.getCurrentInstance().responseComplete();
}
}
finally
{
if (ps != null)
{
ps.close();
}
conn.close();
}
}
但是当我开始下载代码时,我得到 java.lang.OutOfMemoryError: Java heap space on this line:
buf = new byte[binaryStreasm.available()];
我能以某种方式优化代码以减少内存消耗吗?
更新代码:
public void initFileDBData() throws SQLException, IOException
{
if (ds == null)
{
throw new SQLException("Can't get data source");
}
Connection conn = ds.getConnection();
if (conn == null)
{
throw new SQLException("Can't get database connection");
}
PreparedStatement ps = null;
try
{
conn.setAutoCommit(false);
ps = conn.prepareStatement("SELECT *, octet_length(FILE) as file_length FROM PROCEDURE_FILES WHERE ID = ?");
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
while (rs.next())
{
String file_name = rs.getString("FILE_NAME");
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset();
ec.setResponseContentLength(rs.getInt("file_length"));
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + file_name + "\"");
HttpServletResponse response
= (HttpServletResponse) FacesContext.getCurrentInstance()
.getExternalContext().getResponse();
byte[] buffer = new byte[4096];
try (InputStream input = rs.getBinaryStream("FILE");
OutputStream output = response.getOutputStream())
{
int numRead = 0;
while ((numRead = input.read(buffer)) != -1)
{
output.write(buffer, 0, numRead);
}
}
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + file_name);
response.getOutputStream().write(buffer);
response.getOutputStream().flush();
response.getOutputStream().close();
FacesContext.getCurrentInstance().responseComplete();
}
}
finally
{
if (ps != null)
{
ps.close();
}
conn.close();
}
}
【问题讨论】:
-
请注意,
available()不是流的实际大小。它只是可以在不阻塞的情况下读取的字节数的估计 - 它可以小于流的大小,甚至可以更大。 documentation 明确表示使用此方法的返回值来分配旨在保存此流中所有数据的缓冲区是不正确的。 -
好的,我该如何解决这个问题?
-
我还看到 ResultSet 对象 rs 没有关闭。这也可能是内存泄漏。
-
好的,我更新了代码。
-
如果抛出异常,您的结果集仍然不会关闭。假设您使用的是 Java 7 或更新版本,您应该使用 try-with-resources 块来确保
ResultSet在最后关闭。在旧版本中,使用 try/finally。
标签: java postgresql postgresql-9.3