适用于有word模版,然后word里面进行字符串替换
![](https://i-blog.csdnimg.cn/blog_migrate/8c4d04928b588f8ed39c550498d53790.png)
工具类
BokeWordUtils
package com.ruoyi.web.controller.tool;
import com.ruoyi.common.utils.StringUtils;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.RoundingMode;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.NumberFormat;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class BokeWordUtils {
/**
* 根据模板生成新word文档
* 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
*
* @param inputUrl 模板存放地址
* @param file 新文档存放地址
* @param textMap 需要替换的信息集合
*/
public static boolean changWord(String inputUrl, File file, Map<String, String> textMap) {
//模板转换默认成功
boolean changeFlag = true;
try {
//获取docx解析对象
XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
//解析替换文本段落对象
BokeWordUtils.changeText(document, textMap);
//解析替换表格对象
BokeWordUtils.changeTable(document, textMap, null);
//生成新的word
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream stream = new FileOutputStream(file);
document.write(stream);
stream.close();
System.out.println("成功生成!");
} catch (IOException e) {
e.printStackTrace();
changeFlag = false;
}
return changeFlag;
}
/**
* 替换段落文本
*
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
public static void changeText(XWPFDocument document, Map<String, String> textMap) {
//获取段落集合
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
//判断此段落时候需要进行替换
String text = paragraph.getText();
if (checkText(text)) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
//默认:宋体(wps)/等线(office2016) 5号 两端对齐 单倍间距
//run.setText("舜发于畎亩之中, 傅说举于版筑之间, 胶鬲举于鱼盐之中, 管夷吾举于士...");
run.setBold(true);//加粗
run.setCapitalized(false);//我也不知道这个属性做啥的
//runX.setCharacterSpacing(5);//这个属性报错
//run.setColor("BED4F1");//设置颜色--十六进制
run.setDoubleStrikethrough(false);//双删除线
run.setEmbossed(false);//浮雕字体----效果和印记(悬浮阴影)类似
//runX.setFontFamily("宋体");//字体
run.setFontFamily("方正粗黑宋简体", XWPFRun.FontCharRange.cs);//字体,范围----效果不详
run.setFontSize(11);//字体大小
run.setImprinted(false);//印迹(悬浮阴影)---效果和浮雕类似
run.setItalic(false);//斜体(字体倾斜)
//runX.setKerning(1);//字距调整----这个好像没有效果
run.setShadow(false);//阴影---稍微有点效果(阴影不明显)
//runX.setSmallCaps(true);//小型股------效果不清楚
//runX.setStrike(true);//单删除线(废弃)
run.setStrikeThrough(false);//单删除线(新的替换Strike)
//runX.setSubscript(VerticalAlign.SUBSCRIPT);//下标(吧当前这个run变成下标)---枚举
//runX.setTextPosition(20);//设置两行之间的行间距//runX.setUnderline(UnderlinePatterns.DASH_LONG);//各种类型的下划线(枚举)
//runX0.addBreak();//类似换行的操作(html的 br标签)
//runX0.addTab();//tab键
//runX0.addCarriageReturn();//回车键
//替换模板原来位置
// run.setText(changeValue(run.toString(), textMap),0);
String textValue = changeValue(run.toString(), textMap);
run.setText(textValue, 0);
}
}
}
}
/**
* 替换表格对象方法
*
* @param document docx解析对象
* @param textMap 需要替换的信息集合
* @param tableList 需要插入的表格信息集合
*/
public static void changeTable(XWPFDocument document, Map<String, String> textMap,
List<String[]> tableList) {
//获取表格对象集合
List<XWPFTable> tables = document.getTables();
for (int i = 0; i < tables.size(); i++) {
//只处理行数大于等于2的表格,且不循环表头
XWPFTable table = tables.get(i);
if (table.getRows().size() > 1) {
//判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
if (checkText(table.getText())) {
List<XWPFTableRow> rows = table.getRows();
//遍历表格,并替换模板
eachTable(rows, textMap);
} else {
// System.out.println("插入"+table.getText());
insertTable(table, tableList);
}
}
}
}
/**
* 遍历表格
*
* @param rows 表格行对象
* @param textMap 需要替换的信息集合
*/
public static void eachTable(List<XWPFTableRow> rows, Map<String, String> textMap) {
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
//插入图片
if (cell.getText().indexOf("JOB_IMAGE") != -1) {
String text = cell.getText().trim().replace("${", "").replace("}", "");
//String src = "C:\\Users\\12743\\Pictures\\QQ图片20211202183141.png";
String src = textMap.get(text);
if (StringUtils.isNotEmpty(src)) {
XWPFParagraph newPara = cell.getParagraphArray(0);
XWPFRun run = newPara.createRun();
try {
textMap.put(text, "");
File image = new File(src);
if (image.exists()) {
BufferedImage read = ImageIO.read(image);
int width = Units.toEMU(read.getWidth());
int height = Units.toEMU(read.getHeight());
//1 EMU = 1/914400英寸= 1/36000 mm,16是word文档中图片能设置的最大宽度cm
if (width / 360000 > 2.5) {
//向上取整,不要小数
NumberFormat f = NumberFormat.getNumberInstance();
f.setMaximumFractionDigits(0);
f.setRoundingMode(RoundingMode.UP);
//获取图片的EMU和图片在文档中最大EMU的比例值
Double d = width / 360000 / 2.5d;
//根据比例值计算得出适合文档的最大EMU数值
width = Integer.valueOf(f.format(width / d).replace(",", ""));
height = Integer.valueOf(f.format(height / d).replace(",", ""));
}
run.addPicture(new FileInputStream(src), ImagePojo.getPictureFormat(src), image.getName(), width, height);
} else {
if (src.indexOf("http") != -1) {
//src = "https://g.csdnimg.cn/common/csdn-footer/images/email.png";
URL url = new URL(src);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(10 * 1000);
httpURLConnection.setDoInput(true);
httpURLConnection.setRequestMethod("GET");
int responseCode = httpURLConnection.getResponseCode();
InputStream inputStream = null;
if (responseCode == 200) {
inputStream = httpURLConnection.getInputStream();
run.addPicture(inputStream, ImagePojo.getPictureFormat(src), image.getName(), (25 * 36000), (30 * 36000));
}
}
}
} catch (Exception e) {
System.out.println("world插入图片异常: " + e.getMessage());
}
}
}
//判断单元格是否需要替换
if (checkText(cell.getText())) {
List<XWPFParagraph> paragraphs = cell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
//默认:宋体(wps)/等线(office2016) 5号 两端对齐 单倍间距
//run.setText("舜发于畎亩之中, 傅说举于版筑之间, 胶鬲举于鱼盐之中, 管夷吾举于士...");
run.setBold(false);//加粗
run.setCapitalized(false);//我也不知道这个属性做啥的
//runX.setCharacterSpacing(5);//这个属性报错
//run.setColor("BED4F1");//设置颜色--十六进制
run.setDoubleStrikethrough(false);//双删除线
run.setEmbossed(false);//浮雕字体----效果和印记(悬浮阴影)类似
//runX.setFontFamily("宋体");//字体
run.setFontFamily("方正粗黑宋简体", XWPFRun.FontCharRange.cs);//字体,范围----效果不详
run.setFontSize(11);//字体大小
run.setImprinted(false);//印迹(悬浮阴影)---效果和浮雕类似
run.setItalic(false);//斜体(字体倾斜)
//runX.setKerning(1);//字距调整----这个好像没有效果
run.setShadow(false);//阴影---稍微有点效果(阴影不明显)
//runX.setSmallCaps(true);//小型股------效果不清楚
//runX.setStrike(true);//单删除线(废弃)
run.setStrikeThrough(false);//单删除线(新的替换Strike)
//runX.setSubscript(VerticalAlign.SUBSCRIPT);//下标(吧当前这个run变成下标)---枚举
//runX.setTextPosition(20);//设置两行之间的行间距//runX.setUnderline(UnderlinePatterns.DASH_LONG);//各种类型的下划线(枚举)
//runX0.addBreak();//类似换行的操作(html的 br标签)
//runX0.addTab();//tab键
//runX0.addCarriageReturn();//回车键
run.setText(changeValue(run.toString(), textMap), 0);
}
}
}
}
}
}
/**
* 为表格插入数据,行数不够添加新行
*
* @param table 需要插入数据的表格
* @param tableList 插入数据集合
*/
public static void insertTable(XWPFTable table, List<String[]> tableList) {
//创建行,根据需要插入的数据添加新行,不处理表头
for (int i = 1; i < tableList.size(); i++) {
XWPFTableRow row = table.createRow();
}
//遍历表格插入数据
List<XWPFTableRow> rows = table.getRows();
for (int i = 1; i < rows.size(); i++) {
XWPFTableRow newRow = table.getRow(i);
List<XWPFTableCell> cells = newRow.getTableCells();
for (int j = 0; j < cells.size(); j++) {
XWPFTableCell cell = cells.get(j);
cell.setText(tableList.get(i - 1)[j]);
}
}
}
/**
* 判断文本中时候包含$
*
* @param text 文本
* @return 包含返回true, 不包含返回false
*/
public static boolean checkText(String text) {
boolean check = false;
if (text.indexOf("$") != -1) {
check = true;
}
return check;
}
/**
* 匹配传入信息集合与模板
*
* @param value 模板需要替换的区域
* @param textMap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
public static String changeValue(String value, Map<String, String> textMap) {
Set<Entry<String, String>> textSets = textMap.entrySet();
for (Entry<String, String> textSet : textSets) {
//匹配模板与替换值 格式${key}
String key = "${" + textSet.getKey() + "}";
if (value.indexOf(key) != -1) {
// value = textSet.getValue();//全部参数替换
value = value.replace(key, textSet.getValue());//仅替换参数
}
}
//模板未匹配到区域替换为空
if (checkText(value)) {
value = "";
}
return value;
}
}
AsposeWordsUtils
package com.ruoyi.web.controller.tool;
import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
public class AsposeWordsUtils {
/**
* 判断是否有授权文件 如果没有则会认为是试用版,转换的文件会有水印
*
* @return
*/
public static boolean getLicense() {
boolean result = false;
try {
InputStream is = AsposeWordsUtils.class.getClassLoader().getResourceAsStream("license.xml");
//InputStream is=new FileInputStream("C:/profile/license.xml");
License aposeLic = new License();
aposeLic.setLicense(is);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static boolean doc2pdf(String sourcerFile, File file) {
return doc2pdf(sourcerFile, file, true);
}
/**
* Word转PDF操作
*
* @param sourcerFile 源文件
* @param file 目标文件
* @param licenseFlag 是否判断授权文件(仅测试的时候不判断)
*/
public static boolean doc2pdf(String sourcerFile, File file, Boolean licenseFlag) {
boolean flag = true;
if (licenseFlag && !getLicense()) {// 验证License 若不验证则转化出的pdf文档会有水印产生
return false;
}
try {
long old = System.currentTimeMillis();
FileOutputStream os = new FileOutputStream(file);
Document doc = new Document(sourcerFile); //sourcerFile是将要被转化的word文档
doc.save(os, SaveFormat.PDF);//全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换
os.close();
long now = System.currentTimeMillis();
System.out.println("共耗时:" + ((now - old) / 1000.0) + "秒"); //转化用时
} catch (Exception e) {
e.printStackTrace();
flag = false;
}
return flag;
}
}
word,pdf下载预览
@ResponseBody
@RequestMapping("/exportDocw")
@Log(title = "导出")
public void exportDoc(String xh, HttpServletRequest request, HttpServletResponse response) throws Exception {
HashMap<String, String> mmap = new HashMap<>();
//根据xh查询数据
AjaxResult result = null;
try {
result = this.JobXsDetails(xh);
} catch (Exception e) {
e.printStackTrace();
}
if (result != null) {
JobXs jobXs = (JobXs) result.get("data");
JobYjstjb jy = jobYjstjbService.selectJobYjstjbByXh(xh);
//把要展示的字段封装到hashmap里
String yxdm = String.valueOf(jobXs.getBj());
mmap.put("xs_yx", yxdm);
mmap.put("xs_xm", jobXs.getXm());
mmap.put("xs_xh", xh);
mmap.put("xs_zy", jobXs.getZydm());
mmap.put("xs_xb", jobXs.getXbdm());
mmap.put("xs_zzmm", jobXs.getZzmmdm());
mmap.put("xs_csny", jobXs.getCsrq());
mmap.put("xs_mz", jobXs.getMzdm());
//null会引起空指针
for (String key : mmap.keySet()) {
if (mmap.get(key) == null) {
mmap.put(key, "");
}
}
//时间戳加8位随机数
String filePrefix = System.currentTimeMillis() + "_" + (int) ((Math.random() * 9 + 1) * 10000000);
//word模板地址
String inputUrl = resourcesPath + "printTemplate/yjsjytjb.docx";
//打印地址
String path2 = path + "/template/yjsjy/";
File pathDirectory = new File(path2);
if (!pathDirectory.exists()) {
pathDirectory.mkdirs();
}
String inputUrl_doc = path2 + filePrefix + ".docx";
//参数替换
BokeWordUtils.changWord(inputUrl, new File(inputUrl_doc), mmap);
//文件下载
File pdfFile = new File(inputUrl_doc);
if (StringUtils.isNotEmpty(inputUrl_doc) && pdfFile.exists()) {
//word打印
String fileName = FileUtils.setFileDownloadHeader(request, jobXs.getXm() + "xxx表.docx");
//下载与预览的区别: 根据Content-disposition后边接的参数:attachment是下载,inline是预览
response.setHeader("Content-disposition", "attachment;fileName=" + fileName);
//如果是下载就不要下面这行代码,预览就打开
//response.setContentType("application/pdf");
//如果是下载就要下面这行代码,预览就不要这行
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
/*//pdf打印预览
String xms = xm + "xxx协议书.pdf";
String fileName = FileUtils.setFileDownloadHeader(request, xms);
//pdf下载与预览的区别: 根据Content-disposition后边接的参数:attachment是下载,inline是预览
response.setHeader("Content-disposition", "attachment;fileName=" + fileName);
response.setContentType("application/pdf");
//预览就不需要下面这行代码
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); */
FileInputStream in = null;
try {
in = new FileInputStream(pdfFile);
OutputStream out = response.getOutputStream();
byte[] b = new byte[512];
while ((in.read(b)) != -1) {
out.write(b);
}
out.flush();
in.close();
out.close();
//下载完毕后清理文件
pdfFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}