使用POI处理常见的文件类型

前言:拖更了很久,主要是公司项目太忙,抽不出大块时间去整理开发过程中遇到的问题或是心得。当然也有自己最近一段时间对于写博客有些懈怠的原因,以后尽可能的最少每天出一篇吧。

 

正题:相信很多小伙伴在企业级项目开发过程中,都遇到过对Office格式文档操作的问题,博主这段时间遇到这方面的需求比较多,用此文MARK一下。

简单用一下百度词条给出的 POI 释意,POI是Apache的开源函式库,提供API给JAVA程序对Office格式档案读写功能。

那我们大家立刻开始试试吧!

使用POI处理常见的文件类型_第1张图片

其一,使用POI操作Word

 

江湖规矩,先上POM文件


    org.apache.poi
    poi-scratchpad
    4.1.0



    org.apache.poi
    poi-ooxml
    4.1.0



    org.apache.poi
    poi-ooxml-schemas
    4.1.0

以下代码仅为测试demo,提供思路而已,请根据具体场景做对应处理

场景,Word文档里内容是一个JSON串,读取后直接转为对象即可。

 /**
   * 测试demo
   *
   * @param 
   * @return 
   */
   @PostMapping("/word")
   public void mockPaper(@RequestParam("file") MultipartFile file) {
       try {
            String fileName = file.getOriginalFilename();
            int lastIndexOf = fileName .lastIndexOf(".");
            if(lastIndexOf==-1) {
                throw new IllegalArgumentException("当前传入的文件格式不合法!");
            }
            String suffex=fileName.substring(lastIndexOf+1);
           

            if("docx".equals(suffex)){
                InputStream inputStream = file.getInputStream();
                XWPFDocument xwpfDocument = new XWPFDocument(inputStream);
                XWPFWordExtractor extractor = new XWPFWordExtractor(xwpfDocument);
                //从Word文档中提取文本
                String text = extractor.getText();
           

                inputStream.close();
                xwpfDocument.close();
                extractor.close();

                /*
                分页获取
                List paragraphs = xwpfDocument.getParagraphs();
                for(XWPFParagraph paragraph : paragraphs){
                    String words = paragraph.getText();
                }

                Iterator iterator = xwpfDocument.getParagraphsIterator();
                while (iterator.hasNext()) {
                    XWPFParagraph para = iterator.next();
                }*/
            }else if("doc".equals(suffex)){
                InputStream inputStream = file.getInputStream();
                //使用HWPF组件中WordExtractor类从Word文档中提取文本或段落
                WordExtractor wordExtractor = new WordExtractor(inputStream);
                String s = wordExtractor.getText();

                inputStream.close();
                wordExtractor.close();
            }else {
                throw new IllegalArgumentException("不能解析的文档类型,请输入正确的word文档类型的文件!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

其二,使用POI操作Excel

 

老规矩,先上POM


    org.apache.poi
    poi-ooxml-schemas
    4.1.0



    org.apache.poi
    poi-ooxml
    4.1.0

 

场景:根据Excel文档数据(约一万条,九十多列),根据文档单条某一列的值,去查询数据库判断是否存在对应,若存在则将数据保存在临时文件中,不存在则跳过。

思路:文档数据转DTO集合,批量查询数据库并判断单条是否存在对应,批量写入临时文件。

 

相关代码已作脱敏处理,导入接口

/**
     * Excel导入
     *
     * @param file 入参
     * @return RespDto 出参
     */
    @ResponseBody
    @PostMapping("/importExcel")
    @ApiOperation("Excel导入")
    @ControllerLog(desc = "Excel导入")
    public RespDto> importExcel(MultipartFile file) {
        RespDto> result = new RespDto<>(new SingleVo<>(Boolean.FALSE));
        try {
            if(fundPoolService.importFund(file)){
                result.setData(new SingleVo<>(Boolean.TRUE));
            }
        } catch (Exception e) {
            log.error("importExcel 文件导入异常 error {}.param:{}",e,JSONObject.toJSONString(file));
            result.setCode(ResultCodeEnum.UPDATE_ERR.getCode()).setMsg(e.getMessage());
        }
        return result;
    }

 Excel导入,Service层代码接口

​
/**
     * Excel导入
     *
     * @param file 入参
     * @return boolean 出参
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean importExcel(MultipartFile file) throws IOException{
        AssertUtil.assertNotNull(file, "importExcel file is not allow null or empty");
        List xxList = new xxExcelUtil().readExcelFile(file.getInputStream(),file.getOriginalFilename());
        if(ObjectUtils.isEmpty(xxList)){
            throw new BusinessException("Excel导入异常,上传文件格式有误");
        }

        //将成功数据写入临时文件
        createResultFile(xxList);
        return true;
    }

​

 构造通用的Excel处理工具

​
public class xxExcelUtil {

    public List readExcelFile(InputStream inputStream, String fileName) throws IOException {

        Workbook workbook = null;
        try {
            //判断什么类型文件
            if (fileName.endsWith(".xls")) {
                workbook = new HSSFWorkbook(inputStream);
            } else if (fileName.endsWith(".xlsx")) {
                workbook = new XSSFWorkbook(inputStream);
            }else {
                return null;
            }

            //获取所有的工作表的的数量,这里SheetNum数值应为1
            int numOfSheet = workbook.getNumberOfSheets();
            if(numOfSheet!=1){
                return null;
            }

            //获取一个sheet也就是一个工作表
            Sheet sheet = workbook.getSheetAt(numOfSheet-1);
            //获取一个sheet有多少Row
            int lastRowNum = sheet.getLastRowNum();
            Row row;

            List xxList = new ArrayList<>();
            for (int j = 1; j <= lastRowNum; j++) {
                row = sheet.getRow(j);
                if (row == null) {
                    continue;
                }
                //获取一个Row有多少Cell
                short lastCellNum = row.getLastCellNum();
                xx xx= new xx();

                for (int k = 0; k <= lastCellNum; k++) {
                   //根据自己的业务处理
                }
                xxList.add(xx);
            }

            //返回结果集
            return xxList;
        } catch (Exception e) {
            return null;
        }finally {
            inputStream.close();
            if(!ObjectUtils.isEmpty(workbook)){
                workbook.close();
            }
        }
    }
}

​

在这里可以看到,批量查询博主并没有采用常规的Mybatis in+拼接的方式,而采用了查取表中所有数据,通过一次遍历及唯一标识的方式,原因其一,九千条采用拼接的方式,会使查询SQL非常长,其二查询效率比如下这种方式低好几个档次。

特别提醒:但是这种基于内存的判断也有其本身的限制,因数据库表xx条数约两千多条,其日后不会有大变动,因此选择这种方式非常合适。但是如果表中数据条数非常多,或者不确定日后表中数据增长上升的速度,不建议采用这种方式。

/**
     * 将成功数据写入临时文件
     *
     * @param 
     * @return 
     */
    private void createResultFile(List xxList){

        //从数据库查取所有数据的唯一标识
        List codeList = xxMapper.selectAllCode();
        if(ObjectUtils.isEmpty(codeList)){
            throw new BusinessException("将成功数据写入临时文件异常,从数据库查取所有产品集为空");
        }

        StringBuilder prodBuilder = new StringBuilder();
        for (xx xx: xxList){
            if(codeList.contains(xx.getCode())){
                prodBuilder.append(prodInfo.getCode()).append(",")
                        .append(prodInfo.getName()).append(",")
                        .append(prodInfo.getScore()).append("\r\n");
            }
        }
        //写入文件
        FileUtil.writeString(prodBuilder.toString(),fixTempFile(),"UTF-8");
    }

定位临时文件这个方法,有一个特别注意的地方,因为WIN和LINUX路径分隔符并不一致,而 File.separator 可根据项目部署的系统选择对应的分隔符,好用的爆炸。

/**
     * 定位临时文件
     *
     * @param
     * @return
     */
    private String fixTempFile(){

        //定位临时文件
        String resultFilePath = new File(this.getClass().getResource("").getPath()).toString();
        String filePath = resultFilePath.substring(0, resultFilePath.lastIndexOf(File.separator) ) + File.separator +  "resultTxt" + File.separator ;
        return filePath + FundConstant.FILE_NAME;
    }

CSV文件导出先占个坑,下篇出

OK,使用POI处理常见的文件类型,圆满完结

✿✿✿ヽ(°▽°)ノ✿✿✿ヽ(°▽°)ノ✿✿✿ヽ(°▽°)ノ✿✿✿ヽ(°▽°)ノ✿✿✿

你可能感兴趣的:(JDD,poi)