*** 所有目录参数皆为项目相对路径
一、基础准备
1.1 Maven构建项目
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.xhtml</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.12</version>
</dependency>
1.2 获取资源绝对路径
public static String getRealPath(String dirPath) {
//利用资源加载器获取资源URL
String path = Class.class.getResource("/").getPath();
return path + dirPath;
}
1.3 获取目录下所有文件
/**
* 获取目录下所有文件(默认不存在目录下同时存在文件和文件夹)
*
* @param targetPath
* @return
*/
public static List<File> getFiles(String targetPath) {
String realPath = getRealPath(targetPath);
List<File> list = new ArrayList<>();
if (StringUtils.isBlank(realPath)) {
return null;
}
File file = new File(realPath);
if (file.isFile()) {
list.add(file);
return list;
} else {
File[] files = file.listFiles();
return files == null ? null : Arrays.asList(files);
}
}
1.4 创建FTL模版
1.5 获取填充数据
/**
* 注意dataMap里存放的数据Key值要与模板中的参数相对应
*
* @param target
*/
private static Map<String, Object> getData(Object target) {
Map<String, Object> map = new HashMap<>();
Class<?> beanClass = target.getClass();
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
String key = field.getName();
Object value = null;
try {
value = field.get(target);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if(value == null) {
value = "";
}
map.put(key, value);
}
return map;
}
1.6 向目标文件写入内容
private static void writeFile(String content, String path) {
FileOutputStream fos = null;
BufferedWriter bw = null;
try {
File file = new File(path);
if (!file.exists()) {
boolean newFile = file.createNewFile();
}
fos = new FileOutputStream(file);
bw = new BufferedWriter(new OutputStreamWriter(fos));
bw.write(content);
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null) {
bw.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
二、根据用户输入填充ftl生成word文档
/**
* 生成word文件
*
* @param templatePath
* @param fileName
* @param targetFilePath
* @param target
*/
public static void createDoc(String templatePath, String fileName, String targetFilePath,Object target) {
//要填入模本的数据文件
Map<String, Object> data = getData(target);
//设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载,
//这里我们的模板是放在com.phq.document.template包下面
configuration.setClassForTemplateLoading(FileUtils.class, templatePath);
Template t = null;
try {
//test.ftl为要装载的模板
t = configuration.getTemplate(fileName);
} catch (IOException e) {
e.printStackTrace();
}
if(t == null) {
return;
}
//输出文档路径及名称
File outFile = new File(getRealPath(targetFilePath));
Writer out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
try {
t.process(data, out);
} catch (TemplateException | IOException e) {
e.printStackTrace();
}
}
三、word文档转化为HTML实现在线预览
/**
* 将word转换成html
* 支持 .doc and .docx
*
* @param filePath word路径
* @param outPutFilePath html存储路径 形式如"/file/"
* @param newFileName html名
* @throws TransformerException
* @throws IOException
* @throws ParserConfigurationException
*/
public static void wordToHtml(String filePath, String outPutFilePath, String newFileName)
throws TransformerException, IOException, ParserConfigurationException {
String substring = filePath.substring(filePath.lastIndexOf(".") + 1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
outPutFilePath = getRealPath(outPutFilePath);
filePath = getRealPath(filePath);
//防止错误输入
if(!outPutFilePath.endsWith("/") && !outPutFilePath.endsWith("\\")) {
outPutFilePath = outPutFilePath + "/";
}
/*
* word2007和word2003的构建方式不同,
* 前者的构建方式是xml,后者的构建方式是dom树。
* 文件的后缀也不同,前者后缀为.docx,后者后缀为.doc
* 相应的,apache.poi提供了不同的实现类。
*/
if ("docx".equals(substring)) {
InputStream inputStream = new FileInputStream(new File(filePath));
XWPFDocument document = new XWPFDocument(inputStream);
//step 2 : prepare XHTML options
final String imageUrl = "";
XHTMLOptions options = XHTMLOptions.create();
options.setExtractor(new FileImageExtractor(new File(outPutFilePath + imageUrl)));
options.setIgnoreStylesIfUnused(false);
options.setFragment(true);
// @Override 重写的方法,加上这个报错,你看看是啥问题
options.URIResolver(uri -> imageUrl + uri);
//step 3 : convert XWPFDocument to XHTML
XHTMLConverter.getInstance().convert(document, out, options);
} else {
HWPFDocument wordDocument = new HWPFDocument(new FileInputStream(filePath));
WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
DocumentBuilderFactory.newInstance().newDocumentBuilder()
.newDocument());
wordToHtmlConverter.setPicturesManager((content, pictureType, suggestedName, widthInches, heightInches) -> suggestedName);
wordToHtmlConverter.processDocument(wordDocument);
//save pictures
List pics = wordDocument.getPicturesTable().getAllPictures();
if (pics != null) {
for (int i = 0; i < pics.size(); i++) {
Picture pic = (Picture) pics.get(i);
System.out.println();
try {
pic.writeImageContent(new FileOutputStream(outPutFilePath
+ pic.suggestFullFileName()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
Document htmlDocument = wordToHtmlConverter.getDocument();
DOMSource domSource = new DOMSource(htmlDocument);
StreamResult streamResult = new StreamResult(out);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer serializer = tf.newTransformer();
serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
serializer.setOutputProperty(OutputKeys.METHOD, "html");
serializer.transform(domSource, streamResult);
}
out.close();
writeFile(new String(out.toByteArray()), outPutFilePath + newFileName);
}
四、解决Ftl模板生成doc文档的不足
基于ftl模版生成的doc文档,其实际类型还是xml格式,在转化成HTML格式时失败,于是整合网上各类资料,以及自己的努力探索,得出以下方法来实现基于docx模板生成docx文档。
/**
* 实现word文档填充
*
* @param inFile
* word模板路径和名称
* @param bean
* 待填充的数据,从数据库读取
* @throws IOException
*/
public static void fillDocx(String inFile,String outFile, Object bean) throws IOException {
Map<String ,Object> data = getData(bean);
InputStream is = new FileInputStream(FileUtils.getPath(inFile));
XWPFDocument document;
try {
document = new XWPFDocument(OPCPackage.open(is));
// 替换段落里面的变量
replaceInPara(document, data);
OutputStream os = new FileOutputStream(FileUtils.getPath(outFile));
document.write(os);
os.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 替换段落里面的变量
*
* @param doc
* 要替换的文档
* @param params
* 参数
*/
private static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
XWPFParagraph para;
while (iterator.hasNext()) {
para = iterator.next();
replaceInPara(para, params);
}
}
/**
* 替换段落里面的变量
*
* @param para
* 要替换的段落
* @param params
* 参数
*/
private static void replaceInPara(XWPFParagraph para, Map<String, Object> params) {
List<XWPFRun> runs;
Matcher matcher;
String runText = "";
if (pattern.matcher(para.getParagraphText()).find()) {
runs = para.getRuns();
if (runs.size() > 0) {
int j = runs.size();
for (int i = 0; i < j; i++) {
XWPFRun run = runs.get(0);
String i1 = run.toString();
runText += i1;
para.removeRun(0);
}
}
matcher = pattern.matcher(runText);
if (matcher.find()) {
while ((matcher = pattern.matcher(runText)).find()) {
String value = String.valueOf(params.get(matcher.group(1)));
if (value.equals("null")) {
value = "";
}
runText = matcher.replaceFirst(value);
}
// 直接调用XWPFRun的setText()方法设置文本时,在底层会重新创建一个XWPFRun,把文本附加在当前文本后面,
// 所以我们不能直接设值,需要先删除当前run,然后再自己手动插入一个新的run。
para.insertNewRun(0).setText(runText);
}
}
}
/**
* 将word转换成html
* 支持 .doc and .docx
*
* @param filePath word路径
* @param outPutFilePath html存储路径 形式如"/file/"
* @param newFileName html名
* @throws TransformerException
* @throws IOException
* @throws ParserConfigurationException
*/
public static void wordToHtml(String filePath, String outPutFilePath, String newFileName)
throws TransformerException, IOException, ParserConfigurationException {
String substring = filePath.substring(filePath.lastIndexOf(".") + 1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
outPutFilePath = FileUtils.getPath(outPutFilePath);
filePath = FileUtils.getPath(filePath);
//防止错误输入
if (!outPutFilePath.endsWith("/") && !outPutFilePath.endsWith("\\")) {
outPutFilePath = outPutFilePath + "/";
}
/*
* word2007和word2003的构建方式不同,
* 前者的构建方式是xml,后者的构建方式是dom树。
* 文件的后缀也不同,前者后缀为.docx,后者后缀为.doc
* 相应的,apache.poi提供了不同的实现类。
*/
if ("docx".equals(substring)) {
InputStream inputStream = new FileInputStream(new File(filePath));
XWPFDocument document = new XWPFDocument(inputStream);
//step 2 : prepare XHTML options
final String imageUrl = "";
XHTMLOptions options = XHTMLOptions.create();
options.setExtractor(new FileImageExtractor(new File(outPutFilePath + imageUrl)));
options.setIgnoreStylesIfUnused(false);
options.setFragment(true);
// @Override 重写的方法,加上这个报错,你看看是啥问题
options.URIResolver(uri -> imageUrl + uri);
//step 3 : convert XWPFDocument to XHTML
XHTMLConverter.getInstance().convert(document, out, options);
} else {
HWPFDocument wordDocument = new HWPFDocument(new FileInputStream(filePath));
WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
DocumentBuilderFactory.newInstance().newDocumentBuilder()
.newDocument());
wordToHtmlConverter.setPicturesManager((content, pictureType, suggestedName, widthInches, heightInches) -> suggestedName);
wordToHtmlConverter.processDocument(wordDocument);
//save pictures
List<Picture> pics = wordDocument.getPicturesTable().getAllPictures();
if (pics != null) {
for (int i = 0; i < pics.size(); i++) {
Picture pic = pics.get(i);
System.out.println();
try {
pic.writeImageContent(new FileOutputStream(outPutFilePath
+ pic.suggestFullFileName()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
Document htmlDocument = wordToHtmlConverter.getDocument();
DOMSource domSource = new DOMSource(htmlDocument);
StreamResult streamResult = new StreamResult(out);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer serializer = tf.newTransformer();
serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
serializer.setOutputProperty(OutputKeys.METHOD, "html");
serializer.transform(domSource, streamResult);
}
out.close();
writeFile(new String(out.toByteArray()), outPutFilePath + newFileName);
}
/**
* 注意dataMap里存放的数据Key值要与模板中的参数相对应
*
* @param target
*/
private static Map<String, Object> getData(Object target) {
if (target instanceof Map) {
return (Map<String, Object>) target;
}
Map<String, Object> map = new HashMap<>();
Class<?> beanClass = target.getClass();
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
String key = field.getName();
Object value = null;
try {
value = field.get(target);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
map.put(key, value);
}
return map;
}