一、首先制作word模版(这里需要注意的是文件后缀是docx不能是doc),${xxxx}是一会要替换的内容
关于为何必须是docx后缀可以看这篇文章https://www.cnblogs.com/ct-csu/p/8178932.html
二、添加poi所需要的jar包文件,我用的maven对jar包进行管理
三、由于poi自身bug,会出现图片无法显示问题,这里需要自定义一个类继承XWPFDocument类,接下来使用的都是我们自己创建的这个类来操作word对象,这个
类对XWPFDocument进行了继承,所以不用担心会有什么问题
package com.newdo.base;
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
/**
* word 导出工具类
*/
public class CustomXWPFDocument extends XWPFDocument{
public CustomXWPFDocument(InputStream in) throws IOException {
super(in);
}
public CustomXWPFDocument() {
super();
}
public CustomXWPFDocument(OPCPackage pkg) throws IOException {
super(pkg);
}
}
四、接下来就是导出word的工具类了
package com.newdo.base;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
/**
* word 导出工具类
*/
public class WordUtils {
/**
* 用于判断导出的word是否转pdf
*/
public static int judgment = 0;
public WordUtils() {
judgment = 0;
}
/**
* 根据模板生成word
*
* @param path 模板的路径
* @param params 需要替换的参数
* @param tableList 需要插入的参数
* @param fileName 生成word文件的文件名
* @param response
*/
public void getWord(String path, Map<String, Object> params, List<String[]> tableList, String fileName,
HttpServletResponse response) throws Exception {
String saveRoute = path.substring(0, path.indexOf("\\"));
File file = new File(path);
InputStream is = new FileInputStream(file);
CustomXWPFDocument doc = new CustomXWPFDocument(is);
this.replaceInPara(doc, params); // 替换文本里面的变量
this.replaceInTable(doc, params, tableList); // 替换表格里面的变量
OutputStream os = null;
if (judgment == 0) {
fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
os = new FileOutputStream(saveRoute + "\\word\\" + fileName);
doc.write(os);
String pdf = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf"; // 截掉
wToPdfChange(saveRoute + "\\word\\" + fileName, saveRoute + "\\word\\" + pdf);
try {
System.out.println("========================pdf下载开始========================");
// path是指欲下载的文件的路径。
file = new File(saveRoute + "\\word\\" + pdf);
// 取得文件名。
String filename = URLEncoder.encode(file.getName(), "utf-8");
// 取得文件的后缀名。
String ext = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();
// 以流的形式下载文件。
is = new BufferedInputStream(new FileInputStream(saveRoute + "\\word\\" + pdf));
byte[] buffer = new byte[is.available()];
is.read(buffer);
// 清空response
response.reset();
// 设置response的Header
response.addHeader("Content-Disposition", "attachment;filename=" + filename);
response.addHeader("Content-Length", "" + file.length());
os = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
os.write(buffer);
os.flush();
System.out.println("========================pdf下载结束========================");
} catch (IOException ex) {
ex.printStackTrace();
}
} else {
// os = response.getOutputStream();
fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
os = new FileOutputStream(saveRoute + "\\word\\" + fileName);
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
doc.write(os);
judgment = 0;
}
this.close(os);
this.close(is);
}
/**
* word 转 pdf
*
* @param wordFile word 的路径 word 的路径
* @param pdfFile pdf 的路径
*/
public static void wToPdfChange(String wordFile, String pdfFile) {
ActiveXComponent app = null;
Dispatch document = null;
System.out.println("========================开始转换========================");
try {
// 打开word
System.out.println("开始打开word");
app = new ActiveXComponent("Word.Application");
// 获得word中所有打开的文档
Dispatch documents = app.getProperty("Documents").toDispatch();
System.out.println("打开文件: " + wordFile);
// 打开文档
document = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();
// 如果文件存在的话,不会覆盖,会直接报错,所以我们需要判断文件是否存在
File target = new File(pdfFile);
if (target.exists()) {
target.delete();
}
System.out.println("另存为: " + pdfFile);
Dispatch.call(document, "SaveAs", pdfFile, 17);
} catch (Exception e) {
System.out.println("转换失败" + e.getMessage());
} finally {
// 关闭office
// app.invoke("Quit", 0);
if (document != null) {
// 关闭文档
Dispatch.call(document, "Close", false);
}
if (app != null) {
app.invoke("Quit", 0);
}
// 获取系统类型
String osName = System.getProperty("os.name");
// 判断是系统类型
if (osName.toLowerCase().startsWith("win")) {
System.out.println(osName);
// window系统
String killCmd = "taskkill /f /im wps.exe";
String killCmd1 = "taskkill /f /im wpscenter.exe";
Process p;
try {
p = Runtime.getRuntime().exec(killCmd);
p = Runtime.getRuntime().exec(killCmd1);
int runnngStatus = p.waitFor();
System.out.println("已杀" + runnngStatus);
} catch (IOException e) {
e.printStackTrace();
System.out.println("转换失败" + e.getMessage());
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("转换失败" + e.getMessage());
}
}
}
System.out.println("========================转换结束========================");
}
/**
* 替换段落里面的变量
*
* @param doc 要替换的文档
* @param params 参数
*/
private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {
Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
XWPFParagraph para;
while (iterator.hasNext()) {
para = iterator.next();
this.replaceInPara(para, params, doc);
}
}
/**
* 替换段落里面的变量
*
* @param para 要替换的段落
* @param params 参数
*/
private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
List<XWPFRun> runs;
Matcher matcher;
if (this.matcher(para.getParagraphText()).find()) {
runs = para.getRuns();
int start = -1;
int end = -1;
String runsIndex = "";
String str = "";
for (int i = 0; i < runs.size(); i++) {
XWPFRun run = runs.get(i);
String runText = run.toString().trim();
if (!runText.equals("") && (runText.indexOf("${")!=-1 || runText.equals("}"))){
if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {
start = i;
runsIndex += i+",";
}
if ((start != -1)) {
str += runText;
}
if ('}' == runText.charAt(runText.length() - 1)) {
if (start != -1) {
end = i;
for (int k = start; k <= end; k++) {
para.removeRun(k);
k--;
end--;
}
}
}
}
}
// for (int i = start; i <= end; i++) {
// para.removeRun(i);
// i--;
// end--;
// }
int doInt = 0;
String[] runsIns = runsIndex.split(",");
for (int k=0;k<runsIns.length;k++){
if (!"".equals(str)) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
String key = entry.getKey();
if (str.indexOf(key) != -1) {
Object value = entry.getValue();
if (value instanceof String) {
str = str.replace(key, value.toString());
if (((String) str).indexOf("\r") > 0) {
// 设置换行
String[] text = str.toString().split("\r");
para.removeRun(0);
for (int f = 0; f < text.length; f++) {
if (f == 0) {
// 此处不缩进因为word模板已经缩进了。
para.createRun().setText(text[f].trim());
} else {
para.createRun().addCarriageReturn();// 硬回车
// 注意:wps换行首行缩进是三个空格符,office要的话可以用
// run.addTab();缩进或者四个空格符
para.createRun().setText(text[f]);
}
}
} else {
// para.createRun().setText((String) value);
int num = 0;
if (doInt==0){
num = CFunc.ToInt(runsIns[k]);
}else{
num = CFunc.ToInt(runsIns[k])+doInt;
}
para.insertNewRun(num).setText((String) value);
doInt++;
}
// para.createRun().setText(str, 0);
break;
} else if (value instanceof Map) {
str = str.replace(key, "");
Map pic = (Map) value;
int width = Integer.parseInt(pic.get("width").toString());
int height = Integer.parseInt(pic.get("height").toString());
int picType = getPictureType(pic.get("type").toString());
byte[] byteArray = (byte[]) pic.get("content");
ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
try {
// int ind =
// doc.addPicture(byteInputStream,picType);
// doc.createPicture(ind, width , height,para);
doc.addPictureData(byteInputStream, picType);
para.createRun().setText(str, 0);
break;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
}
}
/**
* 为表格插入数据,行数不够添加新行
*
* @param table 需要插入数据的表格
* @param tableList 插入数据集合
*/
private static void insertTable(XWPFTable table, List<String[]> tableList) {
// 创建行,根据需要插入的数据添加新行,不处理表头
for (int i = 0; i < tableList.size(); i++) {
XWPFTableRow row = table.createRow();
}
// 遍历表格插入数据
List<XWPFTableRow> rows = table.getRows();
int length = table.getRows().size();
for (int i = 1; i < length - 1; i++) {
XWPFTableRow newRow = table.getRow(i);
List<XWPFTableCell> cells = newRow.getTableCells();
for (int j = 0; j < cells.size(); j++) {
XWPFTableCell cell = cells.get(j);
String s = tableList.get(i - 1)[j];
cell.setText(s);
}
}
}
/**
* 替换表格里面的变量
*
* @param doc 要替换的文档
* @param params 参数
*/
private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<String[]> tableList) {
Iterator<XWPFTable> iterator = doc.getTablesIterator();
XWPFTable table;
List<XWPFTableRow> rows;
List<XWPFTableCell> cells;
List<XWPFParagraph> paras;
while (iterator.hasNext()) {
table = iterator.next();
// System.out.println("------------>" + table.getRows().size());
if (table.getRows().size() > 1) {
// 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
if (this.matcher(table.getText()).find()) {
rows = table.getRows();
for (XWPFTableRow row : rows) {
cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
paras = cell.getParagraphs();
for (XWPFParagraph para : paras) {
this.replaceInPara(para, params, doc);
}
}
}
} else {
insertTable(table, tableList); // 插入数据
}
}
}
}
/**
* 正则匹配字符串
*
* @param str
* @return
*/
private Matcher matcher(String str) {
Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(str);
return matcher;
}
/**
* 根据图片类型,取得对应的图片类型代码
*
* @param picType
* @return int
*/
private static int getPictureType(String picType) {
int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
if (picType != null) {
if (picType.equalsIgnoreCase("png")) {
res = CustomXWPFDocument.PICTURE_TYPE_PNG;
} else if (picType.equalsIgnoreCase("dib")) {
res = CustomXWPFDocument.PICTURE_TYPE_DIB;
} else if (picType.equalsIgnoreCase("emf")) {
res = CustomXWPFDocument.PICTURE_TYPE_EMF;
} else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
} else if (picType.equalsIgnoreCase("wmf")) {
res = CustomXWPFDocument.PICTURE_TYPE_WMF;
}
}
return res;
}
/**
* 将输入流中的数据写入字节数组
*
* @param in
* @return
*/
public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {
byte[] byteArray = null;
try {
int total = in.available();
byteArray = new byte[total];
in.read(byteArray);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (isClose) {
try {
in.close();
} catch (Exception e2) {
e2.getStackTrace();
}
}
}
return byteArray;
}
/**
* 关闭输入流
*
* @param is
*/
public void close(InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 关闭输出流
*
* @param os
*/
public void close(OutputStream os) {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
五、word转pdf文件我们需要用到一个jacob.jar(下载地址在文章下面 )的一个包,将该jar包放入项目中的resources文件下的lib中
maven中引入本地jar包
同时需要将jacob中的dll文件放到自己的jdk环境中,将 dll 文件拷贝到 C:\Program Files\Java\jdk1.8.0_191\jre\bin (这是我自己的jdk位置)下,或者拷贝到当前项目运行的 jdk 的目录下的 jre/bin 目录下
这里的文件多少位(×64或×86)是根据自己的电脑来的
完成以上操作word才能正常转换为pdf文件
六、最后就是进行测试了,我是用的是springboot框架,这里放出测试代码
@Override
public void contractPrintDetail(HttpServletRequest request, HttpServletResponse response, Map<String, Object> mapArgu) {
//查询出来的数据
Map<String,Object> map = contractPrintMapper.mySelect(mapArgu);
WordUtils wordUtil = new WordUtils();
Map<String, Object> params = new LinkedHashMap<String, Object>();
List<String[]> testList = new ArrayList<String[]>();
params.put("${DWMC}",map.get("DWMC"));
params.put("${DWDZ}",map.get("DWDZ"));
params.put("${FDDBR}",map.get("FDDBR"));
params.put("${XM}",map.get("XM"));
params.put("${XBMC}",map.get("XBMC"));
params.put("${LXDH}",map.get("LXDH"));
params.put("${JG}",map.get("JG"));
params.put("${ZS}",map.get("ZS"));
params.put("${SFZHM}",map.get("SFZHM"));
params.put("${GWMC}",map.get("GWMC"));
try{
String path = CFunc.clobToString((Clob) map.get("HTMB"));
String name = CFunc.ToString(map.get("XM")) + "_" + CFunc.ToString(map.get("HTLX"))+"合同.docx";
String filename = URLEncoder.encode(name, "utf-8");
response.setContentType(request.getServletContext().getMimeType(filename));
wordUtil.getWord(path, params, testList, filename, response);
}catch (Exception e){
e.printStackTrace();
}
POI官网:https://poi.apache.org/
Apache POI Word 简易示例文档:https://my.oschina.net/skymozn/blog/3189732
JACOB:https://sourceforge.net/projects/jacob-project/?source=typ_redirect
参考链接:
https://www.cnblogs.com/duanrantao/p/8682897.html