Java 基于 poi 和 itextpdf 实现 excel 转 pdf

目录

问题

实现思路

pom

Excel2PDFUtil

Excel2PDFUtilTest

输出结果


问题

工作中遇到一个需求,需要实现 excel 文件的预览,实现的思路就是将 excel 转成 pdf 文件,上传到文件服务器上得到文件地址,预览时只需要返回 pdf 预览文件地址即可。

所以主要的问题就是如何将 excel 转为 pdf,调研了相关的技术,最简单的就是使用 aspose 类库,直接一个api调用就搞定了。但是 aspose 并不是免费的,虽然网上有破解,但是为了规避版权风险,最好还是不用了。

可以使用 itextpdf 来平替 aspose 实现 excel 文件转换。由于需求中的 excel 就只是很常规的文本表格,使用 itextpdf 来创建表格,依次从 excel 获取每行每列的单元格依次填入即可将 excel 中的内容搬到新建的 pdf 中,从而实现 excel 到 pdf 的转换。

实现思路

poi 读取 excel 内容,得到 excel 文件中每一张 sheet,每张 sheet 中的所有单元格

java 复制代码
InputStream in = Files.newInputStream(Paths.get(excelPath);
//workbook
Workbook workbook = excelSuffix.endsWith(".xlsx") ? new XSSFWorkbook(in) : new HSSFWorkbook(in);

//获取所有表格
Iterator<Sheet> it = workbook.sheetIterator()
Sheet sheet = it.next();

//获取单元格
Row row = sheet.getRow(rowIndex);
Cell cell = row.getCell(columnIndex);

//获取单元格值
CellType cellType = excelCell.getCellType();
String value = excelCell.getStringCellValue();

itextpdf 创建pdf,将 excel 单元格转换成 pdf 单元格,最后写入 pdf 文件

java 复制代码
OutputStream out = Files.newOutputStream(Paths.get(pdfPath);
//创建 pdf 文档
Document document = new Document(new RectangleReadOnly(842.0F, 595.0F));
        PdfWriter.getInstance(document, outStream);

document.open();

//创建 pdf 表格
PdfPTable table = new PdfPTable();

//船舰 pdf 单元格
PdfPCell cell = new PdfPCell();

//添加单元格到表格
table.add(cell);

//新建一页
document.newPage();

//将表格写入文档
document.add(table);

document.close();

pom

XML 复制代码
    <properties>
        <poi.version>5.2.5</poi.version>
        <itextpdf.version>5.5.13.2</itextpdf.version>
    </properties>
     
    <dependencies>
         <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>${poi.version}</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>${itextpdf.version}</version>
        </dependency>
    </dependencies>

Excel2PDFUtil

java 复制代码
public class Excel2PDFUtil {

    public static void excelToPdf(String excelPath, String pdfPath, String excelSuffix) {
        try (InputStream in = Files.newInputStream(Paths.get(excelPath));
             OutputStream out = Files.newOutputStream(Paths.get(pdfPath))) {
            excelToPdf(in, out, excelSuffix);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Excel转PDF并写入输出流
     *
     * @param inStream    Excel输入流
     * @param outStream   PDF输出流
     * @param excelSuffix Excel类型 .xls 和 .xlsx
     * @throws Exception 异常信息
     */
    public static void excelToPdf(InputStream inStream, OutputStream outStream, String excelSuffix) throws Exception {
        //设置pdf纸张大小 PageSize.A4 A4横向
        Document document = new Document(new RectangleReadOnly(842.0F, 595.0F));
        PdfWriter.getInstance(document, outStream);
        //设置页边距 宽
        document.setMargins(10, 10, 10, 10);
        document.open();
        // 输入流转workbook,获取sheet
        Workbook workbook = excelSuffix.endsWith(".xlsx") ? new XSSFWorkbook(inStream) : new HSSFWorkbook(inStream);
        for (Iterator<Sheet> it = workbook.sheetIterator(); it.hasNext(); ) {
            Sheet sheet = it.next();
            // 获取列宽度占比
            float[] widths = getColWidth(sheet);
            PdfPTable table = new PdfPTable(widths);
            table.setWidthPercentage(100);
            int colCount = widths.length;
            // 遍历行
            for (int rowIndex = sheet.getFirstRowNum(); rowIndex <= sheet.getLastRowNum(); rowIndex++) {
                Row row = sheet.getRow(rowIndex);
                if (Objects.isNull(row)) {
                    // 插入空对象
                    for (int i = 0; i < colCount; i++) {
                        table.addCell(createPdfPCell(null, 0, 13f, null));
                    }
                } else {
                    // 遍历单元格
                    for (int columnIndex = 0; (columnIndex < row.getLastCellNum() || columnIndex < colCount) && columnIndex > -1; columnIndex++) {
                        PdfPCell pCell = excelCellToPdfCell(sheet, row.getCell(columnIndex));
                        //处理合并单元格
                        dealMergedRegion(sheet, rowIndex, columnIndex, pCell);

                        table.addCell(pCell);
                    }
                }
            }

            document.newPage();
            document.add(table);
        }
        document.close();
    }

    /**
     * 单元格转换,poi cell 转换为 itext cell
     *
     * @param sheet  poi sheet页
     * @param excelCell poi 单元格
     * @return com.itextpdf.text.pdf.PdfPCell
     */
    private static PdfPCell excelCellToPdfCell(Sheet sheet, Cell excelCell) {
        if (Objects.isNull(excelCell)) {
            return createPdfPCell(null, 0, 13f, null);
        }

        org.apache.poi.ss.usermodel.Font excelFont = getExcelFont(sheet, excelCell);
        //设置单元格字体
        com.itextpdf.text.Font pdFont = new com.itextpdf.text.Font();
        pdFont.setSize(excelFont.getFontHeightInPoints());
        pdFont.setStyle(excelFont.getBold() ? 1 : 0);
        pdFont.setColor(BaseColor.BLACK);
        Integer border = hasBorder(excelCell) ? null : 0;
        String excelCellValue = getExcelCellValue(excelCell);
        PdfPCell pCell = createPdfPCell(excelCellValue, border, excelCell.getRow().getHeightInPoints(), pdFont);
        // 水平居中
        pCell.setHorizontalAlignment(getHorAlign(excelCell.getCellStyle().getAlignment().getCode()));
        // 垂直对齐
        pCell.setVerticalAlignment(getVerAlign(excelCell.getCellStyle().getVerticalAlignment().getCode()));
        return pCell;
    }

    /**
     * 创建itext pdf 单元格
     *
     * @param content 单元格内容
     * @param border 边框
     * @param minimumHeight 高度
     * @param pdFont 字体
     * @return pdf cell
     */
    private static PdfPCell createPdfPCell(String content, Integer border, Float minimumHeight, com.itextpdf.text.Font pdFont) {
        String contentValue = content == null ? "" : content;
        com.itextpdf.text.Font pdFontNew = pdFont == null ? new com.itextpdf.text.Font() : pdFont;
        PdfPCell pCell = new PdfPCell(new Phrase(contentValue, pdFontNew));
        if (Objects.nonNull(border)) {
            pCell.setBorder(border);
        }
        if (Objects.nonNull(minimumHeight)) {
            pCell.setMinimumHeight(minimumHeight);
        }

        return pCell;
    }

    /**
     * excel垂直对齐方式映射到pdf对齐方式
     */
    private static int getVerAlign(int align) {
        switch (align) {
            case 2:
                return com.itextpdf.text.Element.ALIGN_BOTTOM;
            case 3:
                return com.itextpdf.text.Element.ALIGN_TOP;
            default:
                return com.itextpdf.text.Element.ALIGN_MIDDLE;
        }
    }

    /**
     * excel水平对齐方式映射到pdf水平对齐方式
     */
    private static int getHorAlign(int align) {
        switch (align) {
            case 1:
                return com.itextpdf.text.Element.ALIGN_LEFT;
            case 3:
                return com.itextpdf.text.Element.ALIGN_RIGHT;
            default:
                return com.itextpdf.text.Element.ALIGN_CENTER;
        }
    }

    /*============================================== POI获取图片及文本内容工具方法 ==============================================*/

    /**
     * 获取字体
     *
     * @param sheet excel 转换的sheet页
     * @param cell 单元格
     * @return 字体
     */
    private static org.apache.poi.ss.usermodel.Font getExcelFont(Sheet sheet, Cell cell) {
        // xls
        if (sheet instanceof HSSFSheet) {
            Workbook workbook = sheet.getWorkbook();
            return ((HSSFCell) cell).getCellStyle().getFont(workbook);
        }
        // xlsx
        return ((XSSFCell) cell).getCellStyle().getFont();
    }

    /**
     * 判断excel单元格是否有边框
     */
    private static boolean hasBorder(Cell excelCell) {
        short top = excelCell.getCellStyle().getBorderTop().getCode();
        short bottom = excelCell.getCellStyle().getBorderBottom().getCode();
        short left = excelCell.getCellStyle().getBorderLeft().getCode();
        short right = excelCell.getCellStyle().getBorderRight().getCode();
        return top + bottom + left + right > 2;
    }

    /**
     * 处理合并单元格
     *
     * @param sheet 表格
     * @param row 当前行
     * @param column 当前列
     * @param pCell pdf单元格
     */
    private static void dealMergedRegion(Sheet sheet, int row, int column, PdfPCell pCell) {
        int sheetMergeCount = sheet.getNumMergedRegions();
        for (int i = 0; i < sheetMergeCount; i++) {
            CellRangeAddress range = sheet.getMergedRegion(i);
            int firstColumn = range.getFirstColumn();
            int lastColumn = range.getLastColumn();
            int firstRow = range.getFirstRow();
            int lastRow = range.getLastRow();
            if (firstColumn == column && firstRow == row) {
                pCell.setRowspan(lastRow - firstRow + 1);
                pCell.setColspan(lastColumn - firstColumn + 1);
                break;
            }
        }
    }

    /**
     * 获取excel中每列宽度的占比
     */
    private static float[] getColWidth(Sheet sheet) {
        int rowNum = getMaxColRowNum(sheet);
        Row row = sheet.getRow(rowNum);
        int cellCount = row.getPhysicalNumberOfCells();
        int[] colWidths = new int[cellCount];
        int sum = 0;

        for (int i = row.getFirstCellNum(); i < cellCount; i++) {
            Cell cell = row.getCell(i);
            if (cell != null) {
                colWidths[i] = sheet.getColumnWidth(i);
                sum += sheet.getColumnWidth(i);
            }
        }

        float[] colWidthPer = new float[cellCount];
        for (int i = row.getFirstCellNum(); i < cellCount; i++) {
            colWidthPer[i] = (float) colWidths[i] / sum * 100;
        }
        return colWidthPer;
    }

    /**
     * 获取excel中列数最多的行号
     */
    private static int getMaxColRowNum(Sheet sheet) {
        int rowNum = 0;
        int maxCol = 0;
        for (int r = sheet.getFirstRowNum(); r < sheet.getPhysicalNumberOfRows(); r++) {
            Row row = sheet.getRow(r);
            if (row != null && maxCol < row.getPhysicalNumberOfCells()) {
                maxCol = row.getPhysicalNumberOfCells();
                rowNum = r;
            }
        }
        return rowNum;
    }

    /**
     * poi 根据单元格类型获取单元格内容
     *
     * @param excelCell poi单元格
     * @return 单元格内容文本
     */
    public static String getExcelCellValue(Cell excelCell) {
        if (excelCell == null) {
            return "";
        }
        // 判断数据的类型
        CellType cellType = excelCell.getCellType();

        if (cellType == CellType.STRING) {
            return excelCell.getStringCellValue();
        }
        if (cellType == CellType.BOOLEAN) {
            return String.valueOf(excelCell.getBooleanCellValue());
        }
        if (cellType == CellType.FORMULA) {
            return excelCell.getCellFormula();
        }
        if (cellType == CellType.NUMERIC) {
            //short s = excelCell.getCellStyle().getDataFormat();
            if (DateUtil.isCellDateFormatted(excelCell)) {// 处理日期格式、时间格式
                SimpleDateFormat sdf;
                // 验证short值
                if (excelCell.getCellStyle().getDataFormat() == 14) {
                    sdf = new SimpleDateFormat("yyyy/MM/dd");
                } else if (excelCell.getCellStyle().getDataFormat() == 21) {
                    sdf = new SimpleDateFormat("HH:mm:ss");
                } else if (excelCell.getCellStyle().getDataFormat() == 22) {
                    sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                } else {
                    throw new RuntimeException("日期格式错误!!!");
                }
                Date date = excelCell.getDateCellValue();
                return sdf.format(date);
            } else if (excelCell.getCellStyle().getDataFormat() == 0) {
                //处理数值格式
                DataFormatter formatter = new DataFormatter();
                return formatter.formatCellValue(excelCell);
            }
        }
        if (cellType == CellType.ERROR) {
            return "非法字符";
        }
        return "";
    }

}

Excel2PDFUtilTest

java 复制代码
class Excel2PDFUtilTest {

    @Test
    void excelToPdf() {
        String excelPath = "C:\\Users\\XXXX\\Desktop\\excel.xlsx";
        String pdfPath = "C:\\Users\\XXXX\\Desktop\\pdf.pdf";

        Excel2PDFUtil.excelToPdf(excelPath, pdfPath, ".xlsx");
    }
}

输出结果

excel文件:

pdf文件:

相关推荐
BLOB_1010016 分钟前
【折腾一上午】Java POI 导出 Excel 自适应列宽行高
java·excel
昔舍10 分钟前
C#实现word和pdf格式互转
pdf·word
布值倒区什么name17 分钟前
日常记录,使用springboot,vue2,easyexcel使实现字段的匹配导入
java·spring boot·后端
你不讲 wood17 分钟前
预览 PDF 文档
开发语言·前端·javascript·pdf·html·node·文件预览
single59420 分钟前
【综合算法学习】(第十篇)
java·数据结构·c++·vscode·学习·算法·leetcode
wclass-zhengge23 分钟前
SpringBoot篇(自动装配原理)
java·spring boot·后端
DC妙妙屋27 分钟前
10.24.2024刷华为OD C题型(四) -- 对象list按照多个属性排序
1024程序员节
哎呦没30 分钟前
中小企业设备管理效率提升:Spring Boot系统设计
java·spring boot·后端
ZWZhangYu33 分钟前
【MyBatis源码】SqlSource对象创建流程
java·tomcat·mybatis
Yaml435 分钟前
Spring Boot整合EasyExcel:实现大规模数据的并行导出与压缩下载
java·开发语言·spring boot