为什么使用后端去生成
现在前端vue是有一套生成pdf的依赖,很简单,但是生成的也没办法满足太多功能,例如表头页码,已经复制粘贴等问题
说明
本章有两个方法分别是生成两个模板合成一起的pdf文件,一个是单独一个pdf文件
依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version> <!--(or higher)-->
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.4.1</version>
</dependency>
后端目录
控制器
生成的模板位置
控制器代码
package org.jeecg.modules.drainage.controller;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfPTable;
import org.jeecg.modules.drainage.service.ISensorService;
import org.jeecg.modules.drainage.vo.SensorAndSensorWaterLevel;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.util.PDFMergerUtility;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.xhtmlrenderer.pdf.ITextRenderer;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Api(tags = "pdf接口")
@RestController
@RequestMapping("/pdf")
public class PdfController {
@Autowired
private ISensorService sensorService;
private static final String classpath = PdfController.class.getClassLoader().getResource("").getPath();
String filePath = classpath + "static/templateFCss.pdf";
private static final String DEST = "templateFCss.pdf";
private static final String DESTS = "fmr4s.pdf";
private static final String RAINDESTS = "rainfall.pdf";
private static final String HTML = "templateFCss.html";
private static final String FHTML = "fmr4.html";
private static final String RAINHTML = "rain.html";
private static Configuration freemarkerCfg = null;
static {
freemarkerCfg =new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
freemarkerCfg.setTemplateLoader(new ClassTemplateLoader(PdfController.class,"/pdfTemplate/"));
//freemarker的範本目錄
// try {
// freemarkerCfg.setDirectoryForTemplateLoading(new File(classpath));
// } catch (IOException e) {
// e.printStackTrace();
// }
}
@ApiOperation(value = "導出一个模板pdf文件")
@GetMapping("/pdfRainExport")
public void pdfRainExport(HttpServletResponse response, String searchInput, String startDate, String endDate ) throws IOException, DocumentException {
Map<String,Object> data = new HashMap<String,Object>();
//mybatis查詢根據自己去查詢就好了
List<SensorAndSensorWaterLevel> findSensor= sensorService.findSensor(searchInput,null,null,null,startDate,endDate);
data.put("findSensor", findSensor);
PdfPTable table = new PdfPTable(2);
String content =freeMarkerRender(data,RAINHTML);
createPdf(content,RAINDESTS);
//===============主要下載程式碼======================
/**
* 下載
*/
String savePath = null;
FileInputStream in = null;
OutputStream out = null;
BufferedOutputStream bos = null;
//獲取文件名
savePath = RAINDESTS;
String filename = savePath.substring(savePath.lastIndexOf("/")+1);
filename = new String(filename.getBytes("iso8859-1"),"UTF-8");
// response.setContentType("application/pdf");
String downloadpath = savePath;
response.setContentType("application/pdf");
//設置響應頭
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
//讀取下載文件保存文件流
in= new FileInputStream(downloadpath);
//創建輸出流
out= response.getOutputStream();
bos = new BufferedOutputStream(out);
//緩存區
byte buffer[] = new byte[1024];
int len = 0;
//循環内容到緩存區
while((len = in.read(buffer)) > 0){
bos.write(buffer, 0, len);
}
bos.flush();
in.close();
bos.close();
//刪除臨時文件
File deleteFile=new File(savePath);
deleteFile.delete();
}
@ApiOperation(value = "導出两个模板合成的pdf文件")
@GetMapping("/pdfFmrs4Export")
public void projectExport( HttpServletResponse response ) throws IOException, DocumentException {
System.out.println(classpath);
Map<String,Object> data = new HashMap<String,Object>();
List<SensorAndSensorWaterLevel> findSensor= sensorService.findSensor(null,null,null,null,null,null);
data.put("findSensor", findSensor);
PdfPTable table = new PdfPTable(2);
String content =freeMarkerRender(data,HTML);
String contents =freeMarkerRender(data,FHTML);
//System.out.println(content);
createPdf(contents,DESTS);
createPdf(content,DEST);
//pdf合并工具類
PDFMergerUtility mergePdf = new PDFMergerUtility();
//合并后生成的pdf文件名稱
String destinationFileName ="fmrs4.pdf";
// 合并后pdf存放路徑
String bothPath = "e://test";
File file3 = new File(classpath);
try{
if(!file3.exists()){
file3.mkdirs();
}
}catch(Exception e1){
e1.printStackTrace();
}
mergePdf.addSource(DEST);
mergePdf.addSource(DESTS);
//設置合并pdf名稱
mergePdf.setDestinationFileName(classpath + File.separator + destinationFileName);
//合并pdf
try {
try {
mergePdf.mergeDocuments();
} catch (IOException e) {
e.printStackTrace();
}
} catch (COSVisitorException e) {
e.printStackTrace();
}
//===============主要下載代碼======================
/**
* 下載
*/
String savePath = null;
FileInputStream in = null;
OutputStream out = null;
BufferedOutputStream bos = null;
//獲取文件名
savePath = classpath+"fmrs4.pdf";
String filename = savePath.substring(savePath.lastIndexOf("/")+1);
filename = new String(filename.getBytes("iso8859-1"),"UTF-8");
// response.setContentType("application/pdf");
String downloadpath = savePath;
response.setContentType("application/pdf");
//設置響應頭
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
//讀取下載文件保存文件流
in= new FileInputStream(downloadpath);
//創建輸出流
out= response.getOutputStream();
bos = new BufferedOutputStream(out);
//緩存區
byte buffer[] = new byte[1024];
int len = 0;
//循環内容到緩存區
while((len = in.read(buffer)) > 0){
bos.write(buffer, 0, len);
}
bos.flush();
in.close();
bos.close();
//刪除臨時文件
File deleteFile=new File(savePath);
deleteFile.delete();
}
/**
* freemarker渲染html
*/
public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
Writer out = new StringWriter();
try {
// 獲取模板,設置編碼
Template template = freemarkerCfg.getTemplate(htmlTmp);
template.setEncoding("UTF-8");
// 合并模型跟數據
template.process(data, out); //合并后寫入流
out.flush();
return out.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return null;
}
public static void createPdf(String content,String dest) throws IOException, DocumentException {
ITextRenderer render = new ITextRenderer();
// 解析html生成pdf
render.setDocumentFromString(content);
render.layout();
String path = System.getProperty("catalina.home");
render.createPDF(new FileOutputStream(dest));
render.finishPDF();
}
/**
* File to 64bit Str
*
* @param file
* @return
*/
public static String fileToBase64Str(File file) {
byte[] data = null;
InputStream inputStream = null;
if (file != null) {
try {
inputStream = new FileInputStream(file);
data = new byte[inputStream.available()];
inputStream.read(data);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return Base64.encodeBase64String(data);
}
return null;
}
}
模板代码
這裏的話就只舉例一個模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<style>
body{
font-family:SimHei;
}
.color{
color: green;
}
.pos{
position:absolute;
left:200px;
top:5px;
width: 200px;
font-size: 10px;
}
.pageTable{
margin-top:0.5in;
}
.page{
margin-top:0.6in;
}
@media print {
div.header-right {
display: block;
position: running(header-right);
}
img{page-break-inside:avoid;}
table{page-break-inside:avoid;}
}
@page {
size: 8.5in 11in;
margin-top:1.53in;
margin-left:0.25in;
margin-right:0.25in;
margin-bottom:0.6in;
/*@bottom-center {
content : "Page " counter(page) " of " counter(pages);
}; */
@bottom-center {
content: element(footer)
}
@top-center {content: element(header)}
}
#header {
position:running(header);
margin-top:0.52in;
}
#footer {
position: running(footer);
}
#pages:before {
content: counter(page);
}
#pages:after {
content: counter(pages);
}
table {
border-top: 1px solid #999;
border-spacing: 0;
}
table td {
border-bottom: 1px solid #999;
border-right: 1px solid #999;
}
</style>
</head>
<body>
<div id ="header">
<table class="tablefmr4" style="margin-top:15px;margin: 0 auto; table-layout:fixed; " width="93%" >
<thead>
<tr style="background:#15afcc;color: #fff">
<td width="65" style="padding: 7px; border-left: 1px solid #999; word-break:break-all; word-wrap : break-word;">Distriot-Static ID</td>
<td width="135" style="padding: 7px; word-break:break-all; word-wrap : break-word;">Station Name</td>
<td width="55" style="padding: 7px; word-break:break-all; word-wrap : break-word;">Observed Water Level(mPD)</td>
<td width="55" style="padding: 7px; word-break:break-all; word-wrap : break-word;">River Bank Level(mPD)</td>
<td width="55" style="padding: 7px; word-break:break-all; word-wrap : break-word;">Alert Level(mPD)</td>
<td width="55" style="padding: 7px; word-break:break-all; word-wrap : break-word;">Water Depth above Alert Level(m)</td>
<td width="55" style="padding: 7px; word-break:break-all; word-wrap : break-word;">Probable Affected Areas</td>
<td width="55" style="padding: 7px; word-break:break-all; word-wrap : break-word;">Time of Date Record</td>
</tr>
</thead>
</table>
</div>
<div id="footer">
<div style="text-align: center; width: 100%;font-size: 15px;">Page <span id="pages"> of </span></div>
</div>
<div class="page">
<table class="pageTable" style="margin: 0 auto; table-layout:fixed; " width="95%" >
<tbody width="80%">
<#list findSensor as u>
<tr>
<td width="65" style=" border-left: 1px solid #999; padding: 15px 10px 10px 10px; word-break:break-all; word-wrap : break-word;">${u.stationId!''}</td>
<td width="135" style="padding: 10px;word-break:break-all; word-wrap : break-word;">${u.location!''}</td>
<td width="55" style="padding: 10px;word-break:break-all; word-wrap : break-word;">${u.waterLevel!''}</td>
<td width="55" style="padding: 10px;word-break:break-all; word-wrap : break-word;">${u.riverbankLevel!''}</td>
<td width="55" style="padding: 10px;word-break:break-all; word-wrap : break-word;">${u.alarmLevel!''}</td>
<td width="55" style="padding: 10px; word-break:break-all; word-wrap : break-word;">${(u.waterLevel-u.alarmLevel)!''}</td>
<td width="55" style="padding: 10px; word-break:break-all; word-wrap : break-word;"></td>
<td width="55" style="padding: 10px; word-break:break-all; word-wrap : break-word;">${u.updateTime?string('YYYY-MM-dd HH:mm:ss')!''}</td>
</tr>
</#list>
</tbody>
</table>
</div>
</body>
</html>
前端代碼
接口
// 导出pdf
export function pdfFmrs4Export(fileName) {
return request({
url: '/api/pdf/pdfFmrs4Export',
method: 'get',
responseType: 'arraybuffer',
params:{
fileName:fileName //文件名稱
}
}).then(response => {
console.log(response);
const elink = document.createElement("a");
console.log(new Blob([response.data]));
elink.href = window.URL.createObjectURL(new Blob([response], {type: `application/pdf;;charset-UTF-8`}));
elink.style.display = 'none';
elink.setAttribute('download',fileName);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
});
};