需求
公司业务人员添加订单时要自动生成出库单pdf格式。
考虑过使用apache poi 功能强大,依赖组件多,文档少,不友好,放弃。
使用itext,我需求里边有用到表格,需要代码手画表格,过于繁琐放弃
使用阿里easyexcel,表格填充数据,功能很强大,但不适用我,有些表格内容数据过多,没法自动换行,放弃。
使用poi-tl,加载word模板填充数据生成word,然后通过使用aspose转换pdf,采用。
poi-tl官网
word模板
普通文本使用{{字段}}填充数据,代码要与word里边的字段相同即可,赋值数据
列表循环可以要使用{{goods}}里边的值要使用[值],还是比较简单的。
java代码
前期准备工作就这些,服务端开始 首先是引入jar包,这里采用的是maven引入
<!-- POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<!-- poi-tl -->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.0</version>
</dependency>
记得要刷新maven java端,填充数据 循环表格 生成word
创建生成word方法
//创建word 填充数据
@GetMapping(value = "/pdf/{id}")
public AjaxResult generatePDF(@PathVariable("id") Long id) throws IOException {
HjOrder order = hjOrderService.selectHjOrderById(id);
//加载模板
File file = new ClassPathResource("templates/orderTemplate.docx").getFile();
Map<String, Object> map = new HashMap<>();
//发往地区
map.put("city", order.getArea());
//共计袋数
map.put("total", 1);
//物流
map.put("logistics", order.getLogistics());
//收货人
map.put("consignee", order.getConsignee());
//电话
map.put("phone", order.getPhone());
//发货人
map.put("consignor", order.getConsignor());
//备注
map.put("remarks", order.getRemarks());
//制单人当前用户
map.put("creator", SecurityUtils.getLoginUser().getUser().getNickName());
//日期
map.put("date", DateUtils.getDate());
Map<String, Object> data = new HashMap<>();
List<Map<String, Object>> goodsList = new ArrayList<>();
// 初始化
int num = 1;
int totalBag = 0; // 初始化总袋数
//产品信息添加列表中
for (HjOrderSpecification specification : order.getHjOrderSpecificationList()) {
Map<String, Object> good = new HashMap<>();
good.put("num", num++); //序号
good.put("invoice", specification.getInvoice() == 0 ? "否" : "是");//发票
good.put("commission", specification.getCommission());//提成
good.put("productName", specification.getProductName()); // 产品名称
good.put("productRemark", specification.getProductRemark());//备注
good.put("bag", specification.getBag());//袋
good.put("ton", specification.getTon());//吨
good.put("money", specification.getMoney());//金额
good.put("totalPrice", 1600);
totalBag += specification.getBag();
goodsList.add(good);
}
//总袋数
map.put("total", totalBag);
data.put("goods", goodsList);
//合并map
data.putAll(map);
Configure config = Configure.builder().bind("goods", new LoopRowTableRenderPolicy()).build();
XWPFTemplate template = XWPFTemplate.compile(file, config).render(data);
template.writeToFile("template1_out.docx");
}
根据实际情况修改值,这上边是填充数据生成表格
生成后的效果
word转pdf
我是通过文件流的形式下载的pdf 没有保存到服务器上
// 获取文件流
try {
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("template1_out.docx");
System.out.println(inputStream);
XWPFTemplate template = XWPFTemplate.compile(inputStream).render(data);
Map<String, Object> maps = new HashMap<>();
ByteArrayOutputStream fos = new ByteArrayOutputStream();
template.write(fos);
byte[] bytes = fos.toByteArray();
byte[] convertToPdf = Doc2PdfUtil.doc2Pdf(bytes);
String bytesRes = StringUtils.bytesToHexString2(convertToPdf);
maps.put("bytes", bytesRes);
maps.put("title", System.currentTimeMillis() + "培训服务报告.pdf");
fos.close();
template.close();
return success(maps);
} catch (Exception e) {
throw new RuntimeException(e);
}
这里有用到bytesToHexString2
public static String bytesToHexString2(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src != null && src.length > 0) {
for(int i = 0; i < src.length; ++i) {
int v = src[i] & 255;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
} else {
return null;
}
}
这里是转换pdf代码,由于使用aspose,我们需要下载个本地jar包,进行引入,这样就可以没有水印。里边有工具类,可以直接使用 aspose去水印jar包下载链接 下载完成后需要把jar包放在resources/lib下 使用maven进行导入
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>15.8.0-jdk16</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/aspose-words-15.8.0-jdk16.jar</systemPath>
</dependency>
刷新maven 把工具类放到对应的文件夹下,license.xml放到resources下边
package com.ruoyi.business.utils;
import com.aspose.words.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class Doc2PdfUtil {
/**
* 加载授权配置文件
*
* @return
*/
private static boolean getLicense() {
boolean result = false;
try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("license.xml")) {
// License的包路径必须为com.aspose.words.License
License license = new License();
license.setLicense(in);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* doc转pdf
*
* @return
*/
public static byte[] doc2Pdf(byte[] bytes) {
System.out.println("pdf转换中...");
long old = System.currentTimeMillis();
try (ByteArrayOutputStream fos = new ByteArrayOutputStream()) {
// 验证
if (!getLicense()) {
throw new RuntimeException("文件转换失败!");
}
// 加载字体
//FontSettings settings = FontSettings.getDefaultInstance();
//String[] fontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
//settings.setFontsFolders(fontFamilyNames, true);
//LoadOptions loadOptions = new LoadOptions();
//loadOptions.setFontSettings(settings);
//加载字体
//FontSettings settings = FontSettings.getDefaultInstance();
//settings.setFontsFolder("C:\Windows\Fonts", true);
//LoadOptions loadOptions = new LoadOptions();
//loadOptions.setFontSettings(settings);
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
//Document document = new Document(inputStream, loadOptions);
Document document = new Document(inputStream);
document.save(fos, SaveFormat.PDF);
byte[] buffer = fos.toByteArray();
long now = System.currentTimeMillis();
System.out.println("pdf转换成功,共耗时:" + ((now - old) / 1000.0) + "秒");
return buffer;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("文件转换失败!");
}
}
}
这样就可以了。
前端预览下载pdf,由于使用的是文件流形式不存服务器,前端需要解析
function handlePDF(row) {
exportPdf(row.id).then((response) => {
if (response.code === 200) {
const data = response.data
const bytes = hexStr2Byte(data.bytes)
const blob = new Blob([bytes], {
type: `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
})
const objectUrl = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = objectUrl
link.setAttribute('download', data.title)
document.body.appendChild(link)
link.click()
} else {
this.$message.error(response.data.msg)
}
});
}
hexStr2Byte方法
function hexStr2Byte(str) {
var bytes = [];
for (var i = 0; i < str.length; i += 2) {
bytes.push(parseInt(str.substr(i, 2), 16));
}
return new Uint8Array(bytes);
}
这样就是使用poi-tl填充word循环列表生成word,并word转换pdf了,生成pdf效果图