poi-tl-ext + poi-tl实现复杂docx导出(解决导出速度慢问题)

poi-tl-ext + poi-tl实现复杂docx导出(解决导出速度慢问题)

需求描述

首先我们的需求是一个复杂的word导出,其中包含表格,富文本,图片以及文字,篇幅高达50页,我们使用传统的实测导出需要30分钟!!!这样的结果肯定不能接受,所以找出一个解决方案。

过程描述

大家对于富文本的导出,首先是不是都想到了我们的国产软件包spire.doc,简单集成,方便易用,仅需一行代码搞定。

 Document doc = new Document();
 doc.loadFromStream(new ByteArrayInputStream(sbf.getBytes(StandardCharsets.UTF_8)),
 FileFormat.Html, XHTMLValidationType.None);

并且官方也提供了免费版的jar包,总体来说还是不错。但是通过这个方式导出会在性能方面有劣势。并且免费版支持500个段落,500张图片大概在3分钟左右。毕竟白嫖还是不错的,哈哈哈,在小量导出还是有着优势。
在这里插入图片描述
这里是官方链接,感兴趣的同学可以去看看:
https://www.e-iceblue.cn/Introduce/Spire-Doc-JAVA.html

优化思路

框架优化

上面的代码可以看出,虽然可以一行搞定,但是过程不可控,导致无法进行性能优化,所以需要换一个我们可以进行调优的导出工具,经过查找,终于找到一个大佬写的工具,叫做poi-tl-ext ,其中封装了HtmlRenderPolicy策略,对于大部分标签都支持,可以自己更改源码,提升效率!
使用案例:

   //配置
        Configure config = Configure.newBuilder().build();
        AbstractRenderPolicy htmlRenderPolicy = null;
        htmlRenderPolicy = HtmlToWordUtil.createHtmlRenderPolicy(null);

         config.customPolicy("simpleImg", htmlRenderPolicy);
//        // 通用模板  152
        XWPFTemplate template = null;
        template = XWPFTemplate.compile(HtmlToWordUtil.getResourceInputStream("/out_template.docx"), config).render(map);

        try {
            template.writeToFile("D:\\out_1.docx");

            long endTime =System.currentTimeMillis();
            System.out.println("use Time====================>"+(endTime -startTime)/1000);

        } catch (IOException e) {
            e.printStackTrace();
        }

也可以先对模板数据进行绑定策略,再进行导出:

 Configure config = Configure.newBuilder()
                .bind("softwareList", policy)
                .bind("hardwareList",policy)
                .bind("intelligentDevicesList",policy)
                .build();
XWPFTemplate template = XWPFTemplate.compile(file.getParentFile()
.getPath()+"/templates/xxx.docx",config);

绑定好了直接渲染数据:

 template.render(basicInformation);

basicInformation我这里的这个对象其中的属性就是我在word模板的变量,你们替换为自己的即可。

资源优化

其实对于有外部资源引用的,网络以及资源大小也会影响导出效率,所以可以根据这个思路进行解决。
我们这里主要是对于图片进行压缩,在上传图片时,生成一个缩略图上传,添加一个mini后缀,实际使用的路径时压缩后的路径。
下面时压缩图片的方法:

 //定义缩略图的全路径
            String fileSuffix = "image.jpg";
            String contextPath = dirPath + File.separator + fileSuffix;

            //压缩图片
            MultipartFile newFile = null;
            try {
                //自定义宽高压缩(压缩方法很多,可以根据需求更改)
                Thumbnails.of(file.getInputStream()).forceSize(100, 100).toFile(contextPath);

                //压缩完成后将缩略图生成MultipartFile
                FileItem fileItem = createFileItem(contextPath);
                newFile = new CommonsMultipartFile(fileItem);
            } catch (IOException e) {
                e.printStackTrace();
            }

然后只需要在导出的时候进行标签替换就好了,我们是使用的Jsoup:

 String sbf = "<html " +
                "xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" xmlns:m=\"http://schemas.microsoft.com/office/2004/12/omml\" xmlns=\"http://www.w3.org/TR/REC-html40\"" + //将版式从web版式改成页面试图
                ">" +
                "<head>" +
                "<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:TrackMoves>false</w:TrackMoves><w:TrackFormatting/><w:ValidateAgainstSchemas/><w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid><w:IgnoreMixedContent>false</w:IgnoreMixedContent><w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText><w:DoNotPromoteQF/><w:LidThemeOther>EN-US</w:LidThemeOther><w:LidThemeAsian>ZH-CN</w:LidThemeAsian><w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript><w:Compatibility><w:BreakWrappedTables/><w:SnapToGridInCell/><w:WrapTextWithPunct/><w:UseAsianBreakRules/><w:DontGrowAutofit/><w:SplitPgBreakAndParaMark/><w:DontVertAlignCellWithSp/><w:DontBreakConstrainedForcedTables/><w:DontVertAlignInTxbx/><w:Word11KerningPairs/><w:CachedColBalance/><w:UseFELayout/></w:Compatibility><w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel><m:mathPr><m:mathFont m:val=\"Cambria Math\"/><m:brkBin m:val=\"before\"/><m:brkBinSub m:val=\"--\"/><m:smallFrac m:val=\"off\"/><m:dispDef/><m:lMargin m:val=\"0\"/> <m:rMargin m:val=\"0\"/><m:defJc m:val=\"centerGroup\"/><m:wrapIndent m:val=\"1440\"/><m:intLim m:val=\"subSup\"/><m:naryLim m:val=\"undOvr\"/></m:mathPr></w:WordDocument></xml><![endif]-->" +
                "</head>" +
                "<body>" +
                // 富文本内容
                body +
                "</body></html>";
        //加载html文件
        org.jsoup.nodes.Document document = Jsoup.parse(sbf);
        Elements elements = document.getElementsByTag("img");
        elements.attr("width","400px").attr("height","auto");
        elements.forEach(element -> {
            String src = element.attr("src");
            String suffix = FileUtil.getSuffix(src);
            element.attr("src",src.substring(0,src.lastIndexOf("."))+"_mini."+suffix);
        });
        String html = document.outerHtml();
用户体验优化

虽然可以实现分钟级下载,但是为了用户体验,还是做成异步处理。大家无论是使用线程还是定时任务我就不做赘述了。

依赖整理

总所周知,poi的依赖总是会各种冲突,经过我血与泪的教训与实验,整理如下:

	<dependency>
			<groupId>io.github.draco1023</groupId>
			<artifactId>poi-tl-ext</artifactId>
			<version>0.4.5</version>
			<exclusions>
				<exclusion>
					<artifactId>commons-io</artifactId>
					<groupId>commons-io</groupId>
				</exclusion>
				<exclusion>
					<artifactId>poi-tl</artifactId>
					<groupId>com.deepoove</groupId>
				</exclusion>
				<exclusion>
					<artifactId>poi-ooxml-schemas</artifactId>
					<groupId>org.apache.poi</groupId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>com.deepoove</groupId>
			<artifactId>poi-tl</artifactId>
			<version>1.10.0</version>
		</dependency>
		
		<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.14.2</version>
		</dependency>

资源链接

下面是poi-tl-ext大佬的github地址:
github

问题解决

对于表格导出大家可能会遇到没有边框问题,这是因为poi的行为导致,在表格单元格内插入表格时会先将所有内容全部清空。
直接在加载策略的时候设置一个属性就好:

HtmlRenderPolicy htmlPolicy = new HtmlRenderPolicy();
htmlPolicy.getConfig().setShowDefaultTableBorderInTableCell(true);
希望大佬们多多点赞评论!!!!!!!
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值