Aspose-words导出PDF指南:Java导出带图片模板PDF
大家好,好久不见啊,俺最近也是忙来忙去导致写博客越来越少,正好开发过程中遇到一个需求是需要导出PDF,还要按照模板样式导出,还要处理图片问题,这俺就抓抓脑壳了,实不相瞒,俺之前就接触过Excel导出,这一次也算自己学习提升了,俺浏览了很多资料,最终锁定了Aspose.Words,但是Aspose.Words俺发现CSDN和博客园很多博主写的不是很清晰,所以今天俺就来分享一波这个需求点使用Aspose.Words导出PDF遇到的坑及如何导出。
一 Jar包介绍
Aspose.Words是一个商业.NET类库,可以使得应用程序处理大量的文件任务。Aspose.Words支持Doc,Docx,PDF等不同格式,使用该类库可以在不使用Microsoft.Word的情况下生成、修改、转换和打印文档。在项目中使用Aspose.Words可以有以下好处。
今天我分享的是使用Aspose.Words在Java中进行PDF导出及图片的导出,maven依赖如下
<dependency>
<groupId>com.luhuiguo</groupId>
<artifactId>aspose-words</artifactId>
<version>23.1</version>
</dependency>
二 导出步骤
我们先来看一下这个导出的空白模板
观察这个模板我们可以看到这里有realName,addr,图片1,图片2,以及巡防打卡四个地方需要处理,首先,执行看效果。
我们需要做的是 把这个模板Word先保存,然后放到我们的项目里面,比如俺这里把他放到了Resources目录下
闲话少说,上代码!!
@ApiOperation("测试PDF导出")
@GetMapping("/api/ffp/dict/PDFTest")
public ApiResult<Boolean> PDFTest(HttpServletResponse response,@RequestParam Long id) throws Exception {
FfpFileInfo fileInfo = ffpFileInfoService.getById(id);
if (ObjectUtil.isEmpty(fileInfo)) {
return ApiResult.error(602, "文件不存在");
}
String base64 = baseS3Service.downloadWithBase64(fileInfo.getFileKey());
byte[] bytes = Base64.getDecoder().decode(base64);
ClassPathResource classPathResource= new ClassPathResource("/templateFile/demo.docx");
InputStream inputStream = classPathResource.getInputStream();
Document document=new Document(inputStream);
Map paramsMap = new HashMap();
paramsMap.put("realname","Spring不止春天");
paramsMap.put("addr","四川");
PdfUtils.replaceText(paramsMap,document);
PdfUtils.replaceBookMarkImage( bytes,document);
String name="测试测试";
File tempFile = File.createTempFile(name, ".pdf");
//插入表格
DocumentBuilder builder = new DocumentBuilder(document);
NodeCollection runs = document.getChildNodes(NodeType.PARAGRAPH, true);
for (int i = 0; i < runs.getCount(); i++){
Node node = runs.get(i);
String text = node.getText();
System.out.println(text);
if (text.contains("巡防打卡")){
builder.moveTo(node);
Table table = builder.startTable();
// Insert a cell
builder.insertCell();
table.autoFit( AutoFitBehavior.AUTO_FIT_TO_CONTENTS );
table.setAlignment(1);
builder.getCellFormat().setWidth(135);
builder.write( "打卡时间" );
builder.insertCell();
builder.write( "日志" );
builder.insertCell();
builder.write( "打卡地址" );
// End row
builder.endRow();
// start a next row and set its properties
builder.getRowFormat().setHeight( 40 );
builder.getCellFormat().setWidth(135);
builder.insertCell();
builder.write( "2023:09:01:" );
builder.getCellFormat().setWrapText(true);
builder.insertCell();
builder.write( "进行了打卡的日志" );
builder.getCellFormat().setWrapText(true);
builder.insertCell();
builder.write( "四川省成都市武侯区1111111111111111111" );
builder.getCellFormat().setWrapText(true);
builder.endRow();
//第二行
builder.insertCell();
builder.write( "2023:09:01:" );
builder.getCellFormat().setWrapText(true);
builder.insertCell();
builder.write( "进行了打卡的日志" );
builder.getCellFormat().setWrapText(true);
builder.insertCell();
builder.write( "四川省成都市武侯区2222222222222222222222" );
builder.getCellFormat().setWrapText(true);
builder.endRow();
// End table
builder.endTable();
}
}
document.save(new FileOutputStream(tempFile), SaveFormat.PDF);
InputStream fis = new BufferedInputStream(new FileInputStream(tempFile));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
String filename=tempFile.getName();
// 设置响应的头部信息
response.setContentType("application/octet-stream;charset=UTF-8");
String fileName = new String(filename.getBytes("gb2312"), "iso8859-1");
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
OutputStream ouputStream = response.getOutputStream();
ouputStream.write(buffer);
ouputStream.flush();
ouputStream.close();
tempFile.delete();
return ApiResult.success(true);
}
下面我来逐一讲解简单参数替换,图片插入,列表插入三种不同格式的PDF处理
一 简单参数替换
realName和addr两个参数就属于简单替换值去进行处理,可以看到代码中有这么一段
这个replaceText就是真正替换的方法,方法如下:
/***
* 进行Word文件中的参数替换,方便转为PDF进行导出
* @author jiazl
* @date 2023/6/15 17:54
* @param paramMap,doc
* @return void
**/
public static void replaceText(Map<String, String> paramMap, Document doc) throws Exception {
FindReplaceOptions opt = new FindReplaceOptions();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
String key = String.format("{%s}", entry.getKey());
String value = Objects.isNull(entry.getValue()) ? "" : entry.getValue();
doc.getRange().replace(key, value, opt);
}
int replace = doc.getRange().replace(Pattern.compile(PARAM_MATCH), "", opt);
if (replace > 0) {
logger.error("未知参数:{}", JSON.toJSONString(paramMap));
}
}
这段代码的含义为寻找到Word中的{%s}参数,利用paramMap这个Map去进行值的替换,这个很简单不做过多赘述。
二 图片的插入
细心的同志已经发现了,我给出的这个API是带参数的,这个参数是什么呢,是一个文件ID,我们往想要插入的地方去插入图片,是需要寻找Word中的节点的,例如:
插入图片的方法为:
/***
* 进行Word文件中的图片插入(单处地方,单个图片)
* @author jiazl
* @date 2023/6/15 17:54
* @param doc
* @return void
**/
public static void replaceBookMarkImage( byte[] bytes, Document doc) throws Exception {
DocumentBuilder db = new DocumentBuilder(doc);
NodeCollection runs = doc.getChildNodes(NodeType.PARAGRAPH, true);
for (int i = 0; i < runs.getCount(); i++) {
Node node = runs.get(i);
String text = node.getText();
System.out.println(text);
if (text.contains("图片1")) {
db.moveTo(node);
db.insertImage(bytes, 119.4, 64);
}
}
}
在模板pdf中,就是这里
当他寻找到这个节点,就会执行
db.moveTo(node);
db.insertImage(bytes, 119.4, 64);
这两个方法,第一个是将操作对象转移到这个节点,第二个是进行图片的插入,119.4和64分别为设置的图片大小。
(当然我这里是个Demo接口所以找节点图片1是写死的,各位大宝贝可以将想要寻找的节点作为参数传入,这样代码会更壮硕!)
三 列表的插入
家人们啊,太难了,我浏览了很多网站,很少有人对列表插入写得清楚,因为这个Jar包是国内某大佬重新封装的(正版国外的收费),没注释,我一个一个方法试出来的,麻烦大家点个赞吧~~
对于列表的操作我们主要看这一部分代码
//插入表格
DocumentBuilder builder = new DocumentBuilder(document);
NodeCollection runs = document.getChildNodes(NodeType.PARAGRAPH, true);
for (int i = 0; i < runs.getCount(); i++){
Node node = runs.get(i);
String text = node.getText();
System.out.println(text);
if (text.contains("巡防打卡")){
builder.moveTo(node);
Table table = builder.startTable();
// Insert a cell
builder.insertCell();
table.autoFit( AutoFitBehavior.AUTO_FIT_TO_CONTENTS );
table.setAlignment(1);
builder.getCellFormat().setWidth(135);
builder.write( "打卡时间" );
builder.insertCell();
builder.write( "日志" );
builder.insertCell();
builder.write( "打卡地址" );
// End row
builder.endRow();
// start a next row and set its properties
builder.getRowFormat().setHeight( 40 );
builder.getCellFormat().setWidth(135);
builder.insertCell();
builder.write( "2023:09:01:" );
builder.getCellFormat().setWrapText(true);
builder.insertCell();
builder.write( "进行了打卡的日志" );
builder.getCellFormat().setWrapText(true);
builder.insertCell();
builder.write( "四川省成都市武侯区1111111111111111111" );
builder.getCellFormat().setWrapText(true);
builder.endRow();
//第二行
builder.insertCell();
builder.write( "2023:09:01:" );
builder.getCellFormat().setWrapText(true);
builder.insertCell();
builder.write( "进行了打卡的日志" );
builder.getCellFormat().setWrapText(true);
builder.insertCell();
builder.write( "四川省成都市武侯区2222222222222222222222" );
builder.getCellFormat().setWrapText(true);
builder.endRow();
// End table
builder.endTable();
}
}
这里作为Demo测试接口,写死两行数据,应用于具体业务可以利用for循环去做插入。
这里是不是很熟悉,插入列表同样是寻找到需要插入的节点,然后
builder.getCellFormat().setWrapText(true);
代码中这个方法就是让他在方格内换行的
三 总结及一些注意点
1 因为对于博主来说,插入图片,插入表格,替换模板参数已经是满足大部分开发任务了,更深层次的因为博主比较懒,所以也没有去了解。
2 这个Jar包对图片是有要求的,经过博主的一些测试。JPG,JPEG,PNG均是可以导出图片的,但是点名批评WEBP,会报错,因为我是懒狗,具体什么错也忘了。
3 秉持复制的态度,有用到博主文章中的方法有问题可以私信博主,博主看到就会回复的,放心,不是画大饼。
4 博主最后的给response赋值啊等一部分操作是无用的,Swagger是不支持导出流下载文件。用PostMan调接口会发现这么一个情况
博主努力了,解决不了后缀的问题,于是博主摆烂了给前端处理了。
5 注意代码中
Map paramsMap = new HashMap();
paramsMap.put("realname","Spring不止春天");
paramsMap.put("addr","四川");
这个paramsMap要和PDF中对应,不然会报错,什么错也忘了。
6 有人会说啊,那最终效果中图片两个字咋办?真聪明,再替换一次,把图片两个字替换为空串就行了,篇幅原因,不上代码了。
7 本来我也想写的很好,但是我真的是一个懒狗,对不起各位,但是我能保证代码是能好的,是正常使用的,是OK的!有问题欢迎随时联系~
版权声明:本文为博主作者:拒绝空指针原创文章,版权归属原作者,如果侵权,请联系我们删除!
原文链接:https://blog.csdn.net/weixin_49190101/article/details/131536093