1. pom.xml引入包
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.6</version>
</dependency>
<dependency>
<groupId>net.sf.jtidy</groupId>
<artifactId>jtidy</artifactId>
<version>r938</version>
</dependency>
2. 编写4个工具类,分别是FontProvider.java、FreeMarkToHtml.java、Html2Xhtml.java、XHtml2Pdf.java
- FontProvider.java
/**
* 字体提供类, 更改 默认的字体
*/
public class FontProvider extends XMLWorkerFontProvider {
public FontProvider() {
super(null, null);
}
@Override
public Font getFont(final String fontname, String encoding, float size, final int style) {
String fntname = fontname;
if (fntname == null) {
fntname = "宋体";
}
return super.getFont(fntname, encoding, size, style);
}
}
- FreeMarkToHtml.java
/**
* FreeMark处理类
*/
public class FreeMarkToHtml {
/**
* 通过路径 和文件名称后去魔板
* @param templateFilePath
* @param templateFileName
* @return
*/
public static Template getTemplate(String templateFilePath, String templateFileName) {
try {
Configuration cfg = new Configuration();
cfg.setClassicCompatible(true);
TemplateLoader templateLoader = new FileTemplateLoader(new File(templateFilePath));
cfg.setTemplateLoader(templateLoader);
return cfg.getTemplate(templateFileName);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* @param templateFilePath
* @param templateFileName 加载的模板文件名
* @param replaceData
* @param outFile 生成指定文件
* @return 成功:返回文件名 失败:返回null
*/
public static String freemarkToHtml(String templateFilePath, String templateFileName, Map<String, Object> replaceData, String outFile) {
String path = null;
try (Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"))) {
Template temp = getTemplate(templateFilePath, templateFileName);
temp.setEncoding("UTF-8");
temp.process(replaceData, out);
path = outFile;
} catch (IOException | TemplateException e) {
e.printStackTrace();
path = null;
}
return path;
}
}
- Html2Xhtml.java
/**
* 将html 转换成为严格的XHTML
*/
public class Html2Xhtml {
/**
* 转化类
* @param html html文件输入路径(带文件名称)
* @param xhtml xhtml文件输入路径(带文件名称)
* @return
*/
public static String html2Xhtml(String html, String xhtml) {
String path = null;
try (FileInputStream fin = new FileInputStream(html)) {
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
int data = -1;
while ((data = fin.read()) != -1) {
byteArrayOut.write(data);
}
fin.close();
byte[] htmlFileData = byteArrayOut.toByteArray();
byteArrayOut.close();
ByteArrayInputStream tidyInput = new ByteArrayInputStream(htmlFileData);
ByteArrayOutputStream tidyOut = new ByteArrayOutputStream();
Tidy tidy = new Tidy();
tidy.setInputEncoding("UTF-8");
tidy.setOutputEncoding("UTF-8");
tidy.setShowWarnings(false);
tidy.setIndentContent(true);
tidy.setSmartIndent(true);
tidy.setIndentAttributes(false);
tidy.setMakeClean(true);
tidy.setQuiet(true);
tidy.setWord2000(true);
tidy.setXHTML(true);
tidy.setErrout(new PrintWriter(System.out));
tidy.parse(tidyInput, tidyOut);
tidyInput.close();
tidyOut.writeTo(new FileOutputStream(xhtml));
tidyOut.flush();
tidyOut.close();
path = xhtml;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
path = null;
}
return path;
}
}
- XHtml2Pdf.java
/**
* xhtml 转 pdf
*/
public class XHtml2Pdf {
/**
* 转化方法
* @param html html文件输入路径(带文件名称)
* @param pdf pdf文件输出路径(带文件名称)
*/
public static void XHtml2Pdf(String html, String pdf) throws FileNotFoundException, IOException, DocumentException, CssResolverException {
int i = html.lastIndexOf(".html");
String xhtml = null;
xhtml = html.substring(0, i) + ".xhtml";
xhtml = Html2Xhtml.html2Xhtml(html, xhtml);
if (xhtml != null) {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdf));
document.open();
FontProvider fontProvider = new FontProvider();
fontProvider.addFontSubstitute("lowagie", "garamond");
fontProvider.setUseUnicode(true);
// 使用我们的字体提供器,并将其设置为unicode字体样式
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));
XMLWorker worker = new XMLWorker(pipeline, true);
XMLParser p = new XMLParser(worker);
File input = new File(xhtml);
p.parse(new InputStreamReader(new FileInputStream(input), "UTF-8"));
document.close();
}
}
}
3. 编写ExcelExportController.java类和ExcelExportService.java类
- ExcelExportController.java
@RestController
@RequestMapping("/excelExport")
public class ExcelExportController {
@Autowired
private ExcelExportService excelExportService;
/**
* 导出(下载)审批单
* @return Response
*/
@GetMapping("/exportApproveRecord")
public void exportApproveRecord(ApproveQuery excelVo, HttpServletRequest request, HttpServletResponse response) {
try {
URLDecoder.decode(String.valueOf(excelVo), "UTF-8");
excelExportService.exportApproveRecord(excelVo, request, response);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
- ExcelExportService.java
@Service
public class ExcelExportService {
private final Logger logger = LoggerFactory.getLogger(ExcelExportService.class);
//生产环境的下载路径(根据自己服务器的真实路径配置)
String filePath = "/opt/deployments/BOOT-INF/classes/template/";
//pdf文件名:审批单名字.pdf
String pdfFileName = "审批单名字.pdf";
//html模板文件路径(resources目录下)
String templateFilePath = "template/approve_record.ftl";
//html模板文件路名
String templateFileName = "approve_record.html";
/**
* 导出(下载)审批单
* @param excelVo
* @param response
*/
public void exportApproveRecord(ApproveQuery excelVo, HttpServletRequest request, HttpServletResponse response) {
if (null == excelVo) {
throw new BusinessException("导出打印审批单失败,请刷新界面重新操作!");
}
URL url = Thread.currentThread().getContextClassLoader().getResource("");
//第1步:获取数据并创建文pdf文件
Map<String, Object> tableData = new HashMap<>(3);
tableData.put("data", new ArrayList<>());
//第2步:模板转换
FreeMarkToHtml.freemarkToHtml(Objects.requireNonNull(url).getPath(), templateFilePath, tableData, url.getPath() + templateFileName);
try {
XHtml2Pdf.XHtml2Pdf(url.getPath() + templateFileName, url.getPath() + pdfFileName);
} catch (IOException | DocumentException | CssResolverException e) {
e.printStackTrace();
}
//第3步:执行下载操作
//filePath = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("")).getPath();
File file = new File(filePath + pdfFileName);
try (
InputStream is = new FileInputStream(file);
OutputStream os = response.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(is)) {
byte[] buff = new byte[1024];
int i = bis.read(buff);
while (i != -1) {
os.write(buff, 0, buff.length);
os.flush();
i = bis.read(buff);
}
} catch (IOException e1) {
throw new BusinessException("导出打印审批单失败,请刷新界面重新操作!");
}
}
}
4. 前端页面
- “导出”按钮
<Button icon={<DownloadOutlined />} type="primary" loading={exportApproveLoading} onClick={exportPrint}>
导出审批单
</Button>
- “exportPrint”函数
/**
* 导出打印审批单
*/
const exportPrint = async () => {
setExportApproveLoading(true);
//参数,没有可以不设置
const url = `?params=1`;
//url.replace去掉空格
window.open(`/api/excelExport/exportApprove?excelVo=${encodeURI(url.replace(/\s/g, '')) }`, '_blank');
setExportApproveLoading(false);
};
4. approve_record.ftl模板
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>审批单(pdf文件)</title>
</head>
<body>
<div class="main">
<#if data??>
<!-- data为后端返回的数据 -->
<div>${data}</div>
</#if>
</div>
</body>
</html>