Java+POI+模板”一:打造复杂Excel 报表
1 设计思路
Java 对于Excel 的操作一般借助于POI 类库,由于有些报表的表头比较复杂,直接用POI 控制报表的生成比较困难,这时可以先制作Excel 报表模板,而后再通过Java 调用POI 函数将用户数据写入到Excel 报表模板,最后导出到新的目标文件即可。
2 设计步骤
2.1 初始步骤
2.1.1创建Excel 报表模板
根据需要设计出Excel 报表,并保存为default.xls。如下图所示。
2.1.2创建ExcelTemplate类
- /**
- * 该类实现了基于模板的导出
- * 如果要导出序号,需要在excel中定义一个标识为sernums
- * 如果要替换信息,需要传入一个Map,这个map中存储着要替换信息的值,在excel中通过#来开头
- * 要从哪一行那一列开始替换需要定义一个标识为datas
- * 如果要设定相应的样式,可以在该行使用styles完成设定,此时所有此行都使用该样式
- * 如果使用defaultStyls作为表示,表示默认样式,如果没有defaultStyles使用datas行作为默认样式
- */
- public class ExcelTemplate {
- private ExcelTemplate() {
- }
- public static ExcelTemplate getInstance() {
- return et;
- }
- }
下面是以后要用到的一些变量和常量
- /**
- * 数据行标识
- */
- public final static String DATA_LINE = "datas";
- /**
- * 默认样式标识
- */
- public final static String DEFAULT_STYLE = "defaultStyles";
- /**
- * 行样式标识
- */
- public final static String STYLE = "styles";
- /**
- * 插入序号样式标识
- */
- public final static String SER_NUM = "sernums";
- private static ExcelTemplate et = new ExcelTemplate();
- private Workbook wb;
- private Sheet sheet;
- /**
- * 数据的初始化列数
- */
- private int initColIndex;
- /**
- * 数据的初始化行数
- */
- private int initRowIndex;
- /**
- * 当前列数
- */
- private int curColIndex;
- /**
- * 当前行数
- */
- private int curRowIndex;
- /**
- * 当前行对象
- */
- private Row curRow;
- /**
- * 最后一行的数据
- */
- private int lastRowIndex;
- /**
- * 默认样式
- */
- private CellStyle defaultStyle;
- /**
- * 默认行高
- */
- private float rowHeight;
- /**
- * 存储某一方所对于的样式
- */
- private Map<Integer,CellStyle> styles;
- /**
- * 序号的列
- */
- private int serColIndex;
2.2 读取excel报表模板的数据
- /**
- * 1、读取相应的模板文档
- */
- public ExcelTemplate readTemplateByClasspath(String path){
- try {
- wb=WorkbookFactory.create(ExcelTemplate.class.getResourceAsStream(path));
- initTemplate();
- } catch (InvalidFormatException e) {
- e.printStackTrace();
- throw new RuntimeException("InvalidFormatException, please check.");
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("The template is not exist, please check.");
- }
- return this;
- }
- public ExcelTemplate readTemplateByPath(String path){
- try {
- wb=WorkbookFactory.create(new File(path));
- } catch (InvalidFormatException e) {
- e.printStackTrace();
- throw new RuntimeException("InvalidFormatException, please check.");
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("The template is not exist, please check.");
- }
- return this;
- }
在读取报表模板的时候会初始化模板,记录模板的样式,插入数据的位置
- private void initTemplate(){
- sheet=wb.getSheetAt(0);
- initConfigData();
- lastRowIndex = sheet.getLastRowNum();
- curRow=sheet.getRow(curRowIndex);
- }
- /**
- * 循环遍历,找到有datas字符的那个单元,记录initColIndex,initRowIndex,curColIndex,curRowIndex
- * 调用initStyles()方法
- * 在寻找datas字符的时候会顺便找一下sernums,如果有则记录其列号serColIndex;如果没有则调用initSer()方法,重新循环查找
- */
- private void initConfigData() {
- boolean findData=false;
- boolean findSer = false;
- for(Row row : sheet){
- if(findData) break;
- for(Cell c: row){
- if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;
- String str=c.getStringCellValue().trim();
- if(str.equals(SER_NUM)){
- serColIndex=c.getColumnIndex();
- findSer=true;
- }
- if(str.equals(DATA_LINE)){
- initColIndex=c.getColumnIndex();
- initRowIndex=row.getRowNum();
- curColIndex=initColIndex;
- curRowIndex=initRowIndex;
- findData=true;
- break;
- }
- }
- }
- if(!findSer){
- initSer();
- }
- initStyles();
- }
- /**
- * 初始化序号位置
- */
- private void initSer() {
- for(Row row:sheet) {
- for(Cell c:row) {
- if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;
- String str = c.getStringCellValue().trim();
- if(str.equals(SER_NUM)) {
- serColIndex = c.getColumnIndex();
- }
- }
- }
- }
- /**
- * 初始化样式信息
- */
- private void initStyles(){
- styles = new HashMap<Integer, CellStyle>();
- for(Row row:sheet) {
- for(Cell c:row) {
- if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;
- String str = c.getStringCellValue().trim();
- if(str.equals(DEFAULT_STYLE)) {
- defaultStyle = c.getCellStyle();
- rowHeight=row.getHeightInPoints();
- }
- if(str.equals(STYLE)||str.equals(SER_NUM)) {
- styles.put(c.getColumnIndex(), c.getCellStyle());
- }
- }
- }
- }
2.3 新建excel并向其写数据
- public void writeToFile(String filepath){
- FileOutputStream fos=null;
- try {
- fos=new FileOutputStream(filepath);
- wb.write(fos);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- throw new RuntimeException("写入的文件不存在"+e.getMessage());
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("写入数据失败"+e.getMessage());
- } finally{
- if(fos!=null)
- try {
- fos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
2.4 实现Excel公共模板的第一步(实现了数据插入)
下面是创建单元格的方法,以及重载方法
- public void createCell(String value){
- Cell c =curRow.createCell(curColIndex);
- setCellStyle(c);
- c.setCellValue(value);
- curColIndex++;
- }
- public void createCell(int value) {
- Cell c = curRow.createCell(curColIndex);
- setCellStyle(c);
- c.setCellValue((int)value);
- curColIndex++;
- }
- public void createCell(Date value) {
- Cell c = curRow.createCell(curColIndex);
- setCellStyle(c);
- c.setCellValue(value);
- curColIndex++;
- }
- public void createCell(double value) {
- Cell c = curRow.createCell(curColIndex);
- setCellStyle(c);
- c.setCellValue(value);
- curColIndex++;
- }
- public void createCell(boolean value) {
- Cell c = curRow.createCell(curColIndex);
- setCellStyle(c);
- c.setCellValue(value);
- curColIndex++;
- }
- public void createCell(Calendar value) {
- Cell c = curRow.createCell(curColIndex);
- setCellStyle(c);
- c.setCellValue(value);
- curColIndex++;
- }
上面的方法会调用setCellStyle方法来设置单元格样式
- /**
- * 设置某个单元格的样式
- * @param c
- */
- private void setCellStyle(Cell c) {
- if(styles.containsKey(c.getColumnIndex())) {
- c.setCellStyle(styles.get(c.getColumnIndex()));
- } else {
- c.setCellStyle(defaultStyle);
- }
- }
createNewRow方法用于创建新的行,新的行的位置位于curRowIndex,从curRowIndex到lastRowIndex的所有行会自动向下移动一行。
- public void createNewRow(){
- if(lastRowIndex>curRowIndex&&curRowIndex!=initRowIndex) {
- sheet.shiftRows(curRowIndex, lastRowIndex, 1,true,true);
- lastRowIndex++;
- }
- curRow = sheet.createRow(curRowIndex);
- curRow.setHeightInPoints(rowHeight);
- curRowIndex++;
- curColIndex=initColIndex;
- }
2.5 实现增加序号
- /**
- * 插入序号,会自动找相应的序号标示的位置完成插入
- */
- public void insertSer() {
- int index = 1;
- Row row = null;
- Cell c = null;
- for(int i=initRowIndex;i<curRowIndex;i++) {
- row = sheet.getRow(i);
- c = row.createCell(serColIndex);
- setCellStyle(c);
- c.setCellValue(index++);
- }
- }
2.6 替换模板中#开头的值
- /**
- * 根据map替换相应的常量,通过Map中的值来替换#开头的值
- * @param datas
- */
- public void replaceFinalData(Map<String,String> datas) {
- if(datas==null) return;
- for(Row row:sheet) {
- for(Cell c:row) {
- if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;
- String str = c.getStringCellValue().trim();
- if(str.startsWith("#")) {
- if(datas.containsKey(str.substring(1))) {
- c.setCellValue(datas.get(str.substring(1)));
- }
- }
- }
- }
- }
3实现步骤
- @Test
- public void test01() {
- ExcelTemplate et = ExcelTemplate.getInstance()
- .readTemplateByClasspath("/excel/default.xls");
- et.createNewRow();
- et.createCell("1111111");
- et.createCell("aaaaaaaaaaaa");
- et.createCell("a1");
- et.createCell("a2a2");
- et.createNewRow();
- et.createCell("222222");
- et.createCell("bbbbb");
- et.createCell("b");
- et.createCell("dbbb");
- et.createNewRow();
- et.createCell("3333333");
- et.createCell("cccccc");
- et.createCell("a1");
- et.createCell(12333);
- et.createNewRow();
- et.createCell("4444444");
- et.createCell("ddddd");
- et.createCell("a1");
- et.createCell("a2a2");
- et.createNewRow();
- et.createCell("555555");
- et.createCell("eeeeee");
- et.createCell("a1");
- et.createCell(112);
- et.createNewRow();
- et.createCell("555555");
- et.createCell("eeeeee");
- et.createCell("a1");
- et.createCell("a2a2");
- et.createNewRow();
- et.createCell("555555");
- et.createCell("eeeeee");
- et.createCell("a1");
- et.createCell("a2a2");
- et.createNewRow();
- et.createCell("555555");
- et.createCell("eeeeee");
- et.createCell("a1");
- et.createCell("a2a2");
- et.createNewRow();
- et.createCell("555555");
- et.createCell("eeeeee");
- et.createCell("a1");
- et.createCell("a2a2");
- et.createNewRow();
- et.createCell("555555");
- et.createCell("eeeeee");
- et.createCell("a1");
- et.createCell("a2a2");
- et.createNewRow();
- et.createCell("555555");
- et.createCell("eeeeee");
- et.createCell("a1");
- et.createCell("a2a2");
- Map<String,String> datas = new HashMap<String,String>();
- datas.put("title","测试用户信息");
- datas.put("date","2012-06-02 12:33");
- datas.put("dep","昭通师专财务处");
- et.replaceFinalData(datas);
- et.insertSer();
- et.writeToFile("d:/test/poi/test01.xls");
- }
4 最后结果
java 导出自定义样式excel
原创诸葛_小明 最后发布于2018-11-01 14:56:59 阅读数 2294 收藏
展开
由于项目需要 要求导出一个这样的表格
然而 正常导出的表格都是这样婶儿地
这种格式网上demo有很多就不详细说了 ,主要说说上面三行是怎么画的。
第一行大标题,是9行合并成的一行,而且字体大小需要单独设置
HSSFSheet sheet;
HSSFCell cell;
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename="+filename+".xls");
sheet = workbook.createSheet("物料调拨单");
//第一行大标题
HSSFCellStyle tStyle = workbook.createCellStyle();
tStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
tStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
HSSFFont tFont = workbook.createFont(); //标题字体
tFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
tFont.setFontHeightInPoints((short)15);
tStyle.setFont(tFont);
short twidth = 15,theight=25*20;
sheet.setDefaultColumnWidth(twidth);
cell = getCell(sheet,0,0);
cell.setCellStyle(tStyle);
setText(cell,"物料调拨单");
sheet.getRow(0).setHeight(theight);
sheet.addMergedRegion(new CellRangeAddress(0,0,0,8));
sheet.addMergedRegion(new CellRangeAddress(0,0,0,8)); 这个就是合并单元格方法,需要传入4个int行的参数,
分别是 起始行,结束行,起始列,结束列。因为我们需要在第一行显示1个9个格的标题所以行就是从0到0,列是从0到8
同理: 下面2行可以这么设置合并
sheet.addMergedRegion(new CellRangeAddress(1, 1, 1, 2));
sheet.addMergedRegion(new CellRangeAddress(2, 2, 1, 2));
sheet.addMergedRegion(new CellRangeAddress(1, 1, 4, 5));
sheet.addMergedRegion(new CellRangeAddress(2, 2, 4, 5));
sheet.addMergedRegion(new CellRangeAddress(1, 1, 7, 8));
sheet.addMergedRegion(new CellRangeAddress(2, 2, 7, 8));
由于合并单元格,值取的是第一个单元格的值,所以后面的单元格可以直接赋值为空
————————————————