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

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

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



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


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


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




其一,使用POI操作Word


江湖规矩,先上POM文件


<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>4.1.0</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.0</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.0</version>
</dependency>

以下代码仅为测试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<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
                for(XWPFParagraph paragraph : paragraphs){
                    String words = paragraph.getText();
                }
                Iterator<XWPFParagraph> 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


<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.0</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.0</version>
</dependency>


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


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



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


/**
     * Excel导入
     *
     * @param file 入参
     * @return RespDto 出参
     */
    @ResponseBody
    @PostMapping("/importExcel")
    @ApiOperation("Excel导入")
    @ControllerLog(desc = "Excel导入")
    public RespDto<SingleVo<Boolean>> importExcel(MultipartFile file) {
        RespDto<SingleVo<Boolean>> 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<xx> 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<xx> 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<xx> 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<xx> xxList){
        //从数据库查取所有数据的唯一标识
        List<String> 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处理常见的文件类型,圆满完结


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


相关文章
|
4月前
|
Java API Apache
使用 Apache PDFBox 操作PDF文件
Apache PDFBox库是一个开源的Java工具,专门用于处理PDF文档。它允许用户创建全新的PDF文件,编辑现有的PDF文档,以及从PDF文件中提取内容。此外,Apache PDFBox还提供了一些命令行实用工具。
114 6
【POI】使用POI 创建生成XLS,打开xls文件提示【此文件中某些文本格式可能已经更改,因为它已经超出最多允许的字体数。】
 使用POI 创建生成XLS,打开xls文件提示【此文件中某些文本格式可能已经更改,因为它已经超出最多允许的字体数。】       原因: 是因为在POI处理xls的过程中,太多次调用了: HSSFFont font = hssfWorkbook.createFont(); 解决方法: 将font创建为全局变量,在需要使用的地方再调用进行单独的设置。
2975 0
|
10月前
|
Java Apache
通过Apache PDFBox将pdf转换为word
通过Apache PDFBox将pdf转换为word
506 0
|
Java Linux
POI 生成word 转 pdf
根据业务需要 需要出一份 PDF 文件 作为 公告的附件使用 PDF文件中 需要有 各种数据作为展示 是动态生成的
2166 0
POI  生成word 转 pdf
|
Apache
使用POI读写Word doc文件
使用POI读写word doc文件 目录 1     读word doc文件 1.1     通过WordExtractor读文件 1.2     通过HWPFDocument读文件 2     写word doc文件          Apache poi的hwpf模块是专门用来对word doc文件进行读写操作的。
2233 0