Java==POI报表详解

一.POI报表

在企业开发中,Excel报表是一种最常见的报表需求,Excel报表开发一般分为两种形式:

(1)为了方便操作,基于Excel的报表批量上传数据

(2)通过java代码,快速生成Excel报表

 

POI报表简介:

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

Excel分为两个大的版本Excel2003和Excel2007及以上两个版本:

区别 Excel 2003 Excel 2007及以上
扩展名 xls xlsx
数据结构 二进制格式 xml格式
单sheet数据量 行:65535、列:256 行:1048576、列:16384
特点 存储容量有限 基于xml压缩,占用空间小,操作效率高

API对象介绍

  • 工作簿(表): WorkBook (HssfWordBook:2003版本,XssfWorkBook:2007及以上)

  • 页:Sheet

  • 行:Row

  • 单元格:Cell

二.快速入门

1.创建项目,环境搭建,导入pom.xml的坐标文件


    
    
        org.apache.poi
        poi
        4.0.1
    
    
    
        org.apache.poi
        poi-ooxml
        4.0.1
    
    
    
        org.apache.poi
        poi-ooxml-schemas
        4.0.1
    

2.通过java创建Excel

public class CreateDemo {


    /*
        通过java代码创建excel
     */

    public static void main(String[] args)throws Exception {
        // 1.创建工作表
        /*
            HSSFWorkbook:2003版本
            XSSFWorkbook:2007及以上版本
            SXSSFWorkbook:处理百万数据
         */
        Workbook wb = new XSSFWorkbook();
        // 2.创建第一页
        Sheet sheet = wb.createSheet("poi入门");

        // 指定第三列宽度 24
        sheet.setColumnWidth(2, 24*256); // poi的设计者把我们设置的宽度 除以256

        // 3.创建第三行
        Row row = sheet.createRow(2);// 指定行索引行
        // 4.创建第三个单元格
        Cell cell = row.createCell(2);// 指定索引列
        // 5.填充数据
        cell.setCellValue("北京欢迎你");
        // 6.保存到本地
        FileOutputStream out = new FileOutputStream("d:/demo.xlsx");
        wb.write(out);
    }
}

3.通过java解析Excel

注意:excel单元格类型为:

  • string

  • number

  • boolean

  • date

public class ParseDemo {
    /*
        通过java代码解析excel
     */

    public static void main(String[] args) throws IOException {
        // 1.加载已有的excel工作表
        Workbook wb = new XSSFWorkbook("C:\\Users\\wsl\\Desktop\\demo.xlsx");
        // 2.获取第一页
        Sheet sheet = wb.getSheetAt(0);
        // 3.遍历行
        // sheet.getLastRowNum(); 获取最后一个有数据行的索引号(索引是从0开始),返回值4
        for (int i = 0; i < sheet.getLastRowNum() + 1; i++) {
            Row row = sheet.getRow(i);
            // 4.遍历单元格
            // row.getLastCellNum(); 获取最后一个有数据列的编号(编号是从1开始),返回值6
            for (int j = 2; j < row.getLastCellNum(); j++) {
                Cell cell = row.getCell(j);
                // 5.获取单元格的数据
                // System.out.print(cell.getStringCellValue()+"   ");
                System.out.print(getCellValue(cell)+" ");

            }
            System.out.println("  ");// 换行
        }
    }


    //解析每个单元格的数据
    public static Object getCellValue(Cell cell) {
        Object obj = null;
        CellType cellType = cell.getCellType(); //获取单元格数据类型
        switch (cellType) {
            case STRING: {
                obj = cell.getStringCellValue();//字符串
                break;
            }
            //excel默认将日期也理解为数字
            case NUMERIC: {
                if (DateUtil.isCellDateFormatted(cell)) {
                    obj = cell.getDateCellValue();//日期
                } else {
                    obj = cell.getNumericCellValue(); // 数字
                }
                break;
            }
            case BOOLEAN: {
                obj = cell.getBooleanCellValue(); // 布尔
                break;
            }
            default: {
                break;
            }
        }
        return obj;
    }
}

三.案例:

按规定导出出货表,导出出货表的excel

自定义SQL语句:

-- 根据船期+企业id查询
SELECT 
  c.`custom_name` AS customName,
  c.`contract_no` AS contractNo,
  cp.`product_no` AS productNo,
  cp.`cnumber`,
  cp.`factory_name` AS factoryName,
  c.`delivery_period` AS deliveryPeriod,
  c.`ship_time` AS shipTime,
  c.`trade_terms` AS tradeTerms
FROM `co_contract` c
	INNER JOIN `co_contract_product` cp ON c.`id` = cp.`contract_id`
	WHERE DATE_FORMAT(c.`ship_time`,'%Y-%m') = '2015-01' AND c.`company_id` = '1'

自定义VO对象

import java.io.Serializable;
import java.util.Date;

public class ContractProductVo implements Serializable {

	private String customName;		//客户名称
	private String contractNo;		//合同号,订单号
	private String productNo;		//货号
	private Integer cnumber;		//数量
	private String factoryName;		//厂家名称,冗余字段
	private Date deliveryPeriod;	//交货期限
	private Date shipTime;			//船期
	private String tradeTerms;		//贸易条款

	public String getCustomName() {
		return customName;
	}

	public void setCustomName(String customName) {
		this.customName = customName;
	}

	public String getContractNo() {
		return contractNo;
	}

	public void setContractNo(String contractNo) {
		this.contractNo = contractNo;
	}

	public String getProductNo() {
		return productNo;
	}

	public void setProductNo(String productNo) {
		this.productNo = productNo;
	}

	public Integer getCnumber() {
		return cnumber;
	}

	public void setCnumber(Integer cnumber) {
		this.cnumber = cnumber;
	}

	public String getFactoryName() {
		return factoryName;
	}

	public void setFactoryName(String factoryName) {
		this.factoryName = factoryName;
	}

	public Date getDeliveryPeriod() {
		return deliveryPeriod;
	}

	public void setDeliveryPeriod(Date deliveryPeriod) {
		this.deliveryPeriod = deliveryPeriod;
	}

	public Date getShipTime() {
		return shipTime;
	}

	public void setShipTime(Date shipTime) {
		this.shipTime = shipTime;
	}

	public String getTradeTerms() {
		return tradeTerms;
	}

	public void setTradeTerms(String tradeTerms) {
		this.tradeTerms = tradeTerms;
	}

	@Override
	public String toString() {
		return "ContractProductVo{" +
				"customName='" + customName + '\'' +
				", contractNo='" + contractNo + '\'' +
				", productNo='" + productNo + '\'' +
				", cnumber=" + cnumber +
				", factoryName='" + factoryName + '\'' +
				", deliveryPeriod=" + deliveryPeriod +
				", shipTime=" + shipTime +
				", tradeTerms='" + tradeTerms + '\'' +
				'}';
	}
}

下载工具类:

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class DownloadUtils {
	
	/**
	 * @param filePath 要下载的文件路径
	 * @param returnName 返回的文件名
	 * @param response HttpServletResponse
	 * @param delFlag 是否删除文件
	 */
	protected static void download(String filePath,String returnName,HttpServletResponse response,boolean delFlag){
		prototypeDownload(new File(filePath), returnName, response, delFlag);
	}


	/**
	 * @param file 要下载的文件
	 * @param returnName 返回的文件名
	 * @param response HttpServletResponse
	 * @param delFlag 是否删除文件
	 */
	protected static void download(File file,String returnName,HttpServletResponse response,boolean delFlag){
		prototypeDownload(file, returnName, response, delFlag);
	}
	
	/**
	 * @param file 要下载的文件
	 * @param returnName 返回的文件名
	 * @param response HttpServletResponse
	 * @param delFlag 是否删除文件
	 */
	public static void prototypeDownload(File file,String returnName,HttpServletResponse response,boolean delFlag){
		// 下载文件
		FileInputStream inputStream = null;
		ServletOutputStream outputStream = null;
		try {
			if(!file.exists()) return;
			response.reset();
			//设置响应类型	PDF文件为"application/pdf",WORD文件为:"application/msword", EXCEL文件为:"application/vnd.ms-excel"。  
			response.setContentType("application/octet-stream;charset=utf-8");

			//设置响应的文件名称,并转换成中文编码
			//returnName = URLEncoder.encode(returnName,"UTF-8");
			returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1"));	//保存的文件名,必须和页面编码一致,否则乱码
			
			//attachment作为附件下载;inline客户端机器有安装匹配程序,则直接打开;注意改变配置,清除缓存,否则可能不能看到效果
			response.addHeader("Content-Disposition",   "attachment;filename="+returnName);  
			
			//将文件读入响应流
			inputStream = new FileInputStream(file);
			outputStream = response.getOutputStream();
			int length = 1024;
			int readLength=0;
			byte buf[] = new byte[1024];
			readLength = inputStream.read(buf, 0, length);
			while (readLength != -1) {
				outputStream.write(buf, 0, readLength);
				readLength = inputStream.read(buf, 0, length);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				outputStream.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				outputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				inputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			//删除原文件
			
			if(delFlag) {				
				file.delete();
			}
		}
	}

	/**
	 * @param byteArrayOutputStream 将文件内容写入ByteArrayOutputStream
	 * @param response HttpServletResponse	写入response
	 * @param returnName 返回的文件名
	 */
	public static void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException{
		response.setContentType("application/octet-stream;charset=utf-8");
		returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1"));			//保存的文件名,必须和页面编码一致,否则乱码
		response.addHeader("Content-Disposition",   "attachment;filename=" + returnName);  
		response.setContentLength(byteArrayOutputStream.size());
		
		ServletOutputStream outputstream = response.getOutputStream();	//取得输出流
		byteArrayOutputStream.writeTo(outputstream);					//写到输出流
		byteArrayOutputStream.close();									//关闭
		outputstream.flush();											//刷数据
	}
}

基础数据:

@Controller
@RequestMapping(value = "/cargo/contract", name = "出货表导出")
public class OutContractController extends BaseController {

    @Reference // alibaba
    private ContractService contractService;

    @RequestMapping(value = "/print", name = "跳转到查询页面")
    public String print() {
        return "cargo/print/contract-print";

    }

    @RequestMapping(value = "/printExcel", name = "出货表excel下载")
    public void printExcel(String shipTime) {
        //i.查询vo数据集合
        List list = contractService.findByShipTime(shipTime,getCompanyId());
        System.out.println(list.size());
        //ii.将vo对象封装到excel文件中

        //iii.完成文件下载
    }
}

ContractService

// 根据船期查询出货表
@Override
public List findByShipTime(String shipTime, String companyId) {
    return contractDao.findByShipTime(shipTime,companyId);
}

ContractDao

// 根据船期查询出货表
List findByShipTime(@Param("shipTime") String shipTime, @Param("companyId") String companyId);

 

导出样式:

Excel下载:

(1)不带样式:

@Controller
@RequestMapping(value = "/cargo/contract", name = "出货表导出")
public class OutContractController extends BaseController {

    @Reference // alibaba
    private ContractService contractService;

    @RequestMapping(value = "/print", name = "跳转到查询页面")
    public String print() {
        return "cargo/print/contract-print";

    }

    @RequestMapping(value = "/printExcel", name = "出货表excel下载")
    public void printExcel(String shipTime) throws Exception {
        //i.查询vo数据集合
        List list = contractService.findByShipTime(shipTime, getCompanyId());
        System.out.println(list.size());
        //ii.将vo对象封装到excel文件中
        // 1.创建Workbook 2007版本
        Workbook wb = new XSSFWorkbook();
        // 2.创建第一页
        Sheet sheet = wb.createSheet("Sheet1");

        // 指定列宽度
        sheet.setColumnWidth(1, 20 * 256);
        sheet.setColumnWidth(2, 15 * 256);
        sheet.setColumnWidth(3, 15 * 256);
        sheet.setColumnWidth(4, 15 * 256);
        sheet.setColumnWidth(5, 15 * 256);
        sheet.setColumnWidth(6, 15 * 256);
        sheet.setColumnWidth(7, 15 * 256);

        // 合并单元格 行开始、行结束、列开始、列结束 :取值都是索引
        sheet.addMergedRegion(new CellRangeAddress(0, 0, 1, 8));

        // 3.第一行指定大标题
        // 3.1 创建第一行
        Row row = sheet.createRow(0);
        row.setHeightInPoints(36);// 行高
        // 3.2 创建第二个单元格
        Cell cell = row.createCell(1);
        // 3.3 填充大标题
        String bigTile = shipTime.replace("-", "年").replace("年0", "年") + "月份出货表";
        cell.setCellValue(bigTile);
        // 4.第二行指定列标题
        // 4.1 创建第二行
        row = sheet.createRow(1);
        row.setHeightInPoints(26);// 行高
        // 4.2 指定列标题数组
        String[] colTitile = new String[]{"", "客户", "合同号", "货号", "数量", "工厂", "工厂交期", "船期", "贸易条款"};
        // 4.3 遍历数组设置列标题
        for (int i = 1; i < colTitile.length; i++) {
            cell = row.createCell(i);
            cell.setCellValue(colTitile[i]);
        }
        // 5.从第三行开始,遍历vo数据集合,填充数据行
        int index = 2;
        for (ContractProductVo vo : list) {
            row = sheet.createRow(index);
            row.setHeightInPoints(24);// 行高
            // 客户单元格
            cell = row.createCell(1);
            cell.setCellValue(vo.getCustomName());
            // 合同号单元格
            cell = row.createCell(2);
            cell.setCellValue(vo.getContractNo());
            // 货号单元格
            cell = row.createCell(3);
            cell.setCellValue(vo.getProductNo());
            // 数量单元格
            cell = row.createCell(4);
            cell.setCellValue(vo.getCnumber());
            // 工厂单元格
            cell = row.createCell(5);
            cell.setCellValue(vo.getFactoryName());
            // 工厂交期单元格
            cell = row.createCell(6);
            cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getDeliveryPeriod()));
            // 船期单元格
            cell = row.createCell(7);
            cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getShipTime()));
            // 贸易条款单元格
            cell = row.createCell(8);
            cell.setCellValue(vo.getTradeTerms());
            index++;
        }

        //iii.完成文件下载
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 将workbook的内容输出到字节数组输出中
        wb.write(byteArrayOutputStream);
        DownloadUtils.download(byteArrayOutputStream, response, bigTile + ".xlsx");
    }
}

(2)带样式

@Controller
@RequestMapping(value = "/cargo/contract", name = "出货表导出")
public class OutContractController extends BaseController {

    @Reference // alibaba
    private ContractService contractService;

    @RequestMapping(value = "/print", name = "跳转到查询页面")
    public String print() {
        return "cargo/print/contract-print";

    }

    @RequestMapping(value = "/printExcel", name = "出货表excel下载")
    public void printExcel(String shipTime) throws Exception {
        //i.查询vo数据集合
        List list = contractService.findByShipTime(shipTime, getCompanyId());
        System.out.println(list.size());
        //ii.将vo对象封装到excel文件中
        // 1.创建Workbook 2007版本
        Workbook wb = new XSSFWorkbook();
        // 2.创建第一页
        Sheet sheet = wb.createSheet("Sheet1");

        // 指定列宽度
        sheet.setColumnWidth(1, 20 * 256);
        sheet.setColumnWidth(2, 15 * 256);
        sheet.setColumnWidth(3, 15 * 256);
        sheet.setColumnWidth(4, 15 * 256);
        sheet.setColumnWidth(5, 15 * 256);
        sheet.setColumnWidth(6, 15 * 256);
        sheet.setColumnWidth(7, 15 * 256);

        // 合并单元格 行开始、行结束、列开始、列结束 :取值都是索引
        sheet.addMergedRegion(new CellRangeAddress(0, 0, 1, 8));

        // 3.第一行指定大标题
        // 3.1 创建第一行
        Row row = sheet.createRow(0);
        row.setHeightInPoints(36);// 行高
        // 3.2 创建第二个单元格
        Cell cell = row.createCell(1);
        // 3.3 填充大标题
        String bigTile = shipTime.replace("-", "年").replace("年0", "年") + "月份出货表";
        cell.setCellValue(bigTile);

        cell.setCellStyle(bigTitle(wb)); // 大标题样式
        // 4.第二行指定列标题
        // 4.1 创建第二行
        row = sheet.createRow(1);
        row.setHeightInPoints(26);// 行高
        // 4.2 指定列标题数组
        String[] colTitile = new String[]{"", "客户", "合同号", "货号", "数量", "工厂", "工厂交期", "船期", "贸易条款"};
        // 4.3 遍历数组设置列标题
        for (int i = 1; i < colTitile.length; i++) {
            cell = row.createCell(i);
            cell.setCellValue(colTitile[i]);
            cell.setCellStyle(title(wb)); // 列标题样式
        }
        // 5.从第三行开始,遍历vo数据集合,填充数据行
        int index = 2;
        for (ContractProductVo vo : list) {
            row = sheet.createRow(index);
            row.setHeightInPoints(24);// 行高
            // 客户单元格
            cell = row.createCell(1);
            cell.setCellValue(vo.getCustomName());
            cell.setCellStyle(text(wb)); // 数据样式
            // 合同号单元格
            cell = row.createCell(2);
            cell.setCellValue(vo.getContractNo());
            cell.setCellStyle(text(wb)); // 数据样式
            // 货号单元格
            cell = row.createCell(3);
            cell.setCellValue(vo.getProductNo());
            cell.setCellStyle(text(wb)); // 数据样式
            // 数量单元格
            cell = row.createCell(4);
            cell.setCellValue(vo.getCnumber());
            cell.setCellStyle(text(wb)); // 数据样式
            // 工厂单元格
            cell = row.createCell(5);
            cell.setCellValue(vo.getFactoryName());
            cell.setCellStyle(text(wb)); // 数据样式
            // 工厂交期单元格
            cell = row.createCell(6);
            cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getDeliveryPeriod()));
            cell.setCellStyle(text(wb)); // 数据样式
            // 船期单元格
            cell = row.createCell(7);
            cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getShipTime()));
            cell.setCellStyle(text(wb)); // 数据样式
            // 贸易条款单元格
            cell = row.createCell(8);
            cell.setCellValue(vo.getTradeTerms());
            cell.setCellStyle(text(wb)); // 数据样式
            index++;
        }

        //iii.完成文件下载
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 将workbook的内容输出到字节数组输出中
        wb.write(byteArrayOutputStream);
        DownloadUtils.download(byteArrayOutputStream, response, bigTile + ".xlsx");
    }


    //大标题的样式
    public CellStyle bigTitle(Workbook wb){
        CellStyle style = wb.createCellStyle();
        Font font = wb.createFont();
        font.setFontName("宋体");
        font.setFontHeightInPoints((short)16);
        font.setBold(true);//字体加粗
        style.setFont(font);
        style.setAlignment(HorizontalAlignment.CENTER);				//横向居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);		//纵向居中
        return style;
    }

    //小标题的样式
    public CellStyle title(Workbook wb){
        CellStyle style = wb.createCellStyle();
        Font font = wb.createFont();
        font.setFontName("黑体");
        font.setFontHeightInPoints((short)12);
        style.setFont(font);
        style.setAlignment(HorizontalAlignment.CENTER);				//横向居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);		//纵向居中
        style.setBorderTop(BorderStyle.THIN);						//上细线
        style.setBorderBottom(BorderStyle.THIN);					//下细线
        style.setBorderLeft(BorderStyle.THIN);						//左细线
        style.setBorderRight(BorderStyle.THIN);						//右细线
        return style;
    }

    //文字样式
    public CellStyle text(Workbook wb){
        CellStyle style = wb.createCellStyle();
        Font font = wb.createFont();
        font.setFontName("Times New Roman");
        font.setFontHeightInPoints((short)10);

        style.setFont(font);

        style.setAlignment(HorizontalAlignment.LEFT);				//横向居左
        style.setVerticalAlignment(VerticalAlignment.CENTER);		//纵向居中
        style.setBorderTop(BorderStyle.THIN);						//上细线
        style.setBorderBottom(BorderStyle.THIN);					//下细线
        style.setBorderLeft(BorderStyle.THIN);						//左细线
        style.setBorderRight(BorderStyle.THIN);						//右细线

        return style;
    }
}

(3)使用模板导出

自定义生成Excel报表文件还是有很多不尽如意的地方,特别是针对复杂报表头,单元格样式,字体等操作。手写这些代码不仅费时费力,有时候效果还不太理想。那怎么样才能更方便的对报表样式,报表头进行处理呢?

答案:使用已经准备好的Excel模板,开发者只需要关注模板中的数据即可。

提取模板:

Java==POI报表详解_第1张图片

模板打印

// 模板下载
@RequestMapping(value = "/printExcelTemplate", name = "出货表excel模板下载")
public void printExcelTemplate(String shipTime) throws Exception {
    // i.查询vo数据集合
    List list = contractService.findByShipTime(shipTime, getCompanyId());
    // ii.加载模板,填充数据,生成excel
    // 1.获取模板真实路径
    String realPath = session.getServletContext().getRealPath("/make/xlsprint/tOUTPRODUCT.xlsx");
    // 2.基于模板创建workbook
    Workbook wb = new XSSFWorkbook(realPath);
    // 3.获取第一页
    Sheet sheet = wb.getSheetAt(0);
    // 4.重置大标题数据
    Row row = sheet.getRow(0);
    Cell cell = row.getCell(1);
    String bigTile = shipTime.replace("-", "年").replace("年0", "年") + "月份出货表";
    cell.setCellValue(bigTile);
    // 5.列标题(略)
    // 6.抽取数据行的样式
    // 6.1 获取第三行对象
    row = sheet.getRow(2);
    // 6.2 定义样式存储数组
    CellStyle[] css = new CellStyle[9]; // null,客人,合同号,货号
    // 6.3 遍历第三行
    for (int i = 1; i < row.getLastCellNum(); i++) {
        // 获取单元格对象
        cell = row.getCell(i);
        // 获取样式,并设置到数组
        css[i] = cell.getCellStyle();
    }
    // 7.遍历vo数据集合,填充数据
    int index = 2;
    for (ContractProductVo vo : list) {
        row = sheet.createRow(index);
        // 客户单元格
        cell = row.createCell(1);
        cell.setCellValue(vo.getCustomName());
        cell.setCellStyle(css[1]); // 数据样式
        // 合同号单元格
        cell = row.createCell(2);
        cell.setCellValue(vo.getContractNo());
        cell.setCellStyle(css[2]); // 数据样式
        // 货号单元格
        cell = row.createCell(3);
        cell.setCellValue(vo.getProductNo());
        cell.setCellStyle(css[3]); // 数据样式
        // 数量单元格
        cell = row.createCell(4);
        cell.setCellValue(vo.getCnumber());
        cell.setCellStyle(css[4]); // 数据样式
        // 工厂单元格
        cell = row.createCell(5);
        cell.setCellValue(vo.getFactoryName());
        cell.setCellStyle(css[5]); // 数据样式
        // 工厂交期单元格
        cell = row.createCell(6);
        cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getDeliveryPeriod()));
        cell.setCellStyle(css[6]); // 数据样式
        // 船期单元格
        cell = row.createCell(7);
        cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getShipTime()));
        cell.setCellStyle(css[7]); // 数据样式
        // 贸易条款单元格
        cell = row.createCell(8);
        cell.setCellValue(vo.getTradeTerms());
        cell.setCellStyle(css[8]); // 数据样式
        index++;
    }

    //iii.完成文件下载
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    // 将workbook的内容输出到字节数组输出中
    wb.write(byteArrayOutputStream);
    DownloadUtils.download(byteArrayOutputStream, response, bigTile + ".xlsx");
}

总结:

  1. 加载模板

  2. 重置模板静态数据(大标题)

  3. 抽取模板样式(数据行)

  4. 填充vo数据

 

四.百万数据Excel

(1)概述

在真实的环境下,我们导出导入的数据量在几十万甚至上百万条,Excel 2007虽然可以支持1048576条数据存储;但实际运行时还可能存在问题,原因是执行POI报表所产生的行对象,单元格对象,字体对象,他们都不会销毁,这就导致OOM(内存溢出)的风险。

JDK性能监控工具

这里我们使用JDK提供的性能工具Jvisualvm来监视程序的运行情况,包括CPU,垃圾回收,内存的分配和使用情况,这让程序的运行阶段变得更加可控。

Jvisualvm工具位于JAVA_HOME/bin目录下,直接双击就可以打开该程序。

Java==POI报表详解_第2张图片

 

Java==POI报表详解_第3张图片

 

百万数据报表导出

Java==POI报表详解_第4张图片

SxssfWorkBookx对象:是用来生成海量excel数据文件,主要原理是借助临时存储空间生成excel(当内存中的对象数量到达一定数量后,写入到临时文件,销毁内存对象,直至Excel导出完成)

缺点

  • 不能使用模板

  • 样式对象不能超过64000个

注意:

比较:

使用:XSSFWorkbook对象,造成内存一致增长

Java==POI报表详解_第5张图片

使用:SXSSFWorkbook对象,借助临时文件,内存有增有减

Java==POI报表详解_第6张图片

 

(2)百万数据报表导入

Java==POI报表详解_第7张图片

**POI针对Excel文件解析提供了两种方式:**

用户模式:通过POI提供的基础方法完成excel解析,操作简单,但创建太多的对象,消耗内存

Java==POI报表详解_第8张图片

事件模式:基于SAX方式解析XML,它逐行扫描文档,一边扫描,一边解析。

Java==POI报表详解_第9张图片

 

通过用户模式解析:即Dom方式

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.awt.*;
import java.io.IOException;

public class ParseDemo {
    /*
        通过java代码解析excel
     */

    public static void main(String[] args) throws IOException {
        // 1.加载已有的excel工作表
        Workbook wb = new XSSFWorkbook("C:\\\\Users\\\\Desktop\\\\2020年2月份出货表.xlsx");
        // 2.获取第一页
        Sheet sheet = wb.getSheetAt(0);
        // 3.遍历行
        // sheet.getLastRowNum(); 获取最后一个有数据行的索引号(索引是从0开始),返回值4
        for (int i = 0; i < sheet.getLastRowNum() + 1; i++) {
            Row row = sheet.getRow(i);
            // 4.遍历单元格
            // row.getLastCellNum(); 获取最后一个有数据列的编号(编号是从1开始),返回值6
            for (int j = 2; j < row.getLastCellNum(); j++) {
                Cell cell = row.getCell(j);
                // 5.获取单元格的数据
                // System.out.print(cell.getStringCellValue()+"   ");
                System.out.print(getCellValue(cell)+" ");

            }
            System.out.println("  ");// 换行
        }
    }


    //解析每个单元格的数据
    public static Object getCellValue(Cell cell) {
        Object obj = null;
        CellType cellType = cell.getCellType(); //获取单元格数据类型
        switch (cellType) {
            case STRING: {
                obj = cell.getStringCellValue();//字符串
                break;
            }
            //excel默认将日期也理解为数字
            case NUMERIC: {
                if (DateUtil.isCellDateFormatted(cell)) {
                    obj = cell.getDateCellValue();//日期
                } else {
                    obj = cell.getNumericCellValue(); // 数字
                }
                break;
            }
            case BOOLEAN: {
                obj = cell.getBooleanCellValue(); // 布尔
                break;
            }
            default: {
                break;
            }
        }
        return obj;
    }
}

通过SAX方式解析:重要:

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
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;

public class ExcelParse {

    public static void parse(String path) throws Exception {
        //解析器
        SheetHandler hl = new SheetHandler();
        //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();
            // 处理公共属性
            parser.setContentHandler(new XSSFSheetXMLHandler(styles, sst, hl,
                    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();
        }
    }

    public static void main(String[] args) throws Exception {
        parse("C:\\Users\\Desktop\\2020年2月份出货表.xlsx");
    }
}
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;

public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {

    // 行解析开始
    @Override
    public void startRow(int rowNum) {
        if (rowNum > 2) {
            System.out.println("开启数据解析");
        }
    }

    // 行解析结束
    @Override
    public void endRow(int rowNum) {
        System.out.println(""); // 手动换行
    }

    // 获取单元格数据
    /*
        cellReference:单元格编号(C3、D5、E8),可以判断数据的类型,方便取值
        formattedValue:单元格数据(内容)
        comment:单元格批注(注释)

     */
    @Override
    public void cell(String cellReference, String formattedValue, XSSFComment comment) {
        System.out.print(formattedValue+"  ");
    }
}

pom.xml

    
        
        
            org.apache.poi
            poi
            4.0.1
        
        
        
            org.apache.poi
            poi-ooxml
            4.0.1
        
        
        
            org.apache.poi
            poi-ooxml-schemas
            4.0.1
        
    

    
        
            
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.1
                
                    1.8
                    1.8
                    UTF-8
                
            
        
    

 

资源扩展:

(1)EasyPOI

EasyPOI

easypoi功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员,就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板语言(熟悉的表达式语法),完成以前复杂的写法。

入门案例:


    cn.afterturn
    easypoi-base
    3.2.0


    cn.afterturn
    easypoi-web
    3.2.0


    cn.afterturn
    easypoi-annotation
    3.2.0

定义实体

public class ContractProductVo implements Serializable {

    @Excel(name = "客户")
    private String customName;        //客户名称
    @Excel(name = "合同号")
    private String contractNo;        //合同号,订单号
    @Excel(name = "货号")
    private String productNo;        //货号
    @Excel(name = "数量",type = 10)
    private Integer cnumber;        //数量
    @Excel(name = "工厂")
    private String factoryName;        //厂家名称
    @Excel(name = "工厂交期", format = "yyyy-MM-dd",width = 15)
    private Date deliveryPeriod;    //交货期限
    @Excel(name = "船期", format = "yyyy-MM-dd",width = 15)
    private Date shipTime;            //船期
    @Excel(name = "贸易条款")
    private String tradeTerms;        //贸易条款

    public String getCustomName() {
        return customName;
    }

    public void setCustomName(String customName) {
        this.customName = customName;
    }

    public String getContractNo() {
        return contractNo;
    }

    public void setContractNo(String contractNo) {
        this.contractNo = contractNo;
    }

    public String getProductNo() {
        return productNo;
    }

    public void setProductNo(String productNo) {
        this.productNo = productNo;
    }

    public Integer getCnumber() {
        return cnumber;
    }

    public void setCnumber(Integer cnumber) {
        this.cnumber = cnumber;
    }

    public String getFactoryName() {
        return factoryName;
    }

    public void setFactoryName(String factoryName) {
        this.factoryName = factoryName;
    }

    public Date getDeliveryPeriod() {
        return deliveryPeriod;
    }

    public void setDeliveryPeriod(Date deliveryPeriod) {
        this.deliveryPeriod = deliveryPeriod;
    }

    public Date getShipTime() {
        return shipTime;
    }

    public void setShipTime(Date shipTime) {
        this.shipTime = shipTime;
    }

    public String getTradeTerms() {
        return tradeTerms;
    }

    public void setTradeTerms(String tradeTerms) {
        this.tradeTerms = tradeTerms;
    }

    @Override
    public String toString() {
        return "ContractProductVo{" +
                "customName='" + customName + '\'' +
                ", contractNo='" + contractNo + '\'' +
                ", productNo='" + productNo + '\'' +
                ", cnumber=" + cnumber +
                ", factoryName='" + factoryName + '\'' +
                ", deliveryPeriod=" + deliveryPeriod +
                ", shipTime=" + shipTime +
                ", tradeTerms='" + tradeTerms + '\'' +
                '}';
    }
}

创建excel

public class CreateExcel {

    public static void main(String[] args)throws Exception {
        // 1.定义excel配置
        ExportParams exportParams = new ExportParams();
        exportParams.setSheetName("Sheet1");// 页名称
        exportParams.setTitle("2015年01月份出货表");// 大标题
        exportParams.setType(ExcelType.XSSF);// 07及以上版本
        // 2.创建workbook对象
        Workbook wb = ExcelExportUtil.exportExcel(exportParams, ContractProductVo.class, getData());
        // 3.导出到文件
        FileOutputStream out = new FileOutputStream("d:/demo.xlsx");
        wb.write(out);
        wb.close();
    }

    //从数据库中查找了数据
    public static List getData() {
        List list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            ContractProductVo vo = new ContractProductVo();
            vo.setCustomName("客户"+i);
            vo.setContractNo("合同号"+i);
            vo.setProductNo("货号"+i);
            vo.setCnumber(i);
            vo.setFactoryName("工厂"+i);
            vo.setDeliveryPeriod(new Date());
            vo.setShipTime(new Date());
            vo.setTradeTerms("条款"+i);
            list.add(vo);
        }
        return list;
    }
}

解析Excel

public class ParseExcel {

    public static void main(String[] args) {
        // 1.解析excel的配置参数
        ImportParams params = new ImportParams();
        params.setTitleRows(1); // 大标题行数
        params.setHeadRows(1); //  列标题行数

        // 2.实现解析
        List list = ExcelImportUtil.importExcel(new File("D:/demo.xlsx"), ContractProductVo.class, params);

        // 3.遍历输出
        for (ContractProductVo contractProductVo : list) {
            System.out.println(contractProductVo);
        }
    }
}

(2)EasyExcel

easyExcel

JAVA解析Excel工具EasyExcel

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便

EasyExcel文档

EasyExcel 入口类,用于构建开始各种操作

 

 

你可能感兴趣的:(JavaEE高级,poi,java,excel,xml)