我在我的应用中生成了一些非常大的.png
图像(例如,40000x10000像素)。为了避免过多的内存使用,我利用ImageIO
逐行写入的事实,因此我只需要在内存中保持40000像素(实际上我保持更多 - 100行,但仍然不是完整图片)。
之后,我使用以下代码将图像添加到POI工作簿:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "png", baos);
int pictureIdx = workbook.addPicture(baos.toByteArray, Workbook.PICTURE_TYPE_PNG);
CreationHelper helper = workbook.getCreationHelper();
Sheet sh = workbook.createSheet("Picture");
Drawing patriarch = sh.createDrawingPatriarch();
ClientAnchort anchor = helper.createClientAnchor();
anchor.setCol1(0);
anchor.setRow1(0);
Picture picture = patriarch.createPicture(anchor, pictureIdx);
picture.resize(); // here's the trouble :(
picture.resize()
电话是这里最大的问题。确定"首选"图像大小,它试图将整个图像加载到内存中 - 我们的示例图像在未压缩时需要大约1.6GB的内存。尝试在某些用户计算机上分配1.6GB内存会导致OOM异常。
如果我省略对picture.resize()
的调用,则图片不会显示在生成的xls中 - 它位于文件内部,根据大小判断,但在表格中不可见。
有没有办法跳过在内存中加载整个图像?也许我可以手动为图片提供首选图片尺寸?
答案 0 :(得分:1)
我找到了一种解决此问题的方法 - 基本上是通过从POI源复制代码并删除对getImageDimension()
的调用。请注意,此代码假定HSSF内部过多,并且可能在更新期间中断。
以下是我的解决方案(scala语法):
/** Applies resizing to HSSFPicture - without loading the whole image into memory. */
private def safeResize(pic: HSSFPicture, width: Double, height: Double, sheet: HSSFSheet) {
val anchor = pic.getAnchor.asInstanceOf[HSSFClientAnchor]
anchor.setAnchorType(2)
val pref: HSSFClientAnchor = {
val PX_DEFAULT = 32f
val PX_MODIFIED = 36.56f
val PX_ROW = 15
def getPixelWidth(column: Int): Float = {
val default = sheet.getDefaultColumnWidth*256
val cw = sheet.getColumnWidth(column)
if (default == cw) PX_DEFAULT else PX_MODIFIED
}
def getColumnWidthInPixels(column: Int): Float = {
val cw = sheet.getColumnWidth(column)
cw / getPixelWidth(column)
}
def getRowHeightInPixels(i: Int): Float = {
val row = sheet.getRow(i)
val height: Float = if (row != null) row.getHeight else sheet.getDefaultRowHeight
height / PX_ROW
}
var w = 0f
//space in the leftmost cell
w += getColumnWidthInPixels(anchor.getCol1)*(1 - anchor.getDx1.toFloat/1024)
var col2 = (anchor.getCol1 + 1).toShort
var dx2 = 0
while(w < width){
w += getColumnWidthInPixels(col2)
col2 = (col2 + 1).toShort
}
if (w > width) {
//calculate dx2, offset in the rightmost cell
col2 = (col2 - 1).toShort
val cw = getColumnWidthInPixels(col2)
val delta = w - width
dx2 = ((cw-delta)/cw*1024).toInt
}
anchor.setCol2(col2)
anchor.setDx2(dx2)
var h = 0f
h += (1 - anchor.getDy1.toFloat/256)* getRowHeightInPixels(anchor.getRow1)
var row2 = anchor.getRow1 + 1
var dy2 = 0
while (h < height){
h += getRowHeightInPixels(row2)
row2+=1
}
if(h > height) {
row2-=1
val ch = getRowHeightInPixels(row2)
val delta = h - height
dy2 = ((ch-delta)/ch*256).toInt
}
anchor.setRow2(row2)
anchor.setDy2(dy2)
anchor
}
val row2 = anchor.getRow1 + (pref.getRow2 - pref.getRow1)
val col2 = anchor.getCol1 + (pref.getCol2 - pref.getCol1)
anchor.setCol2(col2.toShort)
anchor.setDx1(0)
anchor.setDx2(pref.getDx2)
anchor.setRow2(row2)
anchor.setDy1(0)
anchor.setDy2(pref.getDy2)
}