近期拿到一个需求,要求将图片写入Excel,且等比例缩小或者放大,且上下左右居中。
因为历史原因,只能使用POI进行操作。遂去实践了一番。
POI操作Excel插入图片(以XSSF为例),一般是如下操作
1 public static void writeImageV1(XSSFSheet sheet,byte[] imageByte) { 2 XSSFDrawing drawing = sheet.createDrawingPatriarch(); 3 XSSFClientAnchor anchors = new XSSFClientAnchor(); 4 anchors.setCol1(1); 5 anchors.setCol2(13); 6 anchors.setRow1(1); 7 anchors.setRow2(13); 8 anchors.setAnchorType(AnchorType.MOVE_AND_RESIZE); 9 drawing.createPicture(anchors, sheet.getWorkbook().addPicture(imageByte, XSSFWorkbook.PICTURE_TYPE_JPEG)); 10 }
但得到的效果如下
图片自己被拉伸。这肯定不是客户想要的东西。所以,对于拉伸,有两种选择
1、自己根据锚点去调整图片的长度宽度
2、调用XSSFPicture.resize() 或者 XSSFPicture.resize(double scale);
先说第二种方式
1 public static void writeImageV2(XSSFSheet sheet,byte[] imageByte) { 2 XSSFDrawing drawing = sheet.createDrawingPatriarch(); 3 XSSFClientAnchor anchors = new XSSFClientAnchor(); 4 anchors.setCol1(1); 5 anchors.setCol2(13); 6 anchors.setRow1(1); 7 anchors.setRow2(13); 8 anchors.setAnchorType(AnchorType.MOVE_AND_RESIZE); 9 XSSFPicture pic = drawing.createPicture(anchors, sheet.getWorkbook().addPicture(imageByte, XSSFWorkbook.PICTURE_TYPE_JPEG)); 10 pic.resize(); 11 }
这个就刺激了,有兴趣可以自己去试试。
这时回到第一种方式。直接进行相对位置的调整。直接进行相对位置的调整。先尝试缩小100个单位。
1 public static void writeImageV3(XSSFSheet sheet,byte[] imageByte) { 2 XSSFDrawing drawing = sheet.createDrawingPatriarch(); 3 XSSFClientAnchor anchors = new XSSFClientAnchor(); 4 anchors.setCol1(1); 5 anchors.setCol2(13); 6 anchors.setRow1(1); 7 anchors.setRow2(13); 8 9 anchors.setDx1(Units.EMU_PER_PIXEL * 100); 10 anchors.setDx2(Units.EMU_PER_PIXEL *(-100)); 11 anchors.setDy1(Units.EMU_PER_PIXEL *(100)); 12 anchors.setDy2(Units.EMU_PER_PIXEL *(-100)); 13 anchors.setAnchorType(AnchorType.MOVE_AND_RESIZE); 14 drawing.createPicture(anchors, sheet.getWorkbook().addPicture(imageByte, XSSFWorkbook.PICTURE_TYPE_JPEG)); 15 }
效果如下
但是看上去并不是这么回事。左上角貌似恰好只移动了一个单元格。看API说明。
Within the first cell ??换句话说,只能在cell单元格里面进行调整??这tm不是捣乱吗?
等等,不对,原始图片左下角单元格不是(13,B) 吗?为何调整后的图片左下角却是(9,C)?如果是限制在单元格里面,为何此处可以跳出单元格???(有知道的朋友不妨留个言)
仅从目前得到的信息来看:调整图片似乎只能往左上的方向进行调整,而不能往右下进行调整。
那只能调整思路,切换锚点了。
1 public static void writeImageV4(XSSFSheet sheet,byte[] imageByte) { 2 XSSFDrawing drawing = sheet.createDrawingPatriarch(); 3 XSSFClientAnchor anchors = new XSSFClientAnchor(); 4 int col1 = 1,col2 = 13,row1 = 1,row2 = 13; //四个坐标 5 anchors.setCol1(col2); anchors.setCol2(col2); //锚点1 6 anchors.setRow1(row2); anchors.setRow2(row2); //锚点2 7 //计算区域的大小 8 XSSFRow row = sheet.createRow(row1); 9 double rowHeight = row.getHeightInPoints()/72*96; //行高,此处的值不精确 10 double colWidth = sheet.getColumnWidthInPixels(col1); //列宽(注意,可能不准) 11 logger.info("rowHeight {} colWidth {}",rowHeight,colWidth); 12 double collWidthSum = colWidth * (col2 - col1), rowHeightSum = rowHeight * (row2 - row1); //区域的实际大小 13 logger.info("collWidthSum {} rowHeightSum {}",collWidthSum,rowHeightSum); 14 InputStream bais = new ByteArrayInputStream(imageByte); //图片大小 15 int imgaeWidth = 0,imageHeight = 0; //图片宽高 16 try { 17 BufferedImage bufferImage = ImageIO.read(bais); 18 imgaeWidth = bufferImage.getWidth(); 19 imageHeight = bufferImage.getHeight(); 20 } catch (IOException e) { /** TODO 异常处理**/} 21 finally { /** TODO 关闭流 **/ } 22 logger.info("imgaeWidth {} imageHeight {}",imgaeWidth,imageHeight); 23 double scale = Math.min(collWidthSum/imgaeWidth, rowHeightSum/imageHeight); //计算缩放比例 24 logger.info("scale {}",scale); 25 double realImageWidth = imgaeWidth*scale,realImageHeight = imageHeight*scale; //处理后图片的大小 26 logger.info("realImageWidth {} realImageHeight {}",realImageWidth,realImageHeight); 27 double moveX = realImageWidth == collWidthSum ? 0 : (collWidthSum - realImageWidth)/2; //计算出需要图片需要移动的坐标 X轴 28 double moveY = realImageHeight == rowHeightSum ? 0 : (rowHeightSum - realImageHeight)/2; //计算出需要图片需要移动的坐标 Y轴 29 logger.info("moveX {} moveY {}",moveX,moveY); 30 anchors.setDx1(Units.EMU_PER_PIXEL *(int)(-realImageWidth - moveX)); 31 anchors.setDx2(Units.EMU_PER_PIXEL *(int)(-moveX)); 32 anchors.setDy1(Units.EMU_PER_PIXEL *(int)(-realImageHeight - moveY)); 33 anchors.setDy2(Units.EMU_PER_PIXEL *(int)(-moveY)); 34 anchors.setAnchorType(AnchorType.MOVE_AND_RESIZE); 35 drawing.createPicture(anchors, sheet.getWorkbook().addPicture(imageByte, XSSFWorkbook.PICTURE_TYPE_JPEG)); 36 }
效果如下
因为计算行高和列宽都不是精确值,所以,上下左右居中,会稍微有一点点偏移~
有兴趣的朋友可以自己尝试写一个更精确的。
java文件如下
https://files.cnblogs.com/files/brave-rocker/WriteImageToExcel.java.zip