如何用Java的Apache POI实现Excel图片适配单元格大小
解决Excel图片无法适配单元格的问题
你遇到的picture.resize无效,本质是锚点类型设置错误,且未正确计算单元格与图片的尺寸匹配关系。要让图片自动适配单元格,需要调整锚点类型并计算缩放比例,以下是修正方案:
问题分析
原代码中设置了ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE,这个类型会让图片固定位置和大小,完全不受单元格尺寸变化影响,自然无法适配单元格。另外,直接调用resize但未基于单元格实际尺寸计算缩放比例,也会导致调整无效。
修正步骤
- 修改锚点类型:使用
MOVE_AND_RESIZE,让图片随单元格移动并可调整大小。 - 计算单元格像素尺寸:POI中列宽和行高的单位不是直接像素,需要转换。
- 计算图片缩放比例:根据图片原始宽高和单元格可用区域,计算等比例缩放的比例,避免拉伸变形。
- 应用缩放:通过
Picture.resize或调整锚点的偏移量来设置图片大小。
修正后的代码
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import org.apache.poi.util.IOUtils; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public static void main(String[] args) throws IOException { Workbook workbook = new HSSFWorkbook(); Sheet sheet = workbook.createSheet("My Sheet2"); // 创建单元格并设置内容 Cell cell1 = sheet.createRow(0).createCell(0); Cell cell2 = sheet.createRow(1).createCell(0); cell1.setCellValue("this is Image1"); cell2.setCellValue("this is Image2"); // 读取图片 InputStream inputStream1 = new FileInputStream("src/main/resources/Image1.png"); InputStream inputStream2 = new FileInputStream("src/main/resources/Image2.png"); byte[] inputImage1 = IOUtils.toByteArray(inputStream1); byte[] inputImage2 = IOUtils.toByteArray(inputStream2); int inputImagePicture1 = workbook.addPicture(inputImage1, Workbook.PICTURE_TYPE_PNG); int inputImagePicture2 = workbook.addPicture(inputImage2, Workbook.PICTURE_TYPE_PNG); inputStream1.close(); inputStream2.close(); CreationHelper helper = workbook.getCreationHelper(); Drawing<?> drawing = sheet.createDrawingPatriarch(); // 设置单元格尺寸(单位转换:列宽1单位=1/256字符宽度,行高1单位=1/20点) int columnWidthInChars = 25; sheet.setColumnWidth(1, columnWidthInChars * 256); short rowHeightInPoints = 60; cell1.getRow().setHeight((short)(rowHeightInPoints * 20)); cell2.getRow().setHeight((short)(rowHeightInPoints * 20)); // 处理第一张图片 ClientAnchor anchor1 = helper.createClientAnchor(); // 设置锚点类型为随单元格移动并调整大小 anchor1.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); // 图片定位在第1列到第2列,第0行到第1行 anchor1.setCol1(1); anchor1.setCol2(2); anchor1.setRow1(0); anchor1.setRow2(1); // 图片边缘与单元格边缘留少量空白(可选) anchor1.setDx1(10); anchor1.setDy1(10); anchor1.setDx2(-10); anchor1.setDy2(-10); Picture picture1 = drawing.createPicture(anchor1, inputImagePicture1); // 获取图片原始尺寸 int imgWidth = picture1.getImageDimension().width; int imgHeight = picture1.getImageDimension().height; // 计算单元格的实际像素尺寸(列宽转换:1字符宽度≈8像素,可根据字体调整;行高转换:1点≈1.333像素) int cellWidth = columnWidthInChars * 8; int cellHeight = rowHeightInPoints * 3 / 2; // 计算等比例缩放比例 double scale = Math.min((double)cellWidth / imgWidth, (double)cellHeight / imgHeight); // 应用缩放 picture1.resize(scale); // 处理第二张图片,逻辑同上 ClientAnchor anchor2 = helper.createClientAnchor(); anchor2.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); anchor2.setCol1(1); anchor2.setCol2(2); anchor2.setRow1(1); anchor2.setRow2(2); anchor2.setDx1(10); anchor2.setDy1(10); anchor2.setDx2(-10); anchor2.setDy2(-10); Picture picture2 = drawing.createPicture(anchor2, inputImagePicture2); imgWidth = picture2.getImageDimension().width; imgHeight = picture2.getImageDimension().height; scale = Math.min((double)cellWidth / imgWidth, (double)cellHeight / imgHeight); picture2.resize(scale); // 自动调整第一列的宽度以适配文本 sheet.autoSizeColumn(0); try(FileOutputStream saveExcel = new FileOutputStream("target/newWorkbook.xls")) { workbook.write(saveExcel); } }
关键说明
- 锚点类型:
MOVE_AND_RESIZE是核心,确保图片与单元格绑定,单元格大小变化时图片也会跟着调整。 - 尺寸转换:POI的列宽和行高单位需要转换成像素才能和图片尺寸匹配,这里的转换值是通用近似值,若使用特殊字体可根据实际情况调整。
- 等比例缩放:通过
Math.min取宽高比例的最小值,保证图片完整显示且不变形。
内容的提问来源于stack exchange,提问作者Prabhanshu gupta
相关产品推荐
相关产品推荐

