Java實現線上模版預覽和下載
阿新 • • 發佈:2019-01-21
*** 所有目錄引數皆為專案相對路徑
一、基礎準備
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;
}