使用poi-tl根据word模板生成word文件——解决生成的表格里数据行有小标题的这种需求

1 篇文章 1 订阅
1 篇文章 0 订阅

一、需求

      总体需求是根据word模板和数据,生成对应的word文件。经过技术调查后,确定poi-tl是最合适,最方便的框架。于是参考了官方文档和一些文章,很快就掌握了基本用法,这里附一下官方文档,写的清晰透彻,简单明了。

      官方文档:http://deepoove.com/poi-tl/#

      源码:https://github.com/Sayi/poi-tl

      但是,我的需求中有一个动态生成带小标题的表格数据的问题,如图:

 

       如图中红色框中就是动态的数据,然后绿色框中就是小标题,小标题和下面的数据都需要根据传来的数据,动态的生成。

二、分析

       一开始参考了官方文档里的例子,根据数据,动态的生成行和列。这种方式数据是可以动态显示了,但是会出现列和表头不对齐的问题。原因是因为建模板的时候,表格整体是使用拆分单元格的方式建的,导致表格真实的列数和看到的列数不一样。如图所示:

      如上图,我们把word文件复制到excel中,就会发现表格总共有11个列。所以,我们可以在生成数据行时,每行生成11个列,然后按照上一行的样式,把列再进行合并。这个思路是没问题,我也实现了,但是和我的需求还是有点不符合。因为这种方法需要知道模板的全部列数,以及模板中每个单元格所占的列数,这两个参数我是取不到的。

      于是我又参考了文档里区块对的例子,发现如果把小标题和下面的记录作为一个循环单元,进行动态的循环,最后形成的结果拼起来就是需求里满足的样子。模板如下:

三、代码

       这里为了省事,我就把我的接口和测试类直接粘过来了,可以自己做个记录,如果能帮到需要的人,给出一些启发就更好了。

/**
     * 生成报表的接口
     * @param file word模板
     * @param
     * @param response
     * @param data
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "/getReport", method = RequestMethod.POST)
    public String doCreateWordByTemplateAndData(MultipartFile file ,String type,HttpServletResponse response,@RequestParam("data") String data) throws IOException {
        Map<String,Object> renderData = JSONObject.parseObject(data);
        //增加对条形码的处理
        System.out.println(data);
        Set<String> sets = renderData.keySet();
        for (String key :sets) {
            //条形码
            if(key.endsWith("barCode")){
                FileInputStream barCode = ImgRenderUtil.getBarCode((String) renderData.get(key));
                renderData.put(key,new PictureRenderData(150,70, ".png",barCode));
            }
            //图片
            if(key.endsWith("img")){
                FileInputStream imgByBase64 = ImgRenderUtil.getImgByBase64((String) renderData.get(key));
                renderData.put(key,new PictureRenderData(150,70, ".png",imgByBase64));
            }
            
            if("cycleDatas".equals(key)){
                List<Map> list = (List<Map>)renderData.get(key);
                for (Map tableData : list) {
                    for (String tableDataKey : (Set<String>)tableData.keySet()) {
                        //列表项
                        if (tableDataKey.endsWith("_list")){
                            List<String> tableDataList = (List<String>)tableData.get(tableDataKey);
                            ArrayList<TextRenderData> textRenderDatas = new ArrayList<>();
                            for (String str :tableDataList) {
                                textRenderDatas.add(new TextRenderData(str));
                            }
                            tableData.put(tableDataKey, new NumbericRenderData(textRenderDatas));
                        }
                    }
                }
            }
            //列表项
            if (key.endsWith("_list")){
                List<String> list = (List<String>)renderData.get(key);
                ArrayList<TextRenderData> textRenderDatas = new ArrayList<>();
                for (String str :list) {
                    textRenderDatas.add(new TextRenderData(str));
                }
                renderData.put(key, new NumbericRenderData(textRenderDatas));
            }
        }

        //根据类型选择渲染方式类 如果遇到特殊的模板无法直接渲染 可以自定义渲染的策略类
        RenderPolicy policy = null;
        if("1".equals(type)){//普通表格列
            policy = new HackLoopTableRenderPolicy();
        }

        Configure config = Configure.newBuilder()
                .bind("tableDatas", policy).build();

        XWPFTemplate template = XWPFTemplate.compile(file.getInputStream(), config).render(renderData);
        System.out.println(renderData);
        ServletOutputStream outputStream = response.getOutputStream();
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename="
                .concat("result.docx"));
        try {
            template.write(outputStream);
            outputStream.flush();
            outputStream.close();
            template.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

调用接口的测试方法: 

  /**
     * 测试的方法 远程调用生成报表的接口
     * @param response
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "/doTest5", method = RequestMethod.GET)
    public String doTest5(HttpServletResponse response) throws IOException {
        List<Map> cycle = new ArrayList<Map>();

        List<Map> datas1 = new ArrayList<Map>();
        Map<String,Object> map0 = new HashMap<String, Object>();
        map0.put("childIndex",1.1);
        map0.put("checkName","启动会在获得伦理批件和签署协议之后");
        map0.put("yes","");
        map0.put("no","");
        map0.put("NA","");
        map0.put("problem","");
        datas1.add(map0);

        Map<String,Object> map1 = new HashMap<String ,Object>();
        map1.put("index",1);
        map1.put("childTitle","启动会");
        map1.put("tableDatas",datas1);

        List<Map> datas2 = new ArrayList<Map>();
        Map<String,Object> map01 = new HashMap<String, Object>();
        map01.put("childIndex",2.1);
        map01.put("checkName","研究者文件夹齐全");
        map01.put("yes","");
        map01.put("no","");
        map01.put("NA","");
        map01.put("problem","");
        datas2.add(map01);

        Map<String,Object> map3 = new HashMap<String ,Object>();
        map3.put("index",2);
        map3.put("childTitle","研究者文件夹");
        map3.put("tableDatas",datas2);

        List<Map> datas3 = new ArrayList<Map>();
        Map<String,Object> map03 = new HashMap<String, Object>();
        map03.put("childIndex",3.1);
        map03.put("checkName","使用的试验方案也伦理批准的版本一致(填写试验方案版本号)");
        map03.put("yes","");
        map03.put("no","");
        map03.put("NA","");
        map03.put("problem","");
        datas3.add(map03);

        Map<String,Object> map06 = new HashMap<String ,Object>();
        map06.put("childIndex",3.2);
        map06.put("checkName","使用的知情同意书与伦理批准的版本一致(请注明知情同意书版本号))");
        map06.put("yes","");
        map06.put("no","");
        map06.put("NA","");
        map06.put("problem","");
        datas3.add(map06);

        Map<String,Object> map5 = new HashMap<String ,Object>();
        map5.put("index",3);
        map5.put("childTitle","试验方案、知情同意书、研究病历和CRF");
        map5.put("tableDatas",datas3);

        cycle.add(map1);
        cycle.add(map3);
        cycle.add(map5);

        Map<String,Object> renderMap = new HashMap<String, Object>();
        renderMap.put("cycleDatas",cycle);
        renderMap.put("title","北京某某医院临床试验质量检查报告");
        renderMap.put("tableName","2015年度1季度器械试验临时检查计划");
        renderMap.put("num","2020-017");
        renderMap.put("trialName","xxx的临床研究");
        renderMap.put("member","xxx");
        renderMap.put("projectNum","2015-118");
        renderMap.put("shenbanzhe","xxx有限公司");
        renderMap.put("checkNum","0");
        renderMap.put("joinNum","0");
        renderMap.put("doingNum","0");
        renderMap.put("finishNum","0");
        renderMap.put("stopNum","0");

        Gson gson = new Gson();
        Map<String, String> params = new HashMap<>();
        params.put("data", gson.toJson(renderMap));
        String fileUrl = "xxx.docx";
        params.put("type","1");

        File wordTemplate = new File(fileUrl);
        String postUrl = "http://192.168.131.51:8081/report/getReport";
        CloseableHttpResponse postResponse = HttpClientUtil.doPostFileAndData(postUrl, wordTemplate, params);

        InputStream inputStream = null;
        inputStream = postResponse.getEntity().getContent();
        int len = 0;
        byte[] buffer = new byte[1024];
        ServletOutputStream outputStream = response.getOutputStream();
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename="
                .concat("result.docx"));
        while ((len = inputStream.read(buffer,0,1024))!=-1){
            outputStream.write(buffer,0,len);
        }
        outputStream.flush();
        outputStream.close();
        inputStream.close();
        return "ok";
    }

 工具类:

/**
     * 根据条形码内容 返回条码文件流
     * @param data
     * @return
     * @throws IOException
     */
    public static FileInputStream getBarCode(String data) throws IOException {
        BarcodeSettings settings = new BarcodeSettings();
        settings.setType(BarCodeType.Code_128);
        settings.setData(data);
        settings.setData2D(data);
        //设置底部显示文本
        settings.setShowTextOnBottom(true);
        settings.setShowText(true);
        settings.setBarHeight(4);
        BarCodeGenerator barCodeGenerator = new BarCodeGenerator(settings);
        BufferedImage bufferedImage = barCodeGenerator.generateImage();
        ImageIO.write(bufferedImage, "png", new File("Barcode.png"));
        return new FileInputStream("Barcode.png");
    }

    /**
     * 根据base64的图片编码 获得图片的文件流
     * @param base64Str
     * @return
     */
    public static FileInputStream getImgByBase64(String base64Str)  {
        String base64Img = base64Str.replace("data:image/png;base64,", "");
        String base64ImgNew = base64Img.replace("data:image/jpg;base64,", "");
        FileOutputStream fileOutputStream = null;
        try{
            byte[] bytes = new BASE64Decoder().decodeBuffer(base64ImgNew.trim());
            fileOutputStream =new FileOutputStream("img.png");
            fileOutputStream.write(bytes);
            return new FileInputStream("img.png");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                fileOutputStream.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        return null;
    }

旧系统可能会使用httpclient调用接口,方法如下: 


    /**
     *
     * @param url 接口地址
     * @param file 模板文件
     * @param param 数据参数
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static CloseableHttpResponse doPostFileAndData(String url,File file, Map<String,String> param) throws ClientProtocolException, IOException {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        try {
            HttpPost httpPost = new HttpPost(url);

            // 相当于<input type="file" name="file"/>
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.addBinaryBody("file",new FileInputStream(file),ContentType.MULTIPART_FORM_DATA,file.getName());
            for (Map.Entry<String,String> entry :param.entrySet()) {
                String key = entry.getKey();
                // 相当于<input type="text" name="userName" value=userName>
                StringBody value = new StringBody(entry.getValue(), ContentType.create("text/plain", Consts.UTF_8));
                builder.addPart(key, value);
            }
            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);

            response = httpClient.execute(httpPost);
            HttpEntity entity1 = response.getEntity();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("传出文件及数据是出现异常",e);
        }
        return response;

    }

       还可以使用restTemplate模拟表单提交,上传文件和数据,如下: 

  MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("data", renderMap);
        params.add("file", new FileSystemResource("xxx.docx"));

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(params, headers);
        String url = "http://192.168.131.51:8081/report/getReport";
//        String url = "http://localhost:8081/report/getReport";
        ResponseEntity<Resource> httpResponse = restTemplate.exchange(url
                , HttpMethod.POST, httpEntity, Resource.class);
        if (httpResponse.getStatusCode().equals(HttpStatus.OK)) {
            InputStream inputStream = null;
            OutputStream outputStream = null;
            inputStream = httpResponse.getBody().getInputStream();
            outputStream = response.getOutputStream();

            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename="
                    .concat("result.docx"));
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = inputStream.read(buffer,0,1024))!=-1){
                outputStream.write(buffer,0,len);
            }
            outputStream.flush();
            outputStream.close();
            inputStream.close();
        }

 

POI-TL (Poi Transfer Library) 是一个基于 Apache POIJava 库,它提供了一种更简单的方式来处理 Microsoft Office 格式文件,如 ExcelWord。当你需要将数据转换成 Word 文档中的表格,并能够按需生成多个表格时,你可以使用它的 API 来实现。 以下是一个简单的步骤说明: 1. **添加依赖**:首先确保你已经在项目中添加了 poi-tl 的依赖。如果你使用的是 Maven,可以在 `pom.xml` 中添加如下依赖: ```xml <dependency> <groupId>com.alibaba.poi</groupId> <artifactId>poi-tl</artifactId> <version>4.x.y</version> <!-- 更新到最新的版本 --> </dependency> ``` 2. **创建文档对象**:创建一个 `XWPFDocument` 对象来代表 Word 文档。 ```java XWPFDocument document = new XWPFDocument(); ``` 3. **创建表模板**:你可以创建一个 `XWPFTable` 对象作为表格模板,然后复制到每个新表格中。 ```java XWPFTable table = document.createTable(); ``` 4. **数据准备**:假设你有一个二维数组,包含表格数据。例如,`data[][]`。 5. **循环遍历并生成表格**:使用一个 for 循环遍历数据,每次迭代生成一个新的表格并将数据填入。 ```java for (int i = 0; i < data.length; i++) { // 创建新的表格 XWPFTable newTable = document.createTable(); // 将数据行填充到表格中 for (int j = 0; j < data[i].length; j++) { XWPFTableRow newRow = newTable.addRow(); XWPFTableCell cell = newRow.createCell(); cell.setCellValue(data[i][j]); } // 可选操作:设置表格标题、样式等 } ``` 6. **保存文档**:最后,你需要将文档保存为 .docx 文件。 ```java try { document.write(new File("output.docx")); } catch (IOException e) { e.printStackTrace(); } ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值