POI 技术如何实现对 Word 和 Excel 的读写操作?POI 技术相对其他同类型技术的优劣势又是哪些?怎样实现复杂的 Excel 读写操作?POI 对于 Word 和 Excel 有足够友好吗?这个 Chat,带领大家使用免费却实用的 POI 技术,实现几种日常通用的业务台账操作。
本场 Chat 主要内容:
- POI 技术优劣介绍。
- POI 导出 Excel。
- 实现批量导入功能。
- POI 对于 Word 的读写。
- 更多拓展想法。
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 工作簿对象模板:
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(); }
实现批量导入功能。
批量导入的原理很简单:获取工作簿 ->获取工作表 ->循环行数据,逐个获取行数据 ->循环行数据,逐个获取单元格数据
批量导入下面三列数据:
代码实现:
//批量导入 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 实现十分简单,麻烦的是获取的值是带属性的,为了适配具体的业务,很多时候就要进行值的属性判断,属性改变等等,就是说大量的处理才能给你存数据库里,还有要做具体到单元格的导入报错信息提醒,毕竟存在本来这个单元格是要写数字的,结果给你写了一堆英文,导入存储失败,就要提醒到这个单元格填写内容错误了。
还有批量导入存表的时候,切记要加事务处理,否则第一次导入 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(); } }
读一个 word 文档:
获取 Word 文件 ->获取段落(获取表格) ->获取段落文字(获取表格单元格文字)
获取如下 word 内容:
代码示例:
//读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 对 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 专享技术内容哦。