1。准备模板 模板数据模型 1、将准备好的Word模板文件另存为。xml文件(PS:建议使用WPS来创建Word文件,不建议用Office) 2、将。xml文件重命名为。ftl文件 3、用文本编辑器打开。ftl文件,将内容复制出来,格式化一下,再覆盖原来的内容 (PS:格式化一下是为了方便查找并设置变量占位符,当然设置好模板参数变量以后可以再压缩后再写会。ftl文件) 另外,强烈不建议在word文件中去编辑设置模板变量,因为。docx文件在另存为。xml文件后,原先好好的一个变量可能就被拆开了,建议另存为之后再用文本编辑器打开去编辑。 4、设置模板参数(变量占位符) 2。代码实现 pom。?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。LoanRimportfreemarker。template。Cimportfreemarker。template。Timportorg。junit。jupiter。api。Timportorg。springframework。boot。test。context。SpringBootTimportjava。io。BufferedWimportjava。io。Fimportjava。io。FileOutputSimportjava。io。OutputStreamWimportjava。math。BigDimportjava。nio。file。Pimportjava。nio。file。Pimportjava。time。LocalDateTimportjava。time。format。DateTimeFimportjava。util。HashMimportjava。util。Limportjava。util。MSpringBootTestclassDemo920ApplicationTests{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)。filenamepath。toFile()File。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);returndataM}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);}} 最主要的是下面两行加载模板Templatetemplateconfiguration。getTemplate(借条。ftl);填充数据template。process(getData(),writer); 数据可以是Map也可以是一个对象 改进一下,将生成文件的操作单独写成一个工具方法packagecom。example。demo920。importcn。hutool。core。io。IoUimportfreemarker。template。Cimportfreemarker。template。Timportfreemarker。template。TemplateEimportjava。io。;publicclassFreemarkerUtils{生成WordparamtemplateDir模板所在的目录paramtemplateName模板文件名称paramfilename生成的文件(含路径)paramdataModel模板参数数据publicstaticvoidgenerateWord(FiletemplateDir,StringtemplateName,Stringfilename,ObjectdataModel){BufferedWConfigurationconfigurationnewConfiguration(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。IoUimportcom。example。demo920。util。FreemarkerUimportcom。example。demo920。util。PdfUimportorg。junit。jupiter。api。Timportorg。springframework。util。ResourceUimportjava。io。;importjava。nio。file。Fimportjava。nio。file。Pimportjava。nio。file。Pimportjava。time。LocalDateTimportjava。util。HashMimportjava。util。MpublicclassWordTest{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()。filenametmpcontractFile。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);returndataM}TestvoidtestGenerateWord2()throwsException{FiletemplateDirResourceUtils。getFile(ResourceUtils。CLASSPATHURLPREFIXtemplates);StringtemplateName借条。StringdestFilename借条System。currentTimeMillis()。MapString,ObjectdatagetDataMap();FreemarkerUtils。generateWord(templateDir,templateName,destFilename,data);}} 3。PDF文件加水印 有时候,生成或者从服务器下载的文件是需要加水印的,比如标识这个文件是谁下载的之类的 pdf加水印还是比较方便的,用itext组件可以轻松实现 另外,如果最终需要pdf文件,建议直接生成pdf文件,跳过word转pdf的步骤packagecom。example。demo920。importcn。hutool。core。io。IoUimportcom。aspose。words。Dimportcom。aspose。words。SaveFimportcom。itextpdf。text。BaseCimportcom。itextpdf。text。DocumentEimportcom。itextpdf。text。Eimportcom。itextpdf。text。Iimportcom。itextpdf。text。pdf。;importjava。io。Fimportjava。io。FileInputSimportjava。io。FileOutputSimportjava。io。IOEimportjava。time。LocalDateTauthorchengjianshengdate20220921publicclassPdfUtils{Word转PDFhttps:www。aspose。com注意:Aspose。Words这个组件是收费的,如果购买的话生成的PDF会有水印。可以去找相应的破解版本,但是我感觉完全可以跳过Word直接生成PDF。比如,可以通过Freemarker直接生成PDF,或者利用iText通过模板生成PDFparamsrcparamdestpublicstaticvoidwordToPdf(Stringsrc,Stringdest){FilefilenewFile(src);if(!file。exists()){thrownewRuntimeException(文件不存在);}FileInputStry{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;循环对每一页都加水印PdfContentBfor(inti1;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。Stringdest2D:借条3。StringimagePathD:1。PdfUtils。addWatermark(src2,dest2,哈哈哈哈哈,imagePath);} 加完水印后效果如图 最后,示例项目结构如图 原文链接:https:www。cnblogs。comcjsblogp16715294。html?utmsourcetuicoolutmmediumreferral