一、实验环境
- java版本:1.8
- springboot版本:2.6.6
- jar依赖(maven):
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>${itext.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>${itext.version}</version>
<scope>test</scope>
</dependency>
4、压缩文件解压依赖(zip(zip使用JDK实现)、rar(只支持rar4))
<!-- rar 文件解压 -->
<dependency>
<groupId>com.github.junrar</groupId>
<artifactId>junrar</artifactId>
<version>7.5.0</version>
</dependency>
二、模板PDF填充PDF
此方式适用于需动态填充数据到PDF中
2.1 编辑PDF(笔者使用 万兴PDF编辑)
2.2 填充PDF代码示例
/**
* 填充PDF
*
* @param content 填充内容
* @return 保存位置
*/
public static String fillPdf(String content) {
PdfDocument pdfDoc = null;
String completePath = null;
String fileName = null;
try {
ClassPathResource classPathResource = null;
classPathResource = new ClassPathResource("templates/word-model-file.pdf");
fileName = "word-model-file-convert";
InputStream inputStream = null;
try {
inputStream = classPathResource.getInputStream();
} catch (IOException e) {
logger.error("获取模板文件失败:{}", e.getMessage());
}
completePath = CommonConfig.getFileSavePath() + "pdf/" + fileName + ".pdf";
File out = new File(completePath);
if (!out.exists()) {
out.getParentFile().mkdir();
}
pdfDoc = new PdfDocument(new PdfReader(inputStream),
new PdfWriter(completePath));
} catch (IOException e) {
logger.error("填充PDF失败:{}", e.getMessage());
}
//详细使用方法请参考itext7官方文档
//https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-jump-start-tutorial-for-java/chapter-6-reusing-existing-pdf-documents
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, false);
Map<String, PdfFormField> fields = form.getFormFields();
ClassPathResource robotoPath = new ClassPathResource("fonts/Roboto-Bold.ttf");
try {
PdfFont robotoRegularFont = PdfFontFactory.createFont(robotoPath.getPath(), PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED, true);
//填充内容 字体 字体大小 颜色
fields.get("content").setValue(content + new Date()).setFont(robotoRegularFont).setFontSize(12).setColor(new DeviceRgb(51, 51, 51));
} catch (Exception e) {
logger.error("PDF模板填充失败:" + e.getMessage());
}
form.flattenFields();
pdfDoc.close();
return completePath;
}
1、中文转换可能会存在乱码或不显示的情况,此情况多数是无此字体造成,可查看官方文档,嵌入使用字体
2、itext7还可创建PDF,编辑等操作,请参考官方文档:https://kb.itextpdf.com/home/it7kb/ebooks
三、HTML转PDF
笔者实现了本地HTML转换,和HTML压缩包转换的操作
3.1本地HTML转PDF
/**
* 本地html 转 PDF
*
* @param width PDF宽度 单位 pt
* @param height PDF高度 单位pt
* @return 生成的文件路径
*/
public static String html2pdfLocalFile(Float width, Float height, String newFileName) {
String path = String.format("%shtml/pdf/%s.pdf", CommonConfig.getFileSavePath(), newFileName);
File file = new File(path);
file.getParentFile().mkdirs();
try {
// PdfWriter writer = new PdfWriter(path,
// new WriterProperties().setFullCompressionMode(true));
//TODO 如需按照页面分页生成, 则需在CSS中定义分页,如下所示:
/*
*
* @page {
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
}
}
*
* */
PdfWriter writer = new PdfWriter(path);
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
PageSize pageSize = new PageSize(width, height);
pdf.setDefaultPageSize(pageSize);
ConverterProperties properties = new ConverterProperties();
//此根路径为html的根路径
properties.setBaseUri(CommonConfig.getFileSavePath() + "html/html");
//如果在代码中使用了相对路径的方式,则需要配置绝对路径
HtmlConverter.convertToPdf(new FileInputStream(CommonConfig.getFileSavePath() + "html/html/Untitled-1.html"), pdf, properties);
} catch (IOException e) {
logger.error("html2pdf 失败:{}", e.getMessage());
}
return path;
}
3.2HTML压缩包转换PDF
/**
* html 转 PDF
*
* @param width PDF宽度 单位 pt
* @param height PDF高度 单位pt
* @param multipartFile 上传的打包文件
* @param htmlFolderName html文件夹名称
* @param htmlName html名称
* @return 生成的文件路径
*/
public static String html2pdf(Float width,
Float height,
MultipartFile multipartFile,
String htmlFolderName,
String htmlName) {
/* html文件打包转换思路
* 1、上传打包文件
* 2、解压文件
* 3、执行html转pdf
* */
//上传文件到服务器
String upFilePath = FileUpload.upload(multipartFile);
logger.info("upFilePath:{}", upFilePath);
String name = multipartFile.getOriginalFilename();
String suffix = name.substring(name.lastIndexOf(".") + 1);
String unZipPath = getUnZipPath();
logger.info("unZipPath:{}", unZipPath);
if ("zip".equals(suffix)) {
//zip解压
try {
UZipFile.unZipFiles(new File(upFilePath), unZipPath);
} catch (IOException e) {
logger.error("zip 文件解压失败:{}", e.getMessage());
}
} else if ("rar".equals(suffix)) {
//rar解压 只支持 rar4
try {
UnRARUtil.unrarFile(upFilePath, unZipPath);
} catch (IOException | RarException e) {
logger.error("rar 文件解压失败:{}", e.getMessage());
}
}
String relativelyPath = String.format("pdf/%s.pdf", getCtm());
String path = CommonConfig.getFileSavePath() + relativelyPath;
logger.info("localPath:{}", path);
logger.info("relativelyPath:{}", relativelyPath);
File file = new File(path);
file.getParentFile().mkdirs();
try {
// PdfWriter writer = new PdfWriter(path,
// new WriterProperties().setFullCompressionMode(true));
//TODO 如需按照页面分页生成, 则需在CSS中定义分页,如下所示:
/*
*
* @page {
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
}
}
*
* */
PdfWriter writer = new PdfWriter(path);
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
PageSize pageSize = new PageSize(width, height);
pdf.setDefaultPageSize(pageSize);
ConverterProperties properties = new ConverterProperties();
//此根路径为html的根路径 需为物理路径,不能使用虚拟路径
logger.info("unZipPath:{}", unZipPath);
logger.info("htmlPath:{}", unZipPath + "/" + htmlFolderName + "/" + htmlName);
logger.info("BaseUri:{}", unZipPath + "/" + htmlFolderName);
properties.setBaseUri(unZipPath + "/" + htmlFolderName);
//如果在代码中使用了相对路径的方式,则需要配置绝对路径
HtmlConverter.convertToPdf(new FileInputStream(unZipPath + "/" + htmlFolderName + "/" + htmlName), pdf, properties);
} catch (IOException e) {
logger.error("html2pdf 失败:{}", e.getMessage());
}
logger.error("http://" + CommonUtils.getLocalIp() + ":" + serverPort + "/" + relativelyPath);
return getServerBasePath() + relativelyPath;
}
3.2.1 zip文件解压
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class UZipFile {
private static Logger log = LoggerFactory.getLogger(UZipFile.class);
/**
* 解压文件
*/
@SuppressWarnings("rawtypes")
public static List<String> unZipFiles(File zipFile, String descDir) throws IOException {
List<String> pathList = new ArrayList<>();
File pathFile = new File(descDir);
if (!pathFile.exists()) {
pathFile.mkdirs();
}
//解决zip文件中有中文目录或者中文文件
ZipFile zip = new ZipFile(zipFile, Charset.forName("GBK"));
for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String zipEntryName = entry.getName();
InputStream in = zip.getInputStream(entry);
String outPath = (descDir + "/" + zipEntryName).replaceAll("\\*", "/");
//判断路径是否存在,不存在则创建文件路径
File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
if (!file.exists()) {
file.mkdirs();
}
//判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
if (new File(outPath).isDirectory()) {
continue;
}
//输出文件路径信息
pathList.add(outPath);
log.info("解压文件路径为:" + outPath);
OutputStream out = new FileOutputStream(outPath);
byte[] buf1 = new byte[1024];
int len;
while ((len = in.read(buf1)) > 0) {
out.write(buf1, 0, len);
}
in.close();
out.close();
}
log.info("******************解压完毕********************");
return pathList;
}
}
3.2.2 rar文件解压
import com.github.junrar.Archive;
import com.github.junrar.exception.RarException;
import com.github.junrar.rarfile.FileHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
public class UnRARUtil {
private static Logger log = LoggerFactory.getLogger(UnRARUtil.class);
public static void unrarFile(String rarPath, String dstDirectoryPath) throws IOException, RarException {
File dstDiretory = new File(dstDirectoryPath);
if (!dstDiretory.exists()) {
dstDiretory.mkdirs();
}
Archive a = new Archive(new File(rarPath));
ArrayList<String> filepathList = new ArrayList<>();
if (a != null) {
a.getMainHeader().print(); //打印文件信息.
FileHeader fh = a.nextFileHeader();
// fileName= fh.getFileNameW().trim();
// if(!existZH(fileName)){
// fileName = fh.getFileNameString().trim();
// }
while (fh != null) {
// 判断编码,解决中文乱码的问题
String localpath = fh.isUnicode() ? fh.getFileNameW() : fh.getFileNameString();
//文件
File out = new File(dstDirectoryPath + File.separator + localpath.trim());
String outPath = out.getAbsolutePath().replaceAll("\\*", "/");
;
//判断路径是否存在,不存在则创建文件路径
File file = new File(outPath.substring(0, outPath.lastIndexOf('\\')));
if (!file.exists()) {
file.mkdirs();
}
//判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
if (new File(outPath).isDirectory()) {
break;
}
//输出文件路径信息
if (fh != null) {
filepathList.add(out.getAbsolutePath());
}
FileOutputStream os = new FileOutputStream(out);
a.extractFile(fh, os);
os.close();
fh = a.nextFileHeader();
}
}
a.close();
log.info("******************解压完毕********************");
}
}