目录
问题
实现思路
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 中的所有单元格
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 文件
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
<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
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,获取sheetWorkbook 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) {// xlsif (sheet instanceof HSSFSheet) {Workbook workbook = sheet.getWorkbook();return ((HSSFCell) cell).getCellStyle().getFont(workbook);}// xlsxreturn ((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
class Excel2PDFUtilTest {@Testvoid excelToPdf() {String excelPath = "C:\\Users\\XXXX\\Desktop\\excel.xlsx";String pdfPath = "C:\\Users\\XXXX\\Desktop\\pdf.pdf";Excel2PDFUtil.excelToPdf(excelPath, pdfPath, ".xlsx");}
}
输出结果
excel文件:
pdf文件: