我有一个函数,可以使用FileInputStream
将大型XML文件转换为字节数组。它可以在我的IDE中正常运行,但是在通过可执行文件jar
独立运行时会抛出Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
。我正在读取字节数组中的大文件,以将其作为Blob存储在目标数据库中。我无法控制Blob的存储方式,我只能访问存储过程以插入Blob。有没有办法在不将整个文件加载到内存的情况下读写数据块?
将文件转换为字节数组的功能-
private byte[] getBytesFromFile(Path path) throws IOException {
FileInputStream fis = new FileInputStream(path.toFile());
byte[] bytes = new byte[(int) path.toFile().length()];
int read = 0;
int offset = 0;
while(offset < bytes.length && (read = fis.read(bytes, offset, bytes.length - offset)) >= 0 ){
offset += read;
}
fis.close();
return bytes;
}
这是使用存储过程调用将字节数组存储到db的代码
private void storeFileToDb(Connection connection, int fileId, String fileName, String fileType, byte[] fileBytes) throws SQLException {
//
String storedProcedure = "{call SP(?,?,?,?,?) }";
CallableStatement callableStatement = connection.prepareCall(storedProcedure);
callableStatement.setInt(1, fileId);
callableStatement.setString(2, fileName);
callableStatement.setString(3, fileType);
Blob fileBlob = connection.createBlob();
fileBlob.setBytes(1, fileBytes);
callableStatement.setBlob(4, fileBlob);
callableStatement.registerOutParameter(5, OracleTypes.NUMBER);
callableStatement.execute();
fileBlob.free(); // not entirely sure how this helps
//callableStatement.close();
}
答案 0 :(得分:1)
使用CallableStatement.setBlob(int, InputStream)
或Blob.setBinaryStream(long)
。两种方法都可以使用InputStream
或OutputStream
对象,并避免在内存中创建byte[]
数组。示例在Adding Large Object Type Object to Database docs中显示。
只要JDBC驱动程序足够聪明,不要在内部某个地方为整个blob创建byte[]
,这应该可以工作。
答案 1 :(得分:1)
服务器配置可能太严格。现在是检查内存参数的好时机。
仅提供 InputStream 即可填充Blob。
压缩XML数据也是一个好主意。尝试一下:将test.xml
压缩为test.xml.gz
,以增加大小。
请注意,标准Java中存在:
private byte[] getBytesFromFile(Path path) throws IOException {
return Files.readAllBytes(path);
}
所以:
private void storeFileToDb(Connection connection, int fileId, String fileName,
String fileType) throws SQLException, IOException {
Path path = Paths.get(fileName); // Or parameter
try (CallableStatement callableStatement = connection.prepareCall(storedProcedure);
GZipInputStream fileIn = new GZipInputStream(Files.newBufferedInputStream(path))) {
...
callableStatement.setBlob(4, fileIn);
...
}
}
try-with-resources确保在引发异常或返回等情况下关闭。对于声明也很有用。
您没有 close 语句,里面有一个Blob。这是不可取的,因为数据可能会停留一段时间。 CallableStatement也是PreparedStatement,其中一个用例正在使用可能的其他参数值重复执行SQL。是否。
并用于解压缩GZipOutputStream
。