一、普通类型的excel导出
1、导入相关依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.17</version>
</dependency>
2、创建一个excel工作簿
//声明一个工作簿
HSSFWorkbook workbook = new HSSFWorkbook();
3、生成一个sheet页
//生成一个表格
HSSFSheet sheet = workbook.createSheet(title);
4、如果需要生成表格的标题行可采取如下操作
//产生表格标题行
HSSFRow row = sheet.createRow(0);
for (short i=0;i<headers.length;i++){
HSSFCell cell = row.createCell(i);
cell.setCellStyle(headerStyle);
HSSFRichTextString text = new HSSFRichTextString(headers[i]);
cell.setCellValue(text);
}
5、遍历数据集合,利用反射机制对单元格进行赋值操作
//遍历集合数据,产生数据行
Iterator<T> iterator = dataSet.iterator();
int index = 0;
while (iterator.hasNext()){
index++;
row = sheet.createRow(index);
T t = (T)iterator.next();
//利用反射,根据javabean属性的先后顺序,动态调用get方法得到属性值
Field[] fields = t.getClass().getDeclaredFields();
for (short i=0;i<fields.length;i++){
HSSFCell cell = row.createCell(i);
Field field = fields[i];
String fieldName = field.getName();
//get方法名
String getMethodName = "get"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
try{
Class cls = t.getClass();
Method getMethod = cls.getMethod(getMethodName,new Class[]{});
Object value = getMethod.invoke(t, new Object[]{});
//在赋值之前可以对value进行类型的判断处理
cell.setCellValue(value);
}catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e){
e.printStackTrace();
}
}
6、将excel数据输出
try {
response.setHeader("Content-Disposition",
"attachment;filename=\"" + new String((fileName+".xls").getBytes(),"iso-8859-1"));
// 执行公式(如果excel有内置公式的前提需要设置此属性)
workbook.setForceFormulaRecalculation(true);
workbook.write(response.getOutputStream());
}catch (IOException e){
e.printStackTrace();
}
二、在一般的实际项目里面,可能我们每次导出的数据都是不一样的,列数也是动态变化,此时我们就无法使用上述Java反射的方法来处理数据。这时我们就需要每次导出时进行动态生成列以及对应的赋值操作,以下是举例说明:
1、生成workbook对象和sheet对象与第一种情况一致;
//声明一个工作簿
HSSFWorkbook workbook = new HSSFWorkbook();
//生成一个表格
HSSFSheet sheet = workbook.createSheet(title);
2、根据特殊的条件动态生成表头数据后生成表头
//此处为个人的处理表头方法,可替换
String[] headers = dealProductHeaders(productList,1);
//样式
HSSFCellStyle commonStyle = commonStyle(workbook);
headerRow.setHeightInPoints(20);
for (short i=0;i<headers.length;i++){
HSSFCell headerCell = headerRow.createCell(i);
HSSFRichTextString text = new HSSFRichTextString(headers[i]);
headerCell.setCellValue(text);
headerCell.setCellStyle(commonStyle);
}
3、对需要导出的数据进行遍历,在遍历的时候循环生成行hssfRow,并对每行的每列hssfCell进行赋值以及样式的处理;
//此处可自行发挥
4、在处理数据的时候,或许我们会遇到某列设置下拉框的情况,这种情况我们有两种方式可以采取
第一种:在对应的列直接赋值,生成下拉框,但是此种方法下拉框值的大小有限制;
/**
* excel导出,有码值的数据使用下拉框展示。
* @param str 数据
* @param firstRow 插入下拉框开始行号
* @param lastRow 插入下拉框结束行号
* @param firstCol 插入下拉框开始列号
* @param lastCol 插入下拉框结束行号
* @return
*/
private static HSSFDataValidation createBox(String[] str, int firstRow, int lastRow, int firstCol, int lastCol) {
HSSFDataValidation dataValidation = null;
//设置下拉框
if(str.length > 0 && null != str) {
//指定0-9行,0-0列为下拉框
CellRangeAddressList cas = new CellRangeAddressList(firstRow , lastRow , firstCol , lastCol);
//创建下拉数据列
DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(str);
//将下拉数据放入下拉框
dataValidation = new HSSFDataValidation(cas, dvConstraint);
}
return dataValidation;
}
第二种:可以采取生成隐藏sheet数据,然后在需要生成下拉框的sheet页里面进行引用(无限制,推荐使用);
private static void createBox(HSSFWorkbook workbook,String sheetName,int sheetNameIndex,String[] sheetData,int firstRow,int lastRow,int firstCol,int lastCol){
//将下拉框数据放到新的sheet里,然后excel通过新的sheet数据加载下拉框数据
Sheet hidden = workbook.createSheet(sheetName);
CellStyle cellstyle = workbook.createCellStyle();
//创建单元格对象
Cell cell =null;
//遍历我们上面的数组,将数据取出来放到新sheet的单元格中
for (int i = 0, length = sheetData.length; i < length; i++){
//取出数组中的每个元素
String name = sheetData[i];
//根据i创建相应的行对象(说明我们将会把每个元素单独放一行)
Row row = hidden.createRow(i);
//创建每一行中的第一个单元格
cell = row.createCell(0);
//然后将数组中的元素赋值给这个单元格
cell.setCellValue(name);
cell.setCellStyle(cellstyle);
}
if (sheetData.length==0){
//没有下拉数据
workbook.setSheetHidden(sheetNameIndex, true);
}else {
//被引用
// 创建名称,可被其他单元格引用
Name namedCell = workbook.createName();
namedCell.setNameName(sheetName);
// 设置名称引用的公式
namedCell.setRefersToFormula(sheetName+"!$A$1:$A$" + sheetData.length);
//加载数据,将名称为hidden的sheet中的数据转换为List形式
DVConstraint constraint = DVConstraint.createFormulaListConstraint(sheetName);
// (3, 65534, 2, 2) ====> (起始行,结束行,起始列,结束列)
CellRangeAddressList regions = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
// 将设置下拉选的位置和数据的对应关系 绑定到一起
DataValidation dataValidation = new HSSFDataValidation(regions, constraint);
//将第二个sheet设置为隐藏
workbook.setSheetHidden(sheetNameIndex, true);
//将数据赋给下拉列表
workbook.getSheetAt(0).addValidationData(dataValidation);
}
}
5、另外,在导出excel数据时难免遇到一些特殊情况,需要实现数据分组(即父子关系)、折叠收缩的效果,此时我们需要使用到 poi的sheet.groupRow(param1,param2)方法:
//param1为分组的起始索引,param2为终止索引
sheet.groupRow(param1,param2);
//设置折叠符号在上方
sheet.setRowSumsBelow(false);
sheet.setRowSumsRight(false);
建议:在循环数据进行单元格赋值的同时生成分组信息Map<Integer, List<List>> map,后续只需遍历map即可实现excel分组效果,另外像组内汇总,嵌套公式都是很方便的。
map的key为层级,value为此层级所有的分组信息
警告:设置折叠符在上方发现poi自带的获取层级方法失效,因此上述建议可采纳。
6、附上部分常用方法
- 合并单元格
private static void commonMergedRegion(HSSFSheet sheet,int firstRow,int lastRow,int firstCol,int lastCol){
CellRangeAddress region=new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
sheet.addMergedRegion(region);
}
- 合并单元格之后边框样式处理
/**
* 合并单元格后添加边框
*/
private static void setBorderStyle(CellRangeAddress region, HSSFSheet sheet) {
RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
}
- 设置样式
public static HSSFCellStyle bodyStyle(HSSFWorkbook workbook){
HSSFCellStyle style = workbook.createCellStyle();
//设置样式
//边框
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
//居中
style.setAlignment(HorizontalAlignment.CENTER);
//字体
HSSFFont font = workbook.createFont();
font.setFontName("仿宋_GB2312");
//字号
font.setFontHeightInPoints((short)12);
//是否加粗
font.setBold(false);
style.setFont(font);
return style;
}