Java多種方式動(dòng)態(tài)生成doc文檔
本來(lái)是要在Android端生成doc的(這需求...),最后方法沒(méi)有好的方法能夠在Android上做到完美,最后還是只能搬遷到服務(wù)器。不浪費(fèi),還是記錄下各框架不支持Android的原因以及他們的特點(diǎn)。Java相關(guān)的這類(lèi)框架還是很多的,有幾個(gè)還不錯(cuò),可惜要么不支持Android,要么要收費(fèi)還價(jià)格不低。
經(jīng)過(guò)親自測(cè)試,Android不支持Java的awt很多包不能直接在Android上用,F(xiàn)reeMarker挺不錯(cuò)的,能生成復(fù)雜漂亮的doc,可惜不支持Android。用POI在Android上能運(yùn)行,但是一路因?yàn)榘姹?,格式等走了很多坑,用WFS打開(kāi)還是亂碼。Jword、Aspose.word能完美支持,Jword試用期只有30天兩者收費(fèi)都不菲。itext沒(méi)有測(cè)試,不過(guò)聽(tīng)說(shuō)也不支持Android。
方法一:freemarker
該方法需要先手動(dòng)創(chuàng)建一個(gè)doc模板(圖片記得使用占位符),并保存為xml文件。通過(guò)動(dòng)態(tài)替換特定標(biāo)簽${}中的內(nèi)容生成。example:

先上效果圖:

public class DocUtil {
public Configuration configure=null;
public DocUtil(){
configure=new Configuration(Configuration.VERSION_2_3_22);
configure.setDefaultEncoding("utf-8");
}
/**
* 根據(jù)Doc模板生成word文件
* @param dataMap 需要填入模板的數(shù)據(jù)
* @param downloadType 文件名稱(chēng)
* @param savePath 保存路徑
*/
public void createDoc(Map<String,Object> dataMap,String downloadType,String savePath){
try {
//加載需要裝填的模板
Template template=null;
//設(shè)置模板裝置方法和路徑,F(xiàn)reeMarker支持多種模板裝載方法??梢灾豷ervlet,classpath,數(shù)據(jù)庫(kù)裝載。
//加載模板文件,放在testDoc下
configure.setClassForTemplateLoading(this.getClass(), "/testDoc");
//設(shè)置對(duì)象包裝器
// configure.setObjectWrapper(new DefaultObjectWrapper());
//設(shè)置異常處理器
configure.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
//定義Template對(duì)象,注意模板類(lèi)型名字與downloadType要一致
template=configure.getTemplate(downloadType+".xml");
File outFile=new File(savePath);
Writer out=null;
out=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"));
template.process(dataMap, out);
out.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
}
public String getImageStr(String imgFile){
InputStream in=null;
byte[] data=null;
try {
in=new FileInputStream(imgFile);
data=new byte[in.available()];
in.read(data);
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
BASE64Encoder encoder=new BASE64Encoder();
return encoder.encode(data);
}
}
public class TestDoc {
public static void main(String[] args) {
DocUtil docUtil=new DocUtil();
Map<String, Object> dataMap=new HashMap<String, Object>();
dataMap.put("name", "Joanna");
dataMap.put("examNum", "111111111111");
dataMap.put("IDCard", "222222222222222222");
dataMap.put("carModel", "C1");
dataMap.put("drivingSchool", "測(cè)試駕校");
dataMap.put("busyType", "初次申領(lǐng)");
dataMap.put("examDate", "2016-03-10");
dataMap.put("orderCount", "第1次");
dataMap.put("userImg1", docUtil.getImageStr("D:\\Img\\userImg1.png"));
dataMap.put("userImg2", docUtil.getImageStr("D:\\Img\\userImg2.png"));
dataMap.put("firstExamTime", "12:41:17-12:44:38");
dataMap.put("firstExamScores", "0分,不及格");
dataMap.put("firstDeductItem", "12:44:15 20102 1號(hào)倒車(chē)入庫(kù),車(chē)身出線(xiàn) 扣100分");
dataMap.put("firstPic1", docUtil.getImageStr("D:\\Img\\firstPic1.png"));
dataMap.put("firstPic2", docUtil.getImageStr("D:\\Img\\firstPic2.png"));
dataMap.put("firstPic3", docUtil.getImageStr("D:\\Img\\firstPic3.png"));
dataMap.put("secondExamTime", "12:46:50-13:05:37");
dataMap.put("secondExamScores", "90分,通過(guò)");
dataMap.put("secondDeductItem", "");
dataMap.put("secondPic1", docUtil.getImageStr("D:\\Img\\secondPic1.png"));
dataMap.put("secondPic2", docUtil.getImageStr("D:\\Img\\secondPic2.png"));
dataMap.put("secondPic3", docUtil.getImageStr("D:\\Img\\secondPic3.png"));
docUtil.createDoc(dataMap, "baseDoc", "D:\\yanqiong.doc");
}
}
xml文件太長(zhǎng),就不貼了...
最后附上Android不能使用的原因:http://stackoverflow.com/questions/25929542/use-freemarker-library-in-android
補(bǔ)充關(guān)于動(dòng)態(tài)顯示list以及換行的問(wèn)題
需求明確到:在上面的扣分項(xiàng)中,如果我有幾條扣分項(xiàng),我希望每顯示一條換行。
直接在要顯示的內(nèi)容上加換行符,并沒(méi)有什么效果,起不到換行的作用。
其中在加ftl標(biāo)簽時(shí),如<#list></list>,就會(huì)出現(xiàn)一些問(wèn)題,在xml中并不識(shí)別,導(dǎo)致項(xiàng)目不能運(yùn)行。
解決:
在需要顯示多條扣分項(xiàng)的位置加,并加換行符:
<#list firstDeductItem as firstItem>
<w:t>${firstItem}</w:t><w:br/>
</#list>
TestDoc.java中改為:
List<String> Strs=new ArrayList<String>();
Strs.add("1111111111111111111");
Strs.add("222222222222222");
Strs.add("333333333333333");
dataMap.put("firstDeductItem", Strs);
DocUtil.java中改為:
//定義Template對(duì)象,注意模板類(lèi)型名字與downloadType要一致
template=configure.getTemplate(downloadType+".ftl");此時(shí)xml文件會(huì)報(bào)錯(cuò),當(dāng)然也不能編譯運(yùn)行項(xiàng)目,需要將.xml文件改為.ftl文件保存。再編譯運(yùn)行,效果圖:

方法二:POI
用這個(gè)方法遇到了很多版本問(wèn)題,這里是基于POI3.7+Word2007的,測(cè)試能夠完美運(yùn)行。
你需要用Word2007手動(dòng)生成文檔模板(用其他的生成會(huì)報(bào)錯(cuò):無(wú)法打開(kāi)文件),并用${}替換需要?jiǎng)討B(tài)更新的內(nèi)容,與上面類(lèi)似,但是不需要你保存為xml文檔格式了。
/**
* 自定義XWPFDocument,并重寫(xiě)createPicture()方法
* @author Joanna.Yan
*
*/
public class CustomXWPFDocument extends XWPFDocument{
public CustomXWPFDocument(InputStream in) throws IOException{
super(in);
}
public CustomXWPFDocument(){
super();
}
public CustomXWPFDocument(OPCPackage pkg) throws IOException{
super(pkg);
}
public void createPicture(int id,int width,int height,XWPFParagraph paragraph){
final int EMU=9525;
width *=EMU;
height *=EMU;
String blipId=((POIXMLDocumentPart) getAllPictures().get(id)).getPackageRelationship().getId();
CTInline inline=paragraph.createRun().getCTR().addNewDrawing().addNewInline();
String picXml=""
+ "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
+ " <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
+ " <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
+ " <pic:nvPicPr>" + " <pic:cNvPr id=\""
+ id
+ "\" name=\"Generated\"/>"
+ " <pic:cNvPicPr/>"
+ " </pic:nvPicPr>"
+ " <pic:blipFill>"
+ " <a:blip r:embed=\""
+ blipId
+ "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"
+ " <a:stretch>"
+ " <a:fillRect/>"
+ " </a:stretch>"
+ " </pic:blipFill>"
+ " <pic:spPr>"
+ " <a:xfrm>"
+ " <a:off x=\"0\" y=\"0\"/>"
+ " <a:ext cx=\""
+ width
+ "\" cy=\""
+ height
+ "\"/>"
+ " </a:xfrm>"
+ " <a:prstGeom prst=\"rect\">"
+ " <a:avLst/>"
+ " </a:prstGeom>"
+ " </pic:spPr>"
+ " </pic:pic>"
+ " </a:graphicData>" + "</a:graphic>";
inline.addNewGraphic().addNewGraphicData();
XmlToken xmlToken=null;
try {
xmlToken=XmlToken.Factory.parse(picXml);
} catch (XmlException e) {
e.printStackTrace();
}
inline.set(xmlToken);
inline.setDistT(0);
inline.setDistB(0);
inline.setDistL(0);
inline.setDistR(0);
CTPositiveSize2D extent=inline.addNewExtent();
extent.setCx(width);
extent.setCy(height);
CTNonVisualDrawingProps docPr=inline.addNewDocPr();
docPr.setId(id);
docPr.setName("圖片"+id);
docPr.setDescr("測(cè)試");
}
}
/**
* 適用于word 2007
* poi版本 3.7
* @author Joanna.Yan
*
*/
public class WordUtil {
public static CustomXWPFDocument generateWord(Map<String, Object> param,String template){
CustomXWPFDocument doc=null;
try {
OPCPackage pack=POIXMLDocument.openPackage(template);
doc=new CustomXWPFDocument(pack);
if(param!=null&¶m.size()>0){
//處理段落
List<XWPFParagraph> paragraphList = doc.getParagraphs();
processParagraphs(paragraphList, param, doc);
//處理表格
Iterator<XWPFTable> it = doc.getTablesIterator();
while(it.hasNext()){
XWPFTable table = it.next();
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
processParagraphs(paragraphListTable, param, doc);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return doc;
}
/**
* 處理段落
* @param paragraphList
* @param param
* @param doc
*/
public static void processParagraphs(List<XWPFParagraph> paragraphList,Map<String, Object> param,CustomXWPFDocument doc){
if(paragraphList!=null&¶graphList.size()>0){
for (XWPFParagraph paragraph : paragraphList) {
List<XWPFRun> runs=paragraph.getRuns();
for (XWPFRun run : runs) {
String text=run.getText(0);
if(text!=null){
boolean isSetText=false;
for (Entry<String, Object> entry : param.entrySet()) {
String key=entry.getKey();
if(text.indexOf(key)!=-1){
isSetText=true;
Object value=entry.getValue();
if(value instanceof String){//文本替換
text=text.replace(key, value.toString());
}else if(value instanceof Map){//圖片替換
text=text.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,paragraph);
} catch (InvalidFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
if(isSetText){
run.setText(text, 0);
}
}
}
}
}
}
/**
* 根據(jù)圖片類(lèi)型獲取對(duì)應(yīng)的圖片類(lèi)型代碼
* @param picType
* @return
*/
public 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;
}
}
public class TestPoi {
public static void main(String[] args) throws IOException {
Map<String, Object> param=new HashMap<String, Object>();
param.put("${name}", "Joanna.Yan");
param.put("${examNum}", "000000000001");
param.put("${IDCard}", "111111111111111111");
param.put("${carModel}", "C1");
CustomXWPFDocument doc=WordUtil.generateWord(param, "D:\\joanna.docx");
FileOutputStream fopts = new FileOutputStream("D:\\yan.docx");
doc.write(fopts);
fopts.close();
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Python文檔生成工具pydoc使用介紹
- eclipse中自動(dòng)生成javadoc文檔的方法
- PHP使用DOMDocument類(lèi)生成HTML實(shí)例(包含常見(jiàn)標(biāo)簽元素)
- PhpDocumentor 2安裝以及生成API文檔的方法
- 探討:如何使用PhpDocumentor生成文檔
- Java讀取Excel文件內(nèi)容的簡(jiǎn)單實(shí)例
- Java數(shù)據(jù)導(dǎo)出功能之導(dǎo)出Excel文件實(shí)例
- java使用poi讀取ppt文件和poi讀取excel、word示例
- java使用poi讀取excel內(nèi)容方法實(shí)例
- Java數(shù)據(jù)導(dǎo)入功能之讀取Excel文件實(shí)例
相關(guān)文章
詳解Java中的四種引用類(lèi)型(強(qiáng)軟弱虛)
Java中的引用類(lèi)型主要分為四種,分別是強(qiáng)引用、軟引用、弱引用和虛引用,這篇文章主要為大家詳細(xì)介紹了四者的使用與區(qū)別,需要的小伙伴可以參考下2023-10-10
logback的AsyncAppender高效日志處理方式源碼解析
這篇文章主要為大家介紹了logback的AsyncAppender高效日志處理方式源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
java使用RestTemplate封裝post請(qǐng)求方式
這篇文章主要介紹了java使用RestTemplate封裝post請(qǐng)求方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
SpringBoot獲取不到用戶(hù)真實(shí)IP的解決方法
最近遇到個(gè)問(wèn)題,項(xiàng)目部署后發(fā)現(xiàn)服務(wù)端無(wú)法獲取到客戶(hù)端真實(shí)的IP地址,本文就來(lái)介紹一下這個(gè)問(wèn)題的解決方法,感興趣的可以了解一下2023-08-08
修改Android應(yīng)用的樣式的一些關(guān)鍵點(diǎn)解析
這篇文章主要介紹了修改Android應(yīng)用的樣式的一些關(guān)鍵點(diǎn),即對(duì)影響外觀(guān)的theme跟style的相關(guān)修改,需要的朋友可以參考下2015-12-12
JMagick實(shí)現(xiàn)基本圖像處理的類(lèi)實(shí)例
這篇文章主要介紹了JMagick實(shí)現(xiàn)基本圖像處理的類(lèi),實(shí)例分析了java圖像處理的相關(guān)技巧,需要的朋友可以參考下2015-06-06

