You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Apache POI 5.4.1:如何在XSSFWorkbook中添加原始尺寸图片

解决Apache POI 5.4.1 XSSF图片缩放与resize(1.0)不显示问题

核心问题原因

Excel默认采用96DPI渲染图片,而多数图片的原生DPI是72(比如PNG/JPG常见值)。直接调用resize(1.0)时,POI会基于图片DPI和Excel的DPI做转换,导致计算出的尺寸异常,甚至图片被缩至不可见。另外,锚点设置不当也会让图片被单元格约束缩放。

正确实现步骤

1. 获取图片原生尺寸与DPI

先读取图片的真实宽度、高度和DPI值,避免依赖POI的自动计算:

// 示例:读取本地图片文件
File imageFile = new File("test.png");
BufferedImage img = ImageIO.read(imageFile);
int imgWidth = img.getWidth();
int imgHeight = img.getHeight();

// 获取图片DPI(如果图片没有DPI元数据,默认用72)
float dpiX = 72f;
float dpiY = 72f;
ImageReader reader = ImageIO.getImageReadersByFormatName("png").next();
try (ImageInputStream iis = ImageIO.createImageInputStream(imageFile)) {
    reader.setInput(iis, true);
    IIOMetadata metadata = reader.getImageMetadata(0);
    NodeList nodes = metadata.getAsTree("javax_imageio_1.0").getElementsByTagName("HorizontalPixelSize");
    if (nodes.getLength() > 0) {
        String value = nodes.item(0).getAttributes().getNamedItem("value").getNodeValue();
        dpiX = 25.4f / Float.parseFloat(value); // 转换为DPI(1英寸=25.4毫米)
    }
    nodes = metadata.getAsTree("javax_imageio_1.0").getElementsByTagName("VerticalPixelSize");
    if (nodes.getLength() > 0) {
        String value = nodes.item(0).getAttributes().getNamedItem("value").getNodeValue();
        dpiY = 25.4f / Float.parseFloat(value);
    }
} catch (Exception e) {
    // 读取DPI失败时使用默认值
}

2. 计算适配Excel的尺寸

基于Excel的96DPI,将图片原生尺寸转换为Excel可识别的显示尺寸:

float excelDpi = 96f;
// 转换图片像素为Excel渲染时的实际像素值
int displayWidth = (int) Math.round(imgWidth * excelDpi / dpiX);
int displayHeight = (int) Math.round(imgHeight * excelDpi / dpiY);

3. 设置锚点并插入图片

使用ClientAnchor定义图片的占位范围,然后手动设置图片尺寸替代resize(1.0)

XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("ImageSheet");

// 读取图片到字节数组
byte[] imageBytes = Files.readAllBytes(imageFile.toPath());
int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG);
XSSFDrawing drawing = sheet.createDrawingPatriarch();
// 锚点设置为足够容纳图片的单元格范围,col1/row1是起始单元格,col2/row2是结束单元格
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 5, 10);

// 创建图片并手动设置尺寸
XSSFPicture picture = drawing.createPicture(anchor, pictureIdx);
picture.setWidth(displayWidth);
picture.setHeight(displayHeight);

// 写入文件
try (FileOutputStream fos = new FileOutputStream("output.xlsx")) {
    workbook.write(fos);
}

4. 简化方案(已知图片DPI为96时)

如果你的图片原生DPI就是96,可以直接跳过DPI计算,直接设置原始像素尺寸:

XSSFPicture picture = drawing.createPicture(anchor, pictureIdx);
picture.setWidth(imgWidth);
picture.setHeight(imgHeight);

关键注意点

  • 避免直接调用resize(1.0),该方法的DPI转换逻辑容易导致尺寸计算偏差。
  • 锚点的col2/row2只需设置到能容纳图片的单元格位置即可,实际显示尺寸由setWidthsetHeight控制。
  • 若图片无DPI元数据,默认用72计算可覆盖绝大多数常见图片场景。

内容的提问来源于stack exchange,提问作者squirrelInTheBarel

火山引擎 最新活动