You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Apache POI与Java实现表格数据导出至Excel并嵌入对应行PDF/Word附件的技术方案咨询

嘿,这个需求我之前刚好折腾过,用Apache POI把PDF/Word附件嵌入到Excel对应单元格其实没那么复杂,我给你拆解成实操步骤和代码示例,你可以直接参考着改:

核心逻辑说明

Apache POI里嵌入附件本质是创建OLE对象,并通过锚点关联到指定单元格位置。不同类型的文件(Word/PDF)需要对应不同的CLSID(类标识符),这样Excel才能识别并显示正确的图标和打开方式。

具体实现步骤

1. 准备依赖

首先确保你的项目里引入了Apache POI的必要依赖(以Maven为例),Gradle项目对应调整即可:

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version> <!-- 建议用4.x或5.x稳定版,版本要统一 -->
</dependency>

2. 封装嵌入附件的通用工具方法

我写了一个通用方法,可以直接复用,传入工作表、附件路径、目标行列号和显示的图标名称就行:

import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.xssf.usermodel.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class ExcelAttachmentHelper {

    /**
     * 将附件嵌入到Excel指定单元格
     * @param sheet 目标工作表
     * @param filePath 附件的本地路径
     * @param rowIndex 目标行号(从0开始计数)
     * @param colIndex 目标列号(从0开始计数)
     * @param displayName 单元格内显示的图标名称
     * @throws IOException 读取文件或写入Excel时的IO异常
     */
    public static void embedFileToCell(XSSFSheet sheet, String filePath, int rowIndex, int colIndex, String displayName) throws IOException {
        XSSFWorkbook workbook = sheet.getWorkbook();
        // 创建绘图容器,用于承载OLE对象
        XSSFDrawing drawing = sheet.createDrawingPatriarch();
        
        // 设置锚点:让附件图标刚好覆盖目标单元格,支持随单元格移动和调整大小
        ClientAnchor anchor = workbook.getCreationHelper().createClientAnchor();
        anchor.setCol1(colIndex);
        anchor.setRow1(rowIndex);
        anchor.setCol2(colIndex + 1);
        anchor.setRow2(rowIndex + 1);
        anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
        
        // 读取附件文件为字节数组(嵌入到Excel内部,不是外部链接)
        File attachmentFile = new File(filePath);
        byte[] fileBytes = new byte[(int) attachmentFile.length()];
        try (FileInputStream fis = new FileInputStream(attachmentFile)) {
            fis.read(fileBytes);
        }
        
        // 创建OLE对象并关联到锚点
        XSSFObjectData oleObject = drawing.createObjectData(anchor);
        oleObject.setData(fileBytes);
        
        // 根据文件类型设置对应的CLSID和显示信息
        String fileName = attachmentFile.getName();
        if (fileName.endsWith(".docx") || fileName.endsWith(".doc")) {
            // Word文档的标准CLSID
            oleObject.setClsid("{00020906-0000-0000-C000-000000000046}");
            oleObject.setFileName(displayName);
            oleObject.setDescription("Word文档");
        } else if (fileName.endsWith(".pdf")) {
            // Adobe PDF的标准CLSID
            oleObject.setClsid("{B801CA65-A1FC-11D0-85AD-444553540000}");
            oleObject.setFileName(displayName);
            oleObject.setDescription("PDF文档");
        }
        // 要是需要支持其他类型,比如Excel、PPT,查对应的CLSID加进来就行
    }
}

3. 关联到你的表格导出逻辑里

你已经实现了表格数据写入Excel的部分,只需要在循环每一行的时候,调用上面的工具方法即可:

// 假设你已经创建了工作簿和工作表
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet dataSheet = workbook.createSheet("业务数据");

// 模拟你的表格数据列表(替换成你实际的数据集合)
List<BusinessData> dataList = fetchYourTableData();
for (int rowNum = 0; rowNum < dataList.size(); rowNum++) {
    BusinessData data = dataList.get(rowNum);
    XSSFRow row = dataSheet.createRow(rowNum + 1); // 第0行留作表头
    
    // 你已经实现的:写入表格数据到单元格
    row.createCell(0).setCellValue(data.getRecordId());
    row.createCell(1).setCellValue(data.getRecordName());
    
    // 新添加的:嵌入附件到第3列(索引为2)
    String attachmentPath = data.getAttachmentFilePath();
    if (attachmentPath != null && !attachmentPath.trim().isEmpty()) {
        try {
            ExcelAttachmentHelper.embedFileToCell(dataSheet, attachmentPath, rowNum + 1, 2, data.getAttachmentFileName());
        } catch (IOException e) {
            // 这里可以加异常处理,比如在单元格标记"嵌入失败"
            row.createCell(2).setCellValue("附件嵌入失败");
            e.printStackTrace();
        }
    }
}

// 最后导出Excel文件
try (FileOutputStream fos = new FileOutputStream("业务数据_带附件.xlsx")) {
    workbook.write(fos);
}
几个关键注意事项
  • CLSID正确性:如果发现附件图标显示不对或者无法打开,大概率是CLSID错了,可以自己查对应文件类型的官方CLSID替换。
  • 版本兼容性:如果用POI 5.x版本,部分API可能有调整,比如createObjectData的参数,需要对照官方文档调整。
  • 单元格锚点:如果想调整图标大小,可以修改锚点的dx1dy1等参数,默认是占满整个单元格。
  • 文件权限:确保程序有读取附件文件的权限,否则会抛出IO异常。

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

火山引擎 最新活动