POI 技术实战

POI 技术如何实现对 Word 和 Excel 的读写操作?POI 技术相对其他同类型技术的优劣势又是哪些?怎样实现复杂的 Excel 读写操作?POI 对于 Word 和 Excel 有足够友好吗?这个 Chat,带领大家使用免费却实用的 POI 技术,实现几种日常通用的业务台账操作。

本场 Chat 主要内容:

  1. POI 技术优劣介绍。
  2. POI 导出 Excel。
  3. 实现批量导入功能。
  4. POI 对于 Word 的读写。
  5. 更多拓展想法。

Apache POI 简介

Apache POI 是用 Java 编写的免费开源的跨平台的 Java API,Apache POI 提供 API 给 Java 程式对 Microsoft Office(Excel、WORD、PowerPoint、Visio 等,主要实现用于 Excel)格式档案读和写的功能,POI 为 “ Poor Obfuscation Implementation ” 的首字母缩写,意为“简洁版的模糊实现”。

POI 技术优劣介绍

Microsoft 的 Office 系列产品拥有大量的用户,其中 Word、Excel 也成为办公文件的首选。在 Java 中,已经有很多对于 Word、Excel 的读写的解决方案,其中开源免费好用、用户量较大的就是 Apache 的 POI。

官方网站:

http://poi.apache.org/index.html

API文档:

http://poi.apache.org/apidocs/index.html

Office 系列产品的 java 读写插件的项目有很多:

docx4j:是一个解压的 docx 包(docx 本身是 zip 包)和解析 WordprocessingML 格式 XML 的 Java 库 。 最新版本的 docx4j 也支持 PowerPoint pptx 文件。但方法实现过于底层,国内相关文档说明特别少,而很少被人熟知。

PageOffice:国产的 Office 插件,虽然功能接口虽然没有没有 poi 的多,但是开发调用简单,特别是对 word 的读写操作比 POI 好用,毕竟 POI 的中文文档太少,经常拿来用的就是做在线预览了。不过要安装 PageOffice 控件,收费。

Jxl:开源世界中,有两套比较有影响的 API 可供使用,一个是 POI,一个是 jExcelAPI。纯 Java 的,并不依赖 Windows 系统,即使运行在 Linux下,它同样能够正确的处理 Excel 文件。图形和图表的支持很有限,而且仅仅识别 PNG 格式。网上有人做过测试,jxl 内存消耗也会更小,大数据量的时候建议使用 jxl,但是实现的功能 POI 比 jxl 更加完善。功能复杂或是有拓展需求的,建议使用 POI。

POI:相较于其他插件,POI 的用户量是最多的。简单易用,功能完善,项目开源,对 Excel 的读写操作功能十分强大,设置到单元格样式、标注脚注、设置打印 、插入图片、超链接等等,基本满足业务的所有需求。(网上有人说 POI 会出现莫名的 bug,数据替换参数总有失败,暂时没发现这种 bug了。)不过 POI 操作 word 的时候,只能创建简单的 word 文档,不过样式文字的读写操作也是完全满足的,只是相较操作 Excel 不算友好。POI 导出数据量过大的时候,容易造成内存溢出。

包名称说明

HSSF:提供读写 Microsoft Excel XLS 格式档案的功能。
XSSF:提供读写 Microsoft Excel OOXML XLSX 格式档案的功能。
HWPF:提供读写 Microsoft Word DOC 格式档案的功能。
HSLF:提供读写 Microsoft PowerPoint 格式档案的功能。
HDGF:提供读 Microsoft Visio 格式档案的功能。
HPBF:提供读 Microsoft Publisher 格式档案的功能。
HSMF:提供读 Microsoft Outlook 格式档案的功能。

其中,POI EXCEL 文档结构类:

HSSFWorkbook excel文档对象。
HSSFSheet excel的sheet HSSFRow excel的行。
HSSFCell excel的单元格 HSSFFont excel字体。
HSSFName 名称 HSSFDataFormat 日期格式。
HSSFHeader sheet头。
HSSFFooter sheet尾。
HSSFCellStyle cell样式。
HSSFDateUtil 日期。
HSSFPrintSetup 打印。
HSSFErrorConstants 错误信息表。

POI 导出 Excel

所用插件:poi - 3.17。下面实现一个简单的Excel导出。

思路:

获取一个已存在的 Excel 工作簿对象模板(也可以创建新的 Excel 工作簿对象,我习惯使用一个已存在的模板,这样 Excel 标题,文档摘要一些固定模板的就不用写了) -> 获取 Excel 工作表对象 -> 创建 Excel 工作表的行 -> 创建单元格样式 -> 创建 Excel 工作表指定行的单元格 -> 设置 Excel 工作表的值 -> 保存 Excel 文件(使用输出流)代码实现:

1,获取一个已存在的 Excel 工作簿对象模板:

POI 技术实战_第1张图片

FileInputStream iStream = new              FileInputStream("C:\\Users\\Administrator\\Desktop\\POI\\sample.xls");//里面的变量就是模板所在路径了  HSSFWorkbook workbook = new HSSFWorkbook(iStream);

2,获取 Excel 工作表对象:

HSSFSheet sheet = workbook.getSheetAt(0);

3,创建 Excel 工作表的行 :

HSSFRow row = sheet.createRow(0);

4,创建单元格样式 :

4.1,创建字体样式:

HSSFFont  headFont = workbook.createFont(); headFont.setFontName('宋体')headFont.setFontHeightInPoints((short)12)             //字体大小headFont.setBoldweight(headFont1.BOLDWEIGHT_BOLD);    //加粗

4.2单元格样式:

HSSFCellStyle  cell_Style= workbook.createCellStyle();cell_Style.setFont(headFont);  //设置字体样式cell_Style.setAlignment(CellStyle.ALIGN_LEFT); //设置单元格左对齐

5,创建 Excel 工作表指定行的单元格:

HSSFCell cell=row.createCell(0);

5.1,使用单元格样式:

cell.setCellStyle(cell_Style);

6,设置 Excel 工作表的值(单元格的值):

cell.setCellValue(“单元格里面的值”);

7,保存 Excel 文件(使用输出流):

FileOutputStream fileOut = new FileOutputStream(path);   workbook.write(fileOut);fileOut.close();//关闭文件流

上面 7 点要素做到,就可以做到导出一个 Excel 了。下面是个例子的代码(注意一点是,我用的是动态语言 groovy,用 java 的变量类型改一下):

//导入文件、单元格样式     def getSheet(){        try {             FileInputStream iStream = new FileInputStream("C:\\Users\\Administrator\\Desktop\\POI\\sample.xls");             HSSFWorkbook  workbook = new HSSFWorkbook(iStream);             HSSFSheet  sheet = workbook.getSheetAt(0);             sheet.setDefaultColumnWidth(50);             //设置标题字体为宋体,14号字,加粗             def  headFont = workbook.createFont()             headFont.setFontName('宋体')             headFont.setFontHeightInPoints((short)14)             headFont.setBoldweight(headFont.BOLDWEIGHT_BOLD);    //加粗             HSSFCellStyle cell_Style = workbook.createCellStyle()            cell_Style.setFont(headFont)                         //设置字体样式            cell_Style.setAlignment(CellStyle.ALIGN_LEFT)           //左对齐             cell_Style.setVerticalAlignment(CellStyle.VERTICAL_CENTER) //垂直居中             cell_Style.setWrapText(true);                       //自动换行            cell_Style.setBorderLeft(HSSFCellStyle.BORDER_THIN);    //边框加线             cell_Style.setBorderRight(HSSFCellStyle.BORDER_THIN);   //边框加线             cell_Style.setBorderTop(HSSFCellStyle.BORDER_THIN);     //边框加线             cell_Style.setBorderBottom(HSSFCellStyle.BORDER_THIN);  //边框加线             return [workbook:workbook,sheet:sheet,cell_Style:cell_Style];         }catch (Exception e){             println("导出文件失败!");             println(e.toString());         }finally{             try{                 if(iStream){iStream.close();}             }catch(Exception e){                 println(e.toString());             }         }     } //导出Excel  def down_excel(){         try {         def data=[["小三","发小"],["小玲","妹纸"],["小7","宅家里"]];         def obj=getSheet();         def workbook=obj.workbook;         def sheet =obj.sheet;         def cell;         def row;         data.eachWithIndex{d,i ->             row=sheet.createRow(i+2);            cell=row.createCell(0);cell.setCellStyle(obj.cell_Style);cell.setCellValue(i+1);             cell=row.createCell(1);cell.setCellStyle(obj.cell_Style);cell.setCellValue(d[0]);             cell=row.createCell(2);cell.setCellStyle(obj.cell_Style);cell.setCellValue(d[1]);         }         def downname=encodeFileName("导出Excel("+(new Date().format('yyyy-MM-dd'))+").xls");         response.setContentType('application/msexcel')         response.setHeader('content-disposition', "attachment;filename=${downname}")         workbook.write(response.outputStream)         response.outputStream.flush()         }catch (Exception e){             println("导出Excel失败!");             e.printStackTrace();         }
实现批量导入功能。

批量导入的原理很简单:获取工作簿 ->获取工作表 ->循环行数据,逐个获取行数据 ->循环行数据,逐个获取单元格数据

批量导入下面三列数据:

POI 技术实战_第2张图片

代码实现:

 //批量导入     def batchExcel(params) {   def iStream = new   FileInputStream("C:\\Users\\Administrator\\Desktop\\POI\\sample.xls");         def workbook = new HSSFWorkbook(iStream);         def sheet = workbook.getSheetAt(0);         if (sheet.getFirstRowNum() == sheet.getLastRowNum()) {             print("批量导入失败!Excel文件的内容不能为空!。");             return false         }         def row, cellData = [];         def dataList = [];         for (int i = 2; i < sheet.getPhysicalNumberOfRows(); i++) {             cellData = [];             row = sheet.getRow(i);             for (int j = 0; j < row.getPhysicalNumberOfCells(); j++) {                 cellData.add(getValue(row.getCell(j)));             }             dataList.add(cellData);         }         dataList.each {             println(it)         }    }

效果:

POI 技术实战_第3张图片

批量导入的读取数据用 POI 实现十分简单,麻烦的是获取的值是带属性的,为了适配具体的业务,很多时候就要进行值的属性判断,属性改变等等,就是说大量的处理才能给你存数据库里,还有要做具体到单元格的导入报错信息提醒,毕竟存在本来这个单元格是要写数字的,结果给你写了一堆英文,导入存储失败,就要提醒到这个单元格填写内容错误了。

还有批量导入存表的时候,切记要加事务处理,否则第一次导入 100 条数据失败了,却还是存了 50 条,要不就一条不存,要不就存好 100 条数据。

POI 对于 Word 的读写

poi 操作 word 文档,较于操作 Excel,功能则少了很多。写入或是读取,都是通过两种手段:

1,段落。
2,table。

写一个简单的 word 文档:

创建 Word 文件 ->新建一个段落(创建一个表格) ->设置段落样式 ->写入值 ->输出流输出 word。

代码示例:

//写word     def write_word() {         try {             XWPFDocument document = new XWPFDocument();// 创建Word文件             XWPFParagraph p = document.createParagraph();// 新建一个段落             p.setAlignment(ParagraphAlignment.CENTER);// 设置段落的对齐方式             XWPFRun r = p.createRun();//创建段落文本             r.setText("---段落文本内容---");//设置文本内容             r.setBold(true);//设置为粗体             def table = document.createTable(3, 3);//创建一个表格             table.getRow(0).getCell(0).setText("小三");             table.getRow(0).getCell(1).setText("小玲");            table.getRow(0).getCell(2).setText("小7");             def downname = encodeFileName("导出word" + UtilTimeFormat.getFormat(new Date(), "yyyy-MM-dd") + ".docx")            response.setContentType('application/vnd.ms-word.document.12');             response.setHeader('content-disposition', "attachment;filename=${downname}");             document.write(response.outputStream);             response.outputStream.flush();         } catch (Exception e) {             println("导出Word失败!");             e.printStackTrace();         }     }

POI 技术实战_第4张图片

读一个 word 文档:

获取 Word 文件 ->获取段落(获取表格) ->获取段落文字(获取表格单元格文字)

获取如下 word 内容:

POI 技术实战_第5张图片

代码示例:

//读word     def down_cqtz() {         FileInputStream stream = new         FileInputStream("C:\\Users\\Administrator\\Desktop\\POI\\poiForWord.docx"); //def getParagraph=document.paragraphs[0];//直接获取第一个段落         for (def p : document.getParagraphs())//遍历段落         {             print(p.getParagraphText())  }   //    def table=document.tables[0];//直接获取第一张表格         for (def table : document.getTables())//遍历表格         {             for (def row : table.getRows()) {                 for (def cell : row.getTableCells()) {                     print(cell.getText())                 }             }         }     }

效果:

POI 技术实战_第6张图片

由于 POI 对 word 的支持不够友好(可能是国内的 POI 的中文 api 确实少的缘故吧),建议使用对 word 操作的时候读写结合,在一个 word 模板上读写操作,这样的实现会好一点。

更多拓展想法

POI 不止对 Excel 和 Word 有操作支持,对 Microsoft Office 的几个办公软件套件都有支持,但国内用户基本没有,相关文档就更少了,在这里就不赘述了。

上面有介绍到如何读写简单的 Excel 和 Word,其实实际应用上功能需求远超过上面的这些,POI 对 Excel 和 Word 的各种属性样式支持也是挺丰富的(大多是 Excel),相关的API在网上也是有够用的,毕竟免费开源,用户量大,实在网上找不到功能的就得看源码找了。日后有空的时候,可以到网上找找,源码看看,整合一些样式设置,功能模块的。


本文首发于GitChat,未经授权不得转载,转载需与GitChat联系。

阅读全文: http://gitbook.cn/gitchat/activity/5b602df055c8d0781bbdb38c

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX

你可能感兴趣的:(POI 技术实战)