Java根据Freemarker模板生成Word文件
1。准备模板
模板数据模型
1、将准备好的Word模板文件另存为。xml文件(PS:建议使用WPS来创建Word文件,不建议用Office)
2、将。xml文件重命名为。ftl文件
3、用文本编辑器打开。ftl文件,将内容复制出来,格式化一下,再覆盖原来的内容
(PS:格式化一下是为了方便查找并设置变量占位符,当然设置好模板参数变量以后可以再压缩后再写会。ftl文件)
另外,强烈不建议在word文件中去编辑设置模板变量,因为。docx文件在另存为。xml文件后,原先好好的一个变量可能就被拆开了,建议另存为之后再用文本编辑器打开去编辑。
4、设置模板参数(变量占位符)
2。代码实现
pom。xmllt;?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0https:maven。apache。orgxsdmaven4。0。0。xsdmodelVersion4。0。0modelVersionparentgroupIdorg。springframework。bootgroupIdspringbootstarterparentartifactIdversion2。7。3versionrelativePath!lookupparentfromrepositoryparentgroupIdcom。examplegroupIddemo920artifactIdversion0。0。1SNAPSHOTversionnamedemo920namedescriptiondemo920descriptionpropertiesjava。version1。8java。versionpropertiesdependenciesdependencygroupIdorg。springframework。bootgroupIdspringbootstarterfreemarkerartifactIddependencydependencygroupIdorg。springframework。bootgroupIdspringbootstarterwebartifactIddependencydependencygroupIdcn。hutoolgroupIdhutoolcoreartifactIdversion5。8。7versiondependencydependencygroupIdcom。itextpdfgroupIditextpdfartifactIdversion5。5。13。3versiondependency!dependencygroupIdcom。asposegroupIdasposewordsartifactIdversion22。9versionclassifierjdk17classifierdependencydependencygroupIdorg。projectlombokgroupIdlombokartifactIdoptionaltrueoptionaldependencydependencygroupIdorg。springframework。bootgroupIdspringbootstartertestartifactIdscopetestscopedependencydependenciesbuildpluginsplugingroupIdorg。springframework。bootgroupIdspringbootmavenpluginartifactIdconfigurationexcludesexcludegroupIdorg。projectlombokgroupIdlombokartifactIdexcludeexcludesconfigurationpluginpluginsbuildproject
写个类测试一下packagecom。example。demo920;importcom。example。demo920。domain。LoanReceipt;importfreemarker。template。Configuration;importfreemarker。template。Template;importorg。junit。jupiter。api。Test;importorg。springframework。boot。test。context。SpringBootTest;importjava。io。BufferedWriter;importjava。io。File;importjava。io。FileOutputStream;importjava。io。OutputStreamWriter;importjava。math。BigDecimal;importjava。nio。file。Path;importjava。nio。file。Paths;importjava。time。LocalDateTime;importjava。time。format。DateTimeFormatter;importjava。util。HashMap;importjava。util。Locale;importjava。util。Map;SpringBootTestclassDemo920ApplicationTests{privateDateTimeFormatterDTFDateTimeFormatter。ofPattern(yyyyMMddHHmmss,Locale。CHINA);TestvoidcontextLoads(){}TestvoidtestGenerateWord()throwsException{ConfigurationconfigurationnewConfiguration(Configuration。VERSION2331);configuration。setDefaultEncoding(UTF8);configuration。setClassForTemplateLoading(this。getClass(),templates);Templatetemplateconfiguration。getTemplate(借条。ftl);PathpathPaths。get(tmp,contract);FilefileDirpath。toFile();if(!fileDir。exists()){fileDir。mkdirs();}Stringfilename借条LocalDateTime。now()。format(DTF)。docx;filenamepath。toFile()File。separatorfilename;BufferedWriterwriternewBufferedWriter(newOutputStreamWriter(newFileOutputStream(filename)));template。process(getDataMap(),writer);template。process(getData(),writer);writer。flush();writer。close();}MapString,ObjectgetDataMap(){MapString,ObjectdataMapnewHashMap();dataMap。put(borrowerName,李白);dataMap。put(borrowerIdCard,421302199001012426);dataMap。put(lenderName,杜甫);dataMap。put(amount,100);dataMap。put(amountInWords,壹佰);dataMap。put(startDate,2022年8月15日);dataMap。put(endDate,2022年11月11日);dataMap。put(borrowingMonths,3);dataMap。put(interestRate,1。23);dataMap。put(guarantorName,白居易);dataMap。put(guarantorIdCard,421302199203152412);returndataMap;}LoanReceiptgetData(){LoanReceiptreceiptnewLoanReceipt();receipt。setBorrowerName(狄仁杰);receipt。setBorrowerIdCard(421302198710121234);receipt。setBorrowingMonths(6);receipt。setLenderName(李元芳);receipt。setAmount(newBigDecimal(101));receipt。setAmountInWords(壹佰零壹);receipt。setInterestRate(newBigDecimal(0。6));receipt。setStartDate(2022年1月1日);receipt。setEndDate(2022年6月30日);receipt。setGuarantorName(武则天);receipt。setGuarantorIdCard(421302199101014567);returnreceipt;}}
最主要的是下面两行加载模板Templatetemplateconfiguration。getTemplate(借条。ftl);填充数据template。process(getData(),writer);
数据可以是Map也可以是一个对象
改进一下,将生成文件的操作单独写成一个工具方法packagecom。example。demo920。util;importcn。hutool。core。io。IoUtil;importfreemarker。template。Configuration;importfreemarker。template。Template;importfreemarker。template。TemplateException;importjava。io。;publicclassFreemarkerUtils{生成WordparamtemplateDir模板所在的目录paramtemplateName模板文件名称paramfilename生成的文件(含路径)paramdataModel模板参数数据publicstaticvoidgenerateWord(FiletemplateDir,StringtemplateName,Stringfilename,ObjectdataModel){BufferedWriterwriternull;ConfigurationconfigurationnewConfiguration(Configuration。VERSION2331);configuration。setDefaultEncoding(UTF8);try{configuration。setDirectoryForTemplateLoading(templateDir);Templatetemplateconfiguration。getTemplate(templateName);writernewBufferedWriter(newOutputStreamWriter(newFileOutputStream(filename)));template。process(dataModel,writer);writer。flush();}catch(IOExceptione){thrownewRuntimeException(e);}catch(TemplateExceptione){thrownewRuntimeException(e);}finally{IoUtil。close(writer);}}}
再测试一下packagecom。example。demo920;importcn。hutool。core。io。IoUtil;importcom。example。demo920。util。FreemarkerUtils;importcom。example。demo920。util。PdfUtils;importorg。junit。jupiter。api。Test;importorg。springframework。util。ResourceUtils;importjava。io。;importjava。nio。file。Files;importjava。nio。file。Path;importjava。nio。file。Paths;importjava。time。LocalDateTime;importjava。util。HashMap;importjava。util。Map;publicclassWordTest{1、从文件服务器下载模板文件2、根据业务类型获取需要填充模板的数据3、模板数据再经过处理生成新的文件4、将生成后的文件上传到文件服务器,并返回一个文件ID5、业务可以保存这个文件ID或者文件的路径TestvoidtestGenerateWordV1()throwsException{PathtempPathPaths。get(tmp,contract2);FilepathtempPath。toFile();if(!path。exists()){path。mkdirs();}FiletempFileFiles。createTempFile(tempPath,qiantiao,。docx)。toFile();System。out。println(tempFile。getParent());System。out。println(tempFile。getName());FileOutputStreamfosnewFileOutputStream(tempFile);FiletemplateFileResourceUtils。getFile(classpath:templates借条。ftl);FileInputStreamfisnewFileInputStream(templateFile);IoUtil。copy(fis,fos);Stringfilename借条System。currentTimeMillis()。docx;filenametmpcontractFile。separatorfilename;FreemarkerUtils。generateWord(newFile(tempFile。getParent()),tempFile。getName(),filename,getDataMap());}获取数据MapString,ObjectgetDataMap(){MapString,ObjectdataMapnewHashMap();dataMap。put(borrowerName,李白2);dataMap。put(borrowerIdCard,421302199001012426);dataMap。put(lenderName,杜甫);dataMap。put(amount,100);dataMap。put(amountInWords,壹佰);dataMap。put(startDate,2022年8月15日);dataMap。put(endDate,2022年11月11日);dataMap。put(borrowingMonths,3);dataMap。put(interestRate,1。23);dataMap。put(guarantorName,白居易);dataMap。put(guarantorIdCard,421302199203152412);returndataMap;}TestvoidtestGenerateWord2()throwsException{FiletemplateDirResourceUtils。getFile(ResourceUtils。CLASSPATHURLPREFIXtemplates);StringtemplateName借条。ftl;StringdestFilename借条System。currentTimeMillis()。docx;MapString,ObjectdatagetDataMap();FreemarkerUtils。generateWord(templateDir,templateName,destFilename,data);}}
3。PDF文件加水印
有时候,生成或者从服务器下载的文件是需要加水印的,比如标识这个文件是谁下载的之类的
pdf加水印还是比较方便的,用itext组件可以轻松实现
另外,如果最终需要pdf文件,建议直接生成pdf文件,跳过word转pdf的步骤packagecom。example。demo920。util;importcn。hutool。core。io。IoUtil;importcom。aspose。words。Document;importcom。aspose。words。SaveFormat;importcom。itextpdf。text。BaseColor;importcom。itextpdf。text。DocumentException;importcom。itextpdf。text。Element;importcom。itextpdf。text。Image;importcom。itextpdf。text。pdf。;importjava。io。File;importjava。io。FileInputStream;importjava。io。FileOutputStream;importjava。io。IOException;importjava。time。LocalDateTime;authorchengjianshengdate20220921publicclassPdfUtils{Word转PDFhttps:www。aspose。com注意:Aspose。Words这个组件是收费的,如果购买的话生成的PDF会有水印。可以去找相应的破解版本,但是我感觉完全可以跳过Word直接生成PDF。比如,可以通过Freemarker直接生成PDF,或者利用iText通过模板生成PDFparamsrcparamdestpublicstaticvoidwordToPdf(Stringsrc,Stringdest){FilefilenewFile(src);if(!file。exists()){thrownewRuntimeException(文件不存在);}FileInputStreamfisnull;try{fisnewFileInputStream(file);DocumentwpdnewDocument(fis);wpd。save(dest,SaveFormat。PDF);}catch(Exceptione){thrownewRuntimeException(e);}finally{IoUtil。close(fis);}}加水印paramsrc源文件paramdest目标文件paramtext文字paramimagePath图片地址publicstaticvoidaddWatermark(Stringsrc,Stringdest,Stringtext,StringimagePath){try{待加水印的文件PdfReaderreadernewPdfReader(src);加完水印的文件PdfStamperstampernewPdfStamper(reader,newFileOutputStream(dest));字体BaseFontbaseFontBaseFont。createFont(STSongLight,UniGBUCS2H,BaseFont。NOTEMBEDDED);透明度PdfGStategsnewPdfGState();gs。setFillOpacity(0。4f);PDF文件总页数inttotalreader。getNumberOfPages()1;循环对每一页都加水印PdfContentBytecontent;for(inti1;itotal;i){水印在文本之上contentstamper。getOverContent(i);content。setGState(gs);if(null!imagePath){ImageimageImage。getInstance(imagePath);image。setAbsolutePosition(150,150);image。scaleToFit(300,300);content。addImage(image);for(intx0;x700;xx300){for(inty0;y900;yy200){image。setAbsolutePosition(x50,y50);image。scaleToFit(100,100);content。addImage(image);}}}if(null!text){content。beginText();content。setColorFill(BaseColor。RED);content。setFontAndSize(baseFont,20);content。showTextAligned(Element。ALIGNCENTER,text,50,50,45);for(intx0;x700;xx300){for(inty0;y900;yy200){水印内容和水印位置content。showTextAligned(Element。ALIGNCENTER,哈哈哈哈哈,x20,y10,30);content。showTextAligned(Element。ALIGNCENTER,LocalDateTime。now()。toString(),x,y,30);}}content。endText();}}stamper。close();reader。close();}catch(IOExceptione){thrownewRuntimeException(e);}catch(DocumentExceptione){thrownewRuntimeException(e);}}}
跑一下TestvoidtestWatermark(){Stringsrc2D:借条2。pdf;Stringdest2D:借条3。pdf;StringimagePathD:1。jpg;PdfUtils。addWatermark(src2,dest2,哈哈哈哈哈,imagePath);}
加完水印后效果如图
最后,示例项目结构如图
原文链接:https:www。cnblogs。comcjsblogp16715294。html?utmsourcetuicoolutmmediumreferral
西安古镇排名是怎样的?哪里最推荐去?要搞清楚西安古镇的排名,首先要明确西安有哪些古镇,然后才能考虑排名的问题。一西安有哪些古镇呢?1啥是古镇古镇应该包括两方面的含义,首先要古,至少得有上百年以上才能算古吧,如果没有超
房地产进入寒冬,面临失业,工程人改行做什么好?1我倒不认为房地产业进入了寒冬了,前两天还有几个一线城市的相关领导出来说话,要大力新建廉租房,北上广深今后五年的廉租房建设都要达到一个新的高峰单说上海一市,预计在十四五末,将建设6
在德阳工作5000元一月是什么水平?不晓得是到手5000还是应得5000,如果是前者应该超过了7080的德阳人了。德阳是个重工业城市,城里以二重东电东汽美丰等企业最为出名,工人应得工资超过5000,可他们光是公积金就
如何优雅的发朋友圈旅行照片?旅行中能拍摄的实在太多了,如何优雅的发朋友圈旅行照片呢那么一步一步来,首先是交通工具,不管是自驾游开车还是坐高铁火车飞机大巴,其实这些都是非常好拍摄地点,借助丰富的自然光进行拍摄。
我在农商银行工作,已年过50,应该坚持还是放弃?年过50还考虑什么放弃?按照国务院关于工人退休退职的暂行办法文件所规定的退休年龄,目前我国男性法定退休年龄为60周岁,女性法定退休年龄为50周岁,既然你都已过50周岁了,那么距离退
去丰宁坝上自由行要带多少钱够了?作为草原旅游区酒店人我可以透露点秘密给你哦。太深入的不说,毕竟我在别人地盘做生意。从以下几个方面给你讲吧。1,住宿淡季是30100的,住宿看两点,1是否24小时热水器,热水器不是太
你见过的最不孝顺的子女是什么样的?75岁的老父亲睡露天猪圈,唯一的心愿是吃顿肉,44岁的女儿却说吃肉可以猪圈还得继续住。而老人9岁外孙女的一个举动,让女儿无地自容。2016年的某天,成都的贺先生回老家办事时,偶然经
养生站桩每天什么时间最好?养生站桩,养的是精气神,站桩养精养的是我们的丹田之精,最好是阳气旺盛的时候。遵循天道,太阳升起到太阳最高的时间点都可以,但是盛夏不要选择中午,这个时候阳气太盛,站桩反而伤身。站桩的
吴梦洁被吹的神乎其神,是否有能力担任国家女排主力?江苏女排的吴梦洁,也被球迷们誉为小朱婷。与同年龄段的朱婷李盈莹相比,朱婷在19岁已经荣膺世锦赛最佳主攻,成为国家队的首发主力李盈莹在19岁之前就已经拿到了联赛的得分王。吴梦洁在排超
异地恋的情侣好不容易见面了,应该如何安排一天的行程?关起门来做不可描述的事,然后睡觉,睡醒叫外卖,吃饱喝足继续做不可描述的事,然后一天就过完了,哈哈哈两个字自信,自信就好,不要担心自己会做不好。我跟我对象也是异地恋,要对明天充满信心
早上起床后,空腹锻炼俯卧撑有什么好处和坏处?锻炼是一种维持健康的行为,但是任何一种行为,都要讲究时度人,不然只会适得其反!曾经接诊过一位40多岁的女病人,为了减肥,她每天坚持晨练,每次都是一起床就出去慢跑,突然有一天感觉心慌