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文件:

相关推荐
xmh-sxh-131412 分钟前
jdk各个版本介绍
java
星星会笑滴20 分钟前
vue+node+Express+xlsx+emements-plus实现导入excel,并且将数据保存到数据库
vue.js·excel·express
天天扭码31 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶31 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺36 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序43 分钟前
vue3 封装request请求
java·前端·typescript·vue
陈王卜1 小时前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、1 小时前
Spring Boot 注解
java·spring boot
java亮小白19971 小时前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF1 小时前
java Queue 详解
java·队列