需求:订单数据生成PDF文件
方案:思路是通过本地的Excle模版和订单数据生成PDF文件。
先把数据填入到Excle模版中,然后通过Excle转为PDF文件,输出PDF文件。
Excle模版内容如下:
生成PDF所需要的依赖如下:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<poi.version>3.17</poi.version>
</properties>
<!--excel转pdf依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>net.sf.jxls</groupId>
<artifactId>jxls-core</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>${poi.version}</version>
</dependency>
写一个工具类FileUtils,如下:
package com.robinboot.service.utils;
import com.alibaba.fastjson.JSON;
import com.itextpdf.text.*;
import com.itextpdf.text.Font;
import com.itextpdf.text.pdf.*;
import com.robinboot.dto.TruckBrokerOrderDto;
import com.robinboot.utils.ServiceException;
import net.sf.jxls.transformer.XLSTransformer;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* @author TF12778
* @Date 2020/3/27
*/
public class FileUtils {
private static final String TEMP_PATH = "/httx/run/temp/";
public static byte[] excelToPdf(Map<String, Object> exportParam,
String templatePath, boolean horizontal) throws Exception {
InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(templatePath);
XLSTransformer transformer = new XLSTransformer();
Workbook workbook = transformer.transformXLS(in, exportParam);
Sheet sheet = workbook.getSheetAt(0);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Document document = new Document();
if (horizontal) {
Rectangle pageSize = new Rectangle(PageSize.A4.getHeight(), PageSize.A4.getWidth());
pageSize.rotate();
document.setPageSize(pageSize);
}
PdfWriter writer = PdfWriter.getInstance(document, stream);
// document.setMargins(0, 0, 15, 15);
document.open();
float[] widths = getColWidth(sheet);
PdfPTable table = new PdfPTable(widths);
table.setWidthPercentage(100);
int colCount = widths.length;
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
BaseFont.NOT_EMBEDDED);
for (int r = sheet.getFirstRowNum(); r < sheet.getPhysicalNumberOfRows(); r++) {
Row row = sheet.getRow(r);
if (row != null) {
for (int c = row.getFirstCellNum(); (c < row.getLastCellNum() || c < colCount) && c > -1; c++) {
if (c >= row.getPhysicalNumberOfCells()) {
PdfPCell pCell = new PdfPCell(new Phrase(""));
pCell.setBorder(0);
table.addCell(pCell);
continue;
}
Cell excelCell = row.getCell(c);
String value = "";
if (excelCell != null) {
value = excelCell.toString().trim();
if (value != null && value.length() != 0) {
//获取excel单元格数据显示样式
String dataFormat = excelCell.getCellStyle().getDataFormatString();
if (dataFormat != "General" && dataFormat != "@") {
try {
String numStyle = getNumStyle(dataFormat);
value = numFormat(numStyle, excelCell.getNumericCellValue());
} catch (Exception e) {
}
}
}
}
org.apache.poi.ss.usermodel.Font excelFont = getExcelFont(excelCell);
Font pdFont = new Font(baseFont, excelFont.getFontHeightInPoints(), Font.NORMAL,
BaseColor.BLACK);
PdfPCell pCell = new PdfPCell(new Phrase(value, pdFont));
boolean hasBorder = hasBorder(excelCell);
if (!hasBorder) {
pCell.setBorder(0);
}
pCell.setHorizontalAlignment(getHorAglin(excelCell.getCellStyle().getAlignment()));
pCell.setVerticalAlignment(getVerAglin(excelCell.getCellStyle().getVerticalAlignment()));
pCell.setUseAscender(true);
pCell.setMinimumHeight(row.getHeightInPoints());
if (isMergedRegion(sheet, r, c)) {
int[] span = getMergedSpan(sheet, r, c);
//忽略合并过的单元格
if (span[0] == 1 && span[1] == 1) {
continue;
}
pCell.setRowspan(span[0]);
pCell.setColspan(span[1]);
//合并过的列直接跳过
c = c + span[1] - 1;
}
table.addCell(pCell);
}
} else {
PdfPCell pCell = new PdfPCell(new Phrase(""));
pCell.setBorder(0);
pCell.setMinimumHeight(13);
table.addCell(pCell);
}
}
document.add(table);
document.close();
writer.close();
byte[] pdfByte = stream.toByteArray();
stream.flush();
stream.reset();
stream.close();
return pdfByte;
}
/**
* * 获取excel中每列宽度的占比
* * @param sheet
* * @return
*/
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;
}
/**
* 获取字体
*
* @param cell
* @return
*/
private static org.apache.poi.ss.usermodel.Font getExcelFont(Cell cell) {
return ((XSSFCell) cell).getCellStyle().getFont();
}
/**
* 判断excel单元格是否有边框
*
* @param excelCell
* @return
*/
private static boolean hasBorder(Cell excelCell) {
BorderStyle top = excelCell.getCellStyle().getBorderTopEnum();
BorderStyle bottom = excelCell.getCellStyle().getBorderBottomEnum();
BorderStyle left = excelCell.getCellStyle().getBorderLeftEnum();
BorderStyle right = excelCell.getCellStyle().getBorderRightEnum();
return top.getCode() + bottom.getCode() + left.getCode() + right.getCode() > 2;
}
/**
* 获取excel单元格数据显示格式
*
* @param dataFormat
* @return
* @throws Exception
*/
private static String getNumStyle(String dataFormat) throws Exception {
if (dataFormat == null || dataFormat.length() == 0) {
throw new Exception("");
}
if (dataFormat.indexOf("%") > -1) {
return dataFormat;
} else {
return dataFormat.substring(0, dataFormat.length() - 2);
}
}
/**
* 判断单元格是否是合并单元格
*
* @param sheet
* @param row
* @param column
* @return
*/
private static boolean isMergedRegion(Sheet sheet, int row, int column) {
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 (row >= firstRow && row <= lastRow) {
if (column >= firstColumn && column <= lastColumn) {
return true;
}
}
}
return false;
}
/**
* 计算合并单元格合并的跨行跨列数
*
* @param sheet
* @param row
* @param column
* @return
*/
private static int[] getMergedSpan(Sheet sheet, int row, int column) {
int sheetMergeCount = sheet.getNumMergedRegions();
int[] span = {1, 1};
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) {
span[0] = lastRow - firstRow + 1;
span[1] = lastColumn - firstColumn + 1;
break;
}
}
return span;
}
/**
* 获取excel中列数最多的行号
*
* @param sheet
* @return
*/
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;
}
/**
* excel垂直对齐方式映射到pdf对齐方式
*
* @param aglin
* @return
*/
private static int getVerAglin(short aglin) {
switch (aglin) {
case 1:
return com.itextpdf.text.Element.ALIGN_MIDDLE;
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水平对齐方式
*
* @param aglin
* @return
*/
private static int getHorAglin(short aglin) {
switch (aglin) {
case 2:
return com.itextpdf.text.Element.ALIGN_CENTER;
case 3:
return com.itextpdf.text.Element.ALIGN_RIGHT;
case 1:
return com.itextpdf.text.Element.ALIGN_LEFT;
default:
return com.itextpdf.text.Element.ALIGN_CENTER;
}
}
/**
* 格式化数字
*
* @param pattern
* @param num
* @return
*/
private static String numFormat(String pattern, double num) {
DecimalFormat format = new DecimalFormat(pattern);
return format.format(num);
}
public static int getPage(String fileId) {
int pages = 1;
PdfReader pdfReader = null;
String localFilePath = "";
try {
localFilePath = FileUtils.genLocalFilePath(fileId);
FileUtils.downLoadNetFile("https://www.com/fastdfsWeb/dfs/" + fileId, localFilePath);
FileInputStream fileInputStream = new FileInputStream(localFilePath);
pdfReader = new PdfReader(fileInputStream);
pages = pdfReader.getNumberOfPages();
if (pages <= 1) {
return 1;
}
} catch (Exception e) {
System.out.println("read page error");
System.out.println(e);
// 以防万一 读取报错
pages = 1000;
} finally {
if(pdfReader != null) {
pdfReader.close();
}
FileUtils.deleteDir(localFilePath);
}
return pages;
}
public static String upload(InputStream inputStream, long fileSize, String ext, Map<String, String> meta) {
try {
// return FastDFSUtil.upload(inputStream, fileSize, ext, meta);
return null;
} catch (Exception e) {
// log.info("[UploadFileUtil --> upload] is error, error is:{}", e);
// throw new BizException("上传失败");
throw new ServiceException("上传失败", "500");
}
}
public static void downLoadNetFile(String netUrl, String localPath) throws Exception {
// 下载网络文件
int bytesum = 0;
int byteread = 0;
URL url = new URL(netUrl);
InputStream inStream = null;
FileOutputStream fs = null;
try {
URLConnection conn = url.openConnection();
inStream = conn.getInputStream();
fs = new FileOutputStream(localPath);
byte[] buffer = new byte[2048];
while ((byteread = inStream.read(buffer)) != -1) {
bytesum += byteread;
fs.write(buffer, 0, byteread);
}
inStream.close();
fs.flush();
fs.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inStream != null) {
inStream.close();
}
if (fs != null) {
fs.close();
}
} catch (Exception e) {
}
}
}
public static String genLocalFilePath(String remoteFilePath) {
// AssertUtil.notEmpty(remoteFilePath, "远程文件路径不能为空");
if (StringUtils.isBlank(remoteFilePath)) {
throw new ServiceException("远程文件路径不能为空", "500");
}
String[] remoteFilePathArray = remoteFilePath.split("/");
if (remoteFilePathArray.length == 0) {
throw new ServiceException("远程文件路径不能为空", "500");
}
// AssertUtil.notEmpty(remoteFilePathArray, "远程文件路径不正确,提取文件名后缀失败");
//文件(文件名+后缀)
String file = remoteFilePathArray[remoteFilePathArray.length - 1];
file = file.split("\\?")[0];
file = file.split("\\.")[0].concat(".").concat(file.split("\\.")[1]);
//文件路径
String localFilePath = TEMP_PATH.concat(file);
return localFilePath;
}
public static void deleteDir(String dirPath) {
File file = new File(dirPath);
if (file.isFile()) {
file.delete();
} else {
File[] files = file.listFiles();
if (files == null) {
file.delete();
} else {
for (int i = 0; i < files.length; i++) {
deleteDir(files[i].getAbsolutePath());
}
file.delete();
}
}
}
}
测试如下:
public class LJPDFTest extends LJTest {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Test
public void pdfTest() {
try {
List<OrderDto> list = JSON.parseArray("[{\"auditStatus\":0,\"bankCardStatus\":0,\"billStatus\":0,\"cancelDate\":1585376549000,\"cancelMemo\":\"列表,取消订单,20032814215320001\",\"cancelType\":1,\"customerCode\":\"568146143\",\"driverFreight\":2000.00,\"driverMobile\":\"17700008810\",\"driverName\":\"李国风\",\"driverPartyId\":568103143,\"driverTotalFreight\":2000.00,\"fromAddress\":\"安徽芜湖市);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Map<String, Object> exportParam = new HashMap<>();
exportParam.put("list", list);
exportParam.put("applyTime", sdf.format(new Date()));
exportParam.put("signMobile", "13421678621");
exportParam.put("count", list.size());
exportParam.put("amount", "9999.08");
exportParam.put("company", "浙江大神有限公司");
exportParam.put("df", df);
byte[] bytes = FileUtils.excelToPdf(exportParam, "template/account.xlsx", true);
String fileId = FileUtils.upload(new ByteArrayInputStream(bytes), bytes.length, "pdf", null);
System.out.println("=========================" + fileId);
// System.out.println("=========================" + SignUtil.addSign4Url(FAST_DFS + fileId));
FileOutputStream fileOutputStream = new FileOutputStream(new File("/Users/sunww/Desktop/对账单.pdf"));
fileOutputStream.write(bytes);
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后在桌面可以看到一个PDF文件内容如下所示:
扫描二维码关注公众号,回复:
12455268 查看本文章
