poi替换生成word 并进行拼接 --XWPFDocument 合并的优化处理

poi替换生成word 并进行拼接--XWPFDocument 合并的优化处理

未优化前

 @Test
    public void downloadElecreicFile(HttpServletResponse response, @Param("exhibitionId") String exhibitionId){
        //从数据库查询数据
        List<Map<String, Object>> data = dataService.getEmsDataByConditions(paramters);
        //获取上传的模板路径
        Attachment attachment = attachmentService.getAttachmentMode(exhibitionId,ELECTRIC_NAME);
        Map<String, Object> parametersMap = new HashMap<String, Object>();
        XWPFTemplate template = null;
        List<XWPFDocument> listTemplate = new ArrayList<>();
        //从服务器获取模板的存储路径
        File file = null;
        String filename = null;
        if (attachment != null) {
            String filePath = attachment.getFilePath();
            String realpath = uploadDir + filePath;
            filename = attachment.getName();
            file = new File(realpath);
        }
        try {
            for (int i = 0; i < data.size(); i++) {
                Map<String, Object> stringObjectMap = data.get(i);
                parametersMap.put("gazw_",stringObjectMap.get("gazw_"));
                parametersMap.put("boothNo",stringObjectMap.get("booth_no"));
                parametersMap.put("companyName",stringObjectMap.get("company_name"));
                parametersMap.put("iWfc_",stringObjectMap.get("iWfc_"));
                parametersMap.put("telephone",stringObjectMap.get("telephone"));
                parametersMap.put("email",stringObjectMap.get("email"));
                parametersMap.put("address",stringObjectMap.get("address"));
                parametersMap.put("JBcl_",stringObjectMap.get("JBcl_"));
                template = XWPFTemplate.compile(new FileInputStream(file)).render(parametersMap);
                XWPFDocument xwpfDocument = template.getXWPFDocument();
                listTemplate.add(xwpfDocument);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }

        //循环合并多个word文档
        try{

            XWPFDocument xmd = listTemplate.get(0);
            for (int i = 0; i < 100; i++) {
                xmd = mergeWord(xmd,listTemplate.get(i+1));
            }
            String fileName = UUID.randomUUID().toString() + ".docx";
            String filePath = "D:/coolgua/wordfile/download" + "/"+fileName;
            File file1 = new File(filePath);
            FileOutputStream fos = new FileOutputStream(file1);
            xmd.write(fos);
            fos.flush();
            fos.close();
            xmd.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
      //两个对象进行追加
	   public  XWPFDocument mergeWord(XWPFDocument document,XWPFDocument doucDocument2) throws Exception {
		    XWPFDocument src1Document =document ;
		    XWPFParagraph p = src1Document.createParagraph();
		    //设置分页符
		    p.setPageBreak(true);
		    CTBody src1Body = src1Document.getDocument().getBody();
		    XWPFDocument src2Document = doucDocument2;
		    CTBody src2Body = src2Document.getDocument().getBody();
//		    XWPFParagraph p2 = src2Document.createParagraph(); 
		    XmlOptions optionsOuter = new XmlOptions();
		    optionsOuter.setSaveOuter();
		    String appendString = src2Body.xmlText(optionsOuter);
		    String srcString = src1Body.xmlText();
		    String prefix = srcString.substring(0,srcString.indexOf(">")+1);
		    String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
		    String sufix = srcString.substring( srcString.lastIndexOf("<") );
		    String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
		    CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
		    src1Body.set(makeBody);
		    return src1Document;
		}

用过循环遍历集合合并

XWPFDocument xmd=list.get(0); //默认获取第一个作为模板
        for (int i=0;i<list.size()-1;i++) {
        	xmd=mergeWord(xmd,list.get(i+1)); //相继合并
		}

缺点:只适用于数据量较小的word,数据量过大时,合并超过两百个word文档时就容易造成堆内存溢出;因为我们看这个循环遍历调用mergeWord()方法,mergeWord里边创造了大量的对象,其中明显最多的就是String对象、CTBody对象,循环调用造成这些对象越来越大,以至于在年轻代的伊甸区放不下,一被创建就直接进入了老年区,最终造成老年区也溢出,报堆内存溢出;还有parametersMap、XWPFTemplate 、listTemplate 这几个存储大数据的对象都放到外边,循环的话不会释放掉它的内存。

优化后1

 /**
     * 对多个word文档进行拼接
     * @param document
     * @param doucDocument2
     * @return
     * @throws Exception
     */
    public XWPFDocument mergeWord(XWPFDocument document, List<XWPFDocument> doucDocument2) throws Exception {
        XWPFDocument src1Document =document ;
        CTBody src1Body = src1Document.getDocument().getBody();
        XmlOptions optionsOuter = new XmlOptions();
        optionsOuter.setSaveOuter();
        String srcString = src1Body.xmlText();

        String prefix = srcString.substring(0,srcString.indexOf(">")+1);
        String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
        String sufix = srcString.substring( srcString.lastIndexOf("<") );

        StringBuilder sb = new StringBuilder();
        sb.append(prefix);
        sb.append(mainPart);

        for (XWPFDocument doc : doucDocument2) {
            CTBody src2Body = doc.getDocument().getBody();
            String appendString = src2Body.xmlText(optionsOuter);
            src2Body = null;
            String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
            appendString = null;
            sb.append(addPart);
            addPart = null;
            doc.close();
        }

        //将两个文档的xml内容进行拼接;拼接文档过大的话,容易造成堆内存溢出
        CTBody makeBody = CTBody.Factory.parse(sb.append(sufix).toString());
        src1Body.set(makeBody);
        return src1Document;
    }

合并

XWPFDocument xmd = null;
xmd = listTemplate.size() > 1 ? mergeWord(listTemplate.get(0), listTemplate.subList(1, listTemplate.size())) : listTemplate.get(0);

这里我们主要使用了StringBuilder来解决循环合并时,String字符串的拼接问题。而且把CTBody这个大对象放到了循环体之外,效果是可以合并带有图像的word文档,大概两千个左右;
缺点:问题是使用poi合并word,始终避免不了合并的两个word文档越来越大时,所占的内存会越来越大。

最终的优化方案

/**
     * 下载电子会刊
     * @param response
     * @param exhibitionId
     */
    @RequestMapping("/downloadElecreicFile")
    public void downloadElecreicFile(HttpServletResponse response,@Param("exhibitionId") String exhibitionId){
        //1:根据条件获取对应表单
        Map<String, String> param = Maps.newHashMap();
        param.put("formType", "24");
        param.put("exhibitionId", exhibitionId);
        Form form =formMapper.getFormByFormType(param).get(0);
        //2:查询审核通过的对应展商的电子会刊表单
        Map<String, Object> paramters = new HashMap<String, Object>();
        paramters.put("exhibitionId", exhibitionId);
        paramters.put("formId", form.getId());
        paramters.put("aduitStatus",AUDIT_STATUS_PASS);
        List<Map<String, Object>> data = dataService.getEmsDataByConditions(paramters);
        //获取上传的模板路径
        Attachment attachment = attachmentService.getAttachmentMode(exhibitionId,ELECTRIC_NAME);
        File file = null;
        String filename = null;
        //从服务器获取模板的存储路径
        if (attachment != null) {
            String filePath = attachment.getFilePath();
            String realpath = uploadDir + filePath;
            filename = attachment.getName();
            file = new File(realpath);
        }
        try {
            Map<String, Object> parametersMap = new HashMap<String, Object>();
            Map<String, Object> dataMap1 = data.get(0);
            replace(parametersMap,dataMap1);
            XWPFTemplate template = XWPFTemplate.compile(new FileInputStream(file)).render(parametersMap);

            XWPFDocument src1Document = null;
            src1Document = template.getXWPFDocument();
            CTBody src1Body = src1Document.getDocument().getBody();
            XmlOptions optionsOuter = new XmlOptions();
            optionsOuter.setSaveOuter();
            String srcString = src1Body.xmlText();

            String prefix = srcString.substring(0,srcString.indexOf(">")+1);
            String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
            String sufix = srcString.substring( srcString.lastIndexOf("<") );

            StringBuilder sb = new StringBuilder();
            sb.append(prefix);
            sb.append(mainPart);
            for (int i = 1; i < data.size(); i++) {
                Map<String, Object> dataMap2 = data.get(i);
                Map<String, Object> parametersMap2 = new HashMap<String, Object>();
                replace(parametersMap2,dataMap2);
                //按占位符替换会刊模板的相应数据
                XWPFTemplate template1 = XWPFTemplate.compile(new FileInputStream(file)).render(parametersMap2);
                XWPFDocument xwpfDocument2 = template1.getXWPFDocument();

                CTBody src2Body = xwpfDocument2.getDocument().getBody();
                String appendString = src2Body.xmlText(optionsOuter);
                src2Body = null;
                String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
                appendString = null;
                sb.append(addPart);
                addPart = null;
                xwpfDocument2.close();
                xwpfDocument2 = null;
            }
            //此对象过大的话,会直接进入老年代,,所以不要放进循环体内。
            CTBody makeBody = CTBody.Factory.parse(sb.append(sufix).toString());
            src1Body.set(makeBody);
            makeBody = null;
            //输出流下载
            response.reset();
            response.setContentType("application/x-msdownload");
            String fileName = new String(filename.getBytes(), "ISO8859-1");
            response.setCharacterEncoding("utf-8");
            response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
            OutputStream os = new BufferedOutputStream(response.getOutputStream());
            src1Document.write(os);
            os.flush();
            os.close();
            src1Document.close();
            src1Document = null;
        }catch (IOException | XmlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 替换模板占位符
     * @param parametersMap
     * @param dataMap
     */
    private void replace(Map<String, Object> parametersMap,Map<String, Object> dataMap){
        parametersMap.put("gazw_",dataMap.get("gazw_"));
        parametersMap.put("boothNo",dataMap.get("booth_no"));
        parametersMap.put("companyName",dataMap.get("company_name"));
        parametersMap.put("iWfc_",dataMap.get("iWfc_"));
        parametersMap.put("telephone",dataMap.get("telephone"));
        parametersMap.put("email",dataMap.get("email"));
        parametersMap.put("address",dataMap.get("address"));
        parametersMap.put("JBcl_",dataMap.get("JBcl_"));
        //从服务器获取图片流
        String tDuI_ = (String) dataMap.get("TDuI_");
        if (tDuI_!=null){
            Attachment attachment2 = attachmentService.getAttachmentById(tDuI_);
            if (attachment2!=null){
                String picFilePath = attachment2.getFilePath();
                String picRealPath = uploadDir + picFilePath;
                String suffix = attachment2.getSuffix();
                File file1 = new File(picRealPath);
                try {
                    parametersMap.put("img",new PictureRenderData(80,100,suffix,new FileInputStream(file1)));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
非常抱歉,之前的回答中有误,现在我会更全面地回答您的问题。 FreeMarker、POIPoi-tl 都可以用于生成 Word 文档,但它们之间存在一些区别,主要体现在以下方面: 1. 生成方式 FreeMarker 是基于模板的生成方式,需要程序员先创建一个 Word 文档模板,然后使用 FreeMarker 引擎将数据模型中的数据填充到模板中,并生成一个完整的 Word 文档。 POIPoi-tl 则是使用代码直接对 Word 文档进行操作,可以读取、写入、修改、创建 Word 文档,并提供了针对 Word 模板操作的 API,例如修改样式、替换变量、插入图片等。 2. 操作方法 FreeMarker 和 Poi-tl 都是使用 API 对 Word 文档进行操作,例如修改样式、替换变量、插入图片等;而 POI 则是使用 POI API 操作 Word 文档。 3. 使用场景 由于 FreeMarker 和 Poi-tl 是基于模板的生成方式,因此适用于需要批量生成相似结构的 Word 文档的场景,例如生成合同、报告、简历等;而 POI 则适用于需要对 Word 文档进行灵活操作的场景,例如读取、修改、创建、删除等。 4. 功能 POIPoi-tl 都提供了丰富的 API,可以对 Word 文档进行各种操作,例如读取、写入、修改、创建、删除、格式化等等;而 FreeMarker 则主要是通过填充模板来生成 Word 文档,并提供了一些基本的操作 API,例如对模板中的变量进行替换、对模板中的样式进行修改、插入图片等等。 5. 性能 由于 FreeMarker 和 Poi-tl 都是基于模板的生成方式,因此生成 Word 文档的性能相对较高,特别是在批量生成 Word 文档时更为明显;而 POI 则需要直接对文档进行操作,因此在生成 Word 文档时性能相对较低。 综上所述,FreeMarker、POIPoi-tl 都是用于生成 Word 文档的工具,它们之间有着不同的生成方式、操作方法、使用场景、功能和性能。选择哪个工具应根据具体的需求和场景进行判断。如果需要批量生成相似结构的 Word 文档,可以选择 FreeMarker 或 Poi-tl;如果需要对 Word 文档进行灵活操作,可以选择 POI

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值