apache PoI excel world 等

编程入门 行业动态 更新时间:2024-10-09 19:22:55

apache <a href=https://www.elefans.com/category/jswz/34/1754000.html style=PoI excel world 等"/>

apache PoI excel world 等

一,简介

POI是apache的项目,可对微软的Word,Excel,PPT进行操作,包括office2003和2007,Excle2003和2007。
poi现在一直有更新。所以现在主流使用POI。

Apache POI是Apache软件基金会的开源项目,由Java编写的免费开源的跨平台的 Java API,Apache
POI提供API给Java语言操作Microsoft Office的功能。

API对象介绍

工作簿 : WorkBook (HSSFWordBook : 2003版本,XSSFWorkBook : 2007级以上)
工作表 : Sheet (HSSFSheet : 2003版本,XSSFSheet : 2007级以上)
行 : Row (HSSFRow : 2003版本,XSSFRow : 2007级以上)
单元格 : Cell (HSSFCell : 2003版本,XSSFCell : 2007级以上)

POI操作excel

添加所需的依赖:

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

POI操作Excel高低版本区别

操作高版本 案例

package com.itheima.demo;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileOutputStream;public class POIDemo02 {public static void main(String[] args) throws Exception{Workbook workbook = new XSSFWorkbook(); //创建了一个全新(里面什么都没有)的工作薄Sheet sheet = workbook.createSheet("demo测试");  //创建了一个全新(里面什么都没有)的工作表Row row = sheet.createRow(0);  //创建了第一行(空的)Cell cell = row.createCell(0);//创建的是第一行的第一个单元格cell.setCellValue("这是我第一次玩POI");
//        把工作薄输出到本地磁盘workbook.write(new FileOutputStream("d://test.xlsx"));}
}

1、基于模板导出列表数据

1.2、思路

首先准备一个excel模板,这个模板把复杂的样式和固定的内容先准备好并且放入到项目中,然后读取到模板后向里面放入数据。

1.3、实现

第一步:准备一个excel作为导出的模板,模板内容如下

第一个sheet:

第二个sheet:

第二步:把这个模板改一个英文名称比如:userList.xlsx,放入到项目中

第三步:UserService实现方法

public void downLoadXlsxWithTempalte(HttpServletRequest request, HttpServletResponse response) throws Exception {//        获取模板的路径File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式File templatePath = new File(rootPath.getAbsolutePath(),"/excel_template/userList.xlsx");//        读取模板文件产生workbook对象,这个workbook是一个有内容的工作薄Workbook workbook  = new XSSFWorkbook(templatePath);//        读取工作薄的第一个工作表,向工作表中放数据Sheet sheet = workbook.getSheetAt(0);//        获取第二个的sheet中那个单元格中的单元格样式 第二个sheet 第一行,第一列,的样式CellStyle cellStyle = workbook.getSheetAt(1).getRow(0).getCell(0).getCellStyle();//        处理内容List<User> userList = this.findAll();//从索引2开始存放数据int rowIndex = 2;Row row = null;Cell cell = null;//有多少条数据就创建多少行for (User user : userList) {row = sheet.createRow(rowIndex);row.setHeightInPoints(15); //设置行高//第一个单元格 索引为0cell = row.createCell(0);cell.setCellValue(user.getId());cell.setCellStyle(cellStyle); //设置单元格样式cell = row.createCell(1);cell.setCellValue(user.getUserName());cell.setCellStyle(cellStyle);cell = row.createCell(2);cell.setCellValue(user.getPhone());cell.setCellStyle(cellStyle);cell = row.createCell(3);cell.setCellValue(simpleDateFormat.format(user.getHireDate()));cell.setCellStyle(cellStyle);cell = row.createCell(4);cell.setCellValue(user.getAddress());cell.setCellStyle(cellStyle);rowIndex++;}//            导出的文件名称String filename="用户列表数据.xlsx";//            设置文件的打开方式和mime类型ServletOutputStream outputStream = response.getOutputStream();response.setHeader( "Content-Disposition", "attachment;filename="  + new String(filename.getBytes(),"ISO8859-1"));response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");workbook.write(outputStream);}

第四步:修改UserController中的方法,导出测试

@GetMapping(value = "/downLoadXlsxByPoi",name = "使用POI下载高版本")
public void downLoadXlsx(HttpServletRequest request,HttpServletResponse response) throws Exception{//       userService.downLoadXlsx(response);userService.downLoadXlsxWithTempalte(request,response); //下载的excel带样式
}

2、导出用户详细数据

2.1、 需求

如下,点击用户列表中的下载按钮,下载文件内容如下

2.2、思路

最简单的方式就是先根据案例制作模板,导出时查询用户数据、读取模板,把数据放入到模板中对应的单元格中,其中我们先处理最基本的数据,稍后再处理图片

2.3、实现

第一步:制作一个excel导出模板,如下

第二步:制作好的模板放入到项目中

第三步:Controller中添加方法
@GetMapping(value = "/download",name = "导出用户详细信息")
public void downLoadUserInfoWithTempalte(Long id,HttpServletRequest request,HttpServletResponse response) throws Exception{userService.downLoadUserInfoWithTempalte(id,request,response);
}
第四步:在UserService中添加方法
 public void downLoadUserInfoWithTempalte(Long id, HttpServletRequest request, HttpServletResponse response) throws Exception  {//        获取模板的路径File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式File templatePath = new File(rootPath.getAbsolutePath(),"/excel_template/userInfo.xlsx");
//        读取模板文件产生workbook对象,这个workbook是一个有内容的工作薄Workbook workbook  = new XSSFWorkbook(templatePath);
//        读取工作薄的第一个工作表,向工作表中放数据Sheet sheet = workbook.getSheetAt(0);
//        处理内容User user = userMapper.selectByPrimaryKey(id);
//        接下来向模板中单元格中放数据
//        用户名   第2行第2列sheet.getRow(1).getCell(1).setCellValue(user.getUserName());
//        手机号   第3行第2列sheet.getRow(2).getCell(1).setCellValue(user.getPhone());
//        生日     第4行第2列  日期转成字符串sheet.getRow(3).getCell(1).setCellValue(simpleDateFormat.format(user.getBirthday()));
//        工资 第5行第2列sheet.getRow(4).getCell(1).setCellValue(user.getSalary());
//        工资 第6行第2列sheet.getRow(5).getCell(1).setCellValue(simpleDateFormat.format(user.getHireDate()));
//        省份     第7行第2列sheet.getRow(6).getCell(1).setCellValue(user.getProvince());
//        现住址   第8行第2列sheet.getRow(7).getCell(1).setCellValue(user.getAddress());
//        司龄     第6行第4列暂时先不考虑//        城市     第7行第4列sheet.getRow(6).getCell(3).setCellValue(user.getCity());// 先创建一个字节输出流
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
// BufferedImage是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中
BufferedImage bufferImg = ImageIO.read(new File(rootPath + user.getPhoto()));// 把读取到图像放入到输出流中
// 此处最好不要把后缀名写死,可以判断获取扩展名, 转换成大写 ,Workbook.PICTURE_TYPE_JPEG 也需要判断
ImageIO.write(bufferImg, "JPG", byteArrayOut);
// 创建一个绘图控制类,负责画图
Drawing patriarch = sheet.createDrawingPatriarch();
// 指定把图片放到哪个位置  结束位置需要注意,
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, 2, 1, 4, 5);
// 开始把图片写入到sheet指定的位置patriarch.createPicture(anchor, workbook.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_JPEG));//            导出的文件名称String filename="用户详细信息数据.xlsx";
//            设置文件的打开方式和mime类型ServletOutputStream outputStream = response.getOutputStream();response.setHeader( "Content-Disposition", "attachment;filename="  + new String(filename.getBytes(),"ISO8859-1"));response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");workbook.write(outputStream);}
点击页面上的下载按钮,效果如下:

接下来处理一下头像照片和司龄

3、导出数据带图片、公式

3.1、导出图片

个人信息的导出中包含了头像照片,需要用到POI的导出图片功能,那么POI主要提供了两个类来处理照片,这两个类是Patriarch和ClientAnchor前者负责在表中创建图片,后者负责设置图片的大小位置。

在UserService的方法中添加以下代码

// 先创建一个字节输出流
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
// BufferedImage是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中
BufferedImage bufferImg = ImageIO.read(new File(rootPath + user.getPhoto()));
// 把读取到图像放入到输出流中
ImageIO.write(bufferImg, "jpg", byteArrayOut);
// 创建一个绘图控制类,负责画图
Drawing patriarch = sheet.createDrawingPatriarch();
// 指定把图片放到哪个位置
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, 2, 1, 4, 5);
// 开始把图片写入到sheet指定的位置
patriarch.createPicture(anchor, workbook.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_JPEG));

关于XSSFClientAnchor的8个参数说明


dx1 - the x coordinate within the first cell.//定义了图片在第一个cell内的偏移x坐标,既左上角所在cell的偏移x坐标,一般可设0
dy1 - the y coordinate within the first cell.//定义了图片在第一个cell的偏移y坐标,既左上角所在cell的偏移y坐标,一般可设0
dx2 - the x coordinate within the second cell.//定义了图片在第二个cell的偏移x坐标,既右下角所在cell的偏移x坐标,一般可设0
dy2 - the y coordinate within the second cell.//定义了图片在第二个cell的偏移y坐标,既右下角所在cell的偏移y坐标,一般可设0col1 - the column (0 based) of the first cell.//第一个cell所在列,既图片左上角所在列
row1 - the row (0 based) of the first cell.//图片左上角所在行
col2 - the column (0 based) of the second cell.//图片右下角所在列
row2 - the row (0 based) of the second cell.//图片右下角所在行

3.2、导出公式

应用场景说明,在导出用户详细数据时有一个司龄的显示,这里的司龄就是截止到现在入职到本公司的时间,为了学习POI对公式的操作,我们这里使用POI的公式来做。

计算截止到现在入职到本公司的时间应该用到两个日期相差的函数:DATEDIF函数,这个函数需要3个参数

P1: 一个日期 P2:截止日期 P3: 时间单位 举例:

1、DATEDIF(“2015-10-01”,“2020-10-01”,“y”) 结果是5

2、CONCATENATE(DATEDIF(“2015-10-01”,“2020-10-01”,“y”)),“年”,DATEDIF(“2015-10-01”,“2020-10-01”,“ym”),“个月”) 结果是5年0个月

放到这个用户导出时,第一个参数就是放到相应单元格上数据,第二个参数就是当天时间,

如果直接在excel中操作,如下:

在使用POI导出时使用setCellFormula方法来设置公式:


关于POI支持公式详见官网:.html

4、自定义导出详细数据的引擎

看我们刚才导出时写的代码,必须要提前知道要导出数据在哪一行哪一个单元格,但是如果模板一旦发生调整,那么我们的java代码必须要修改,我们可以自定义个导出的引擎,有了这个引擎即使模板修改了我们的java代码也不用修改

4.2、思路

在制作模板时,在需要插入数据的位置我们坐上标记,在导出时,对象的属性要和标记做对应,如果对应匹配一样,就把值赋值到相应的位置。

4.3、实现

第一步:制作模板,命名 userInfo2.xlsx

第二步:添加到项目中

第三步:实现导出的引擎代码

package com.itheima.utils;import org.apachemons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.springframework.util.ResourceUtils;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;public class ExcelExportEngine {private static SimpleDateFormat  sdf = new SimpleDateFormat("yyyy-MM-dd");public  static Workbook writeToExcel(Object object, Workbook workbook,String photoPath) throws Exception{//先把bean转成map//EntityUtils.entityToMap(object); ,自己去找把bean 转为map的方法 hutool工具类Map<String, Object> map = EntityUtils.entityToMap(object);//循环遍历每一对数据,把日期型的转成字符串,方便导出for (String key : map.keySet()) {Object vlaue = map.get(key);if(vlaue instanceof Date){System.out.println(sdf.format(vlaue));map.put(key,sdf.format(vlaue));}}//获取第一个sheet,整体的思路是循环100个行的100个单元格Sheet sheet = workbook.getSheetAt(0);Cell cell =null;Row row = null;for (int i = 0; i < 100; i++) {row = sheet.getRow(i); //获取到空行为止if(row==null){break;}else{for (int j = 0; j < 100; j++) {cell = row.getCell(j);//获取到空单元格不处理if(cell!=null){writeCell(cell,map); //开始向单元格中写内容}}}}if(StringUtils.isNotBlank(photoPath)){File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
//        BufferedImage是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中BufferedImage bufferImg = ImageIO.read(new File(rootPath + photoPath));ImageIO.write(bufferImg, "jpg", byteArrayOut);Drawing patriarch = sheet.createDrawingPatriarch();//第二个sheet 作为图片的位置参数,以后在的二个sheet中设置位置,后台获取Sheet sheet2 = workbook.getSheetAt(1);row = sheet2.getRow(0);int col1 = ((Double) row.getCell(0).getNumericCellValue()).intValue();int row1 = ((Double) row.getCell(1).getNumericCellValue()).intValue();int col2 = ((Double) row.getCell(2).getNumericCellValue()).intValue();int row2 = ((Double) row.getCell(3).getNumericCellValue()).intValue();
//            锚点,固定点ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0,  col1, row1, col2, row2);patriarch.createPicture(anchor, workbook.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_JPEG));workbook.removeSheetAt(1);}return workbook;}private static void writeCell(Cell cell, Map<String, Object> map) {CellType cellType = cell.getCellType();switch (cellType){case FORMULA:{  //如果是公式就直接放行了break;}default:{String cellValue = cell.getStringCellValue();//就是判断一下获取到单元格中的值是否和map中的key保持一致if(StringUtils.isNotBlank(cellValue)){for (String key : map.keySet()) {if(key.equals(cellValue)){cell.setCellValue(map.get(key).toString());}}}}}}
}

第四步:修改UserService的方法

public void downLoadUserInfoWithTempalte2(Long id, HttpServletRequest request, HttpServletResponse response) throws Exception  {//        获取模板的路径File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式File templatePath = new File(rootPath.getAbsolutePath(),"/excel_template/userInfo2.xlsx");//        读取模板文件产生workbook对象,这个workbook是一个有内容的工作薄Workbook workbook  = new XSSFWorkbook(templatePath);// 查询用户信息User user = userMapper.selectByPrimaryKey(id);// 这里使用引擎直接导出workbook = ExcelExportEngine.writeToExcel(user,workbook,user.getPhoto());//            导出的文件名称String filename="用户详细信息数据.xlsx";//            设置文件的打开方式和mime类型ServletOutputStream outputStream = response.getOutputStream();response.setHeader( "Content-Disposition", "attachment;filename="  + new String(filename.getBytes(),"ISO8859-1"));response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");workbook.write(outputStream);
}

5、百万数据导出

5.1、概述

我们都知道Excel可以分为早期的Excel2003版本(使用POI的HSSF对象操作)和Excel2007版本(使用POI的XSSF操作),两者对百万数据的支持如下:
Excel 2003:在POI中使用HSSF对象时,excel 2003最多只允许存储65536条数据,一般用来处理较少的数据量。这时对于百万级别数据,Excel肯定容纳不了。
Excel 2007:当POI升级到XSSF对象时,它可以直接支持excel2007以上版本,因为它采用ooxml格式。这时excel可以支持1048576条数据,单个sheet表就支持近百万条数据。但实际运行时还可能存在问题,原因是执行POI报表所产生的行对象,单元格对象,字体对象,他们都不会销毁,这就导致OOM的风险。

5.2、解决方案分析

对于百万数据量的Excel导入导出,只讨论基于Excel2007的解决方法。在ApachePoi 官方提供了对操作大数据量的导入导出的工具和解决办法,操作Excel2007使用XSSF对象,可以分为三种模式:

java代码解析xml

dom4j:一次性加载xml文件再解析

SAX:逐行加载,逐行解析

用户模式:用户模式有许多封装好的方法操作简单,但创建太多的对象,非常耗内存(之前使用的方法)

事件模式:基于SAX方式解析XML,SAX全称Simple API for XML,它是一个接口,也是一个软件包。它是一种XML解析的替代方法,不同于DOM解析XML文档时把所有内容一次性加载到内存中的方式,它逐行扫描文档,一边扫描,一边解析。

SXSSF对象:是用来生成海量excel数据文件,主要原理是借助临时存储空间生成excel

5.3、原理分析

在实例化SXSSFWorkBook这个对象时,可以指定在内存中所产生的POI导出相关对象的数量(默认100),一旦内存中的对象的个数达到这个指定值时,就将内存中的这些对象的内容写入到磁盘中(XML的文件格式),就可以将这些对象从内存中销毁,以后只要达到这个值,就会以类似的处理方式处理,直至Excel导出完成。

5.4、百万数据的导出

5.4.2、思路分析

导出时使用的是SXSSFWorkBook这个类,一个工作表sheet最多只能放1048576行数据, 当我们的业务数据已超过100万了,一个sheet就不够用了,必须拆分到多个工作表。

导出百万数据时有两个弊端:

1、不能使用模板

2、不能使用太多的样式

也就是说导出的数据太多时必须要放弃一些。

5.4.3、代码实现

UserController代码

@GetMapping(value = "/downLoadMillion",name = "导出用户百万数据的导出")
public void downLoadMillion(Long id,HttpServletRequest request,HttpServletResponse response) throws Exception{userService.downLoadMillion(request,response);
}

UserService代码

public void downLoadMillion(HttpServletRequest request, HttpServletResponse response) throws Exception {
//        创建一个空的工作薄Workbook workbook = new SXSSFWorkbook();//不能一次性查 5百万,可以写循环做分页查询int page = 1;int pageSize=200000;int rowIndex = 1; //每一个工作页sheet的行数int num = 0; //总数据量Row row = null;Cell cell = null;Sheet sheet = null;while (true){  //不停地查询List<User> userList = this.findPage(page,pageSize);if(CollectionUtils.isEmpty(userList)){  //如果查询不到就不再查询了break;}if(num%1000000==0){  //每100W个就重新创建新的sheet和标题rowIndex = 1;//每一个sheet行索引重置为1// 一个sheet 一百万,一共五个sheet       在工作薄中创建一个工作表sheet = workbook.createSheet("第"+((num/1000000)+1)+"个工作表");
//        设置列宽sheet.setColumnWidth(0,8*256);sheet.setColumnWidth(1,12*256);sheet.setColumnWidth(2,15*256);sheet.setColumnWidth(3,15*256);sheet.setColumnWidth(4,30*256);//            处理标题String[] titles = new String[]{"编号","姓名","手机号","入职日期","现住址"};//        创建标题行Row titleRow = sheet.createRow(0);for (int i = 0; i < titles.length; i++) {cell = titleRow.createCell(i);cell.setCellValue(titles[i]);}}//        处理内容for (User user : userList) {row = sheet.createRow(rowIndex);cell = row.createCell(0);cell.setCellValue(user.getId());cell = row.createCell(1);cell.setCellValue(user.getUserName());cell = row.createCell(2);cell.setCellValue(user.getPhone());cell = row.createCell(3);cell.setCellValue(simpleDateFormat.format(user.getHireDate()));cell = row.createCell(4);cell.setCellValue(user.getAddress());rowIndex++;num++;}page++;// 继续查询下一页}
//            导出的文件名称String filename="百万数据.xlsx";
//            设置文件的打开方式和mime类型ServletOutputStream outputStream = response.getOutputStream();response.setHeader( "Content-Disposition", "attachment;filename="  + new String(filename.getBytes(),"ISO8859-1"));response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");workbook.write(outputStream);}

5.4.4、测试结果

导出的这个文档大概需要3-5分钟的时间,有105 MB,内容如下

6.百万数据的导入

事件模式:它逐行扫描文档,一边扫描一边解析。由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中,这对于大型文档的解析是个巨大优势。

1.3 代码实现

1.3.1 步骤分析

(1)设置POI的事件模式
根据Excel获取文件流
根据文件流创建OPCPackage 用来组合读取到的xml 组合出来的数据占用的空间更小
创建XSSFReader对象
(2)Sax解析
自定义Sheet处理器
创建Sax的XmlReader对象
设置Sheet的事件处理器
逐行读取

1.3.2 自定义处理器

package com.itheima.test;import com.itheima.pojo.User;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {//    编号 用户名  手机号  入职日期 现住址private User user=null;@Overridepublic void startRow(int rowIndex) { //每一行的开始, 有一行就只赢一次,有两行就执行两次  rowIndex代表的是每一个sheet的行索引if(rowIndex==0){user = null;}else{user = new User();}}@Override  //处理每一行的所有单元格,有几个单元格就会执行几次public void cell(String cellName, String cellValue, XSSFComment comment) {if(user!=null){String letter = cellName.substring(0, 1);  //每个单元名称的首字母 A  B  Cswitch (letter){//A开头,代表是存放编号case "A":{user.setId(Long.parseLong(cellValue));break;}case "B":{user.setUserName(cellValue);break;}}}}@Overridepublic void endRow(int rowIndex) { //每一行的结束,后会执行,写保存方法if(rowIndex!=0){System.out.println(user);}}
}

1.3.3 自定义解析

在今天的资料中提供了这个类,直接可以拿过来使用

package com.itheima.test;import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;import java.io.InputStream;/*** 自定义Excel解析器*/
public class ExcelParser {public void parse (String path) throws Exception {//1.根据Excel获取OPCPackage对象OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);try {//2.创建XSSFReader对象XSSFReader reader = new XSSFReader(pkg);//3.获取SharedStringsTable对象SharedStringsTable sst = reader.getSharedStringsTable();//4.获取StylesTable对象StylesTable styles = reader.getStylesTable();XMLReader parser = XMLReaderFactory.createXMLReader();// 处理公共属性:Sheet名,Sheet合并单元格,parser.setContentHandler(new XSSFSheetXMLHandler(styles,sst, new SheetHandler(), false));XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) reader.getSheetsData();while (sheets.hasNext()) {InputStream sheetstream = sheets.next();InputSource sheetSource = new InputSource(sheetstream);try {parser.parse(sheetSource);} finally {sheetstream.close();}}} finally {pkg.close();}}
}

1.3.4 测试

用户模式下读取测试Excel文件直接内存溢出,测试Excel文件映射到内存中还是占用了不少内存;事件模式下可以流畅的运行。

使用事件模型解析

public class POIDemo5 {public static void main(String[] args) throws Exception{new ExcelParser().parse("C:\\Users\\syl\\Desktop\\百万用户数据的导出.xlsx");}
}

3、POI导出word

3.1 需求

把页面展示的内容导出到word中。

3.2 分析

3.2.1 POI操作Word的API介绍

poi对低版本的doc本身支持的就不好所以我们直接说高版本的docx版本的api。

1、poi操作word正文

XWPFDocument代表一个docx文档,其可以用来读docx文档,也可以用来写docx文档

一个文档包含多个段落,一个段落包含多个Runs文本,一个Runs包含多个Run,Run是文档的最小单元

  • 获取所有段落:List paragraphs = word.getParagraphs();

  • 获取一个段落中的所有片段Runs:List xwpfRuns = xwpfParagraph.getRuns();

  • 获取一个Runs中的一个Run:XWPFRun run = xwpfRuns.get(index);

2、poi操作word中的表格

一个文档包含多个表格,一个表格包含多行,一行包含多列单元格

  • 获取所有表格:List xwpfTables = doc.getTables();

  • 获取一个表格中的所有行:List xwpfTableRows = xwpfTable.getRows();

  • 获取一行中的所有列:List xwpfTableCells = xwpfTableRow.getTableCells();

  • 获取一格里的内容:List paragraphs = xwpfTableCell.getParagraphs();

之后和正文段落一样

3.2.2 思路分析

首先我们先制作一个word模板,把动态的内容先写特殊字符然后替换,表格的话需要我们自己创建然后向表格中放内容。

3.3 代码实现

第一步:制作模板(模板内容如上图所示),放入到项目中

第三步:完成导出word功能

Controller代码

@GetMapping(value = "/downloadContract",name = "导出用户合同")
public void downloadContract(Long id,HttpServletResponse response) throws Exception{userService.downloadContract(id,response);
}

UserService代码

先准备两个方法,一个是想指定的单元格中放入图片,另一个是 复制word中表格的行

//    向单元格中写入图片
private void setCellImage(XWPFTableCell cell, File imageFile) {XWPFRun run = cell.getParagraphs().get(0).createRun();//        InputStream pictureData, int pictureType, String filename, int width, int heighttry(FileInputStream inputStream = new FileInputStream(imageFile)) {run.addPicture(inputStream,XWPFDocument.PICTURE_TYPE_JPEG,imageFile.getName(), Units.toEMU(100),Units.toEMU(50));} catch (Exception e) {e.printStackTrace();}}//    用于深克隆行
private void copyRow(XWPFTable xwpfTable, XWPFTableRow sourceRow, int rowIndex) {XWPFTableRow targetRow = xwpfTable.insertNewTableRow(rowIndex);//设置行属性targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());//        获取源行的单元格List<XWPFTableCell> cells = sourceRow.getTableCells();if(CollectionUtils.isEmpty(cells)){return;}XWPFTableCell targetCell = null;for (XWPFTableCell cell : cells) {targetCell = targetRow.addNewTableCell();//            附上单元格的样式//            单元格的属性targetCell.getCTTc().setTcPr(cell.getCTTc().getTcPr());//段落属性   targetCell.getParagraphs().get(0).getCTP().setPPr(cell.getParagraphs().get(0).getCTP().getPPr());}
}

完成导出主体方法

/*** 下载用户合同数据* @param id*/
public void downloadContract(Long id,HttpServletResponse response) throws Exception {//        1、读取到模板File rootFile = new File(ResourceUtils.getURL("classpath:").getPath()); //获取项目的根目录File templateFile = new File(rootFile, "/word_template/contract_template.docx");XWPFDocument word = new XWPFDocument(new FileInputStream(templateFile));//        2、查询当前用户User--->mapUser user = this.findById(id);Map<String,String> params = new HashMap<>();params.put("userName",user.getUserName());params.put("hireDate",simpleDateFormat.format(user.getHireDate()));params.put("address",user.getAddress());//        3、替换数据//         处理正文开始//获取所有段落List<XWPFParagraph> paragraphs = word.getParagraphs();//获取段落中的所有片段for (XWPFParagraph paragraph : paragraphs) {List<XWPFRun> runs = paragraph.getRuns();//获取片段所有的文本for (XWPFRun run : runs) {String text = run.getText(0);//判断文本中的keyfor (String key : params.keySet()) {if(text.contains(key)){//替换内容run.setText(text.replaceAll(key,params.get(key)),0);}}}}//         处理正文结束//      处理表格开始     名称	价值	是否需要归还	照片 数据库中查出来List<Resource> resourceList = user.getResourceList(); //表格中需要的数据//获取第一个表格XWPFTable xwpfTable = word.getTables().get(0);XWPFTableRow row = xwpfTable.getRow(0);int rowIndex = 1;for (Resource resource : resourceList) {//        添加行//            xwpfTable.addRow(row);//第一个参数拷贝那个table//第二个参数,拷贝第几行//第三个参数,拷贝的放到第几行copyRow(xwpfTable,row,rowIndex);XWPFTableRow row1 = xwpfTable.getRow(rowIndex);row1.getCell(0).setText(resource.getName());row1.getCell(1).setText(resource.getPrice().toString());row1.getCell(2).setText(resource.getNeedReturn()?"需求":"不需要");File imageFile = new File(rootFile,"/static"+resource.getPhoto());setCellImage(row1.getCell(3),imageFile);rowIndex++;}//     处理表格开始结束//        4、导出wordString filename = "员工(" + user.getUserName() + ")合同.docx";response.setHeader("content-disposition", "attachment;filename=" + new String(filename.getBytes(), "ISO8859-1"));response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");word.write(response.getOutputStream());
}

更多推荐

apache PoI excel world 等

本文发布于:2024-03-08 03:44:31,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1719742.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:PoI   apache   world   excel

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!