一、引言
1、java实现Excel的导出与读取功能,常用技术如下:
名称简介优势Apache POIApache POI是Java操作Microsoft Office文件的强大API,特别适用于Excel文件的读写。它提供了HSSF和XSSF两种方式来处理Excel文件,分别对应旧版(.xls)和新版(.xlsx)的Excel格式。此外,POI还有一个扩展库SXSSF,用于处理大数据量的Excel导出,能有效避免内存溢出的问题。
功能全面:支持各种Excel操作,包括创建、修改、读取等。性能稳定:处理大型Excel文件时性能良好。社区活跃:有大量的教程和示例代码可供参考。JExcelApiJExcelApi是另一个用于读写Excel文件的Java库。它提供了一个简单的API来创建、写入和读取Excel文件,支持.xls和.xlsx格式。
使用简单:API直观易用,适合初学者。轻量级:不依赖其他库,适合对性能要求较高的场景。EasyExcelEasyExcel是一个基于Apache POI的开源项目,用于简化Excel的读写操作。它采用了流式读写的方式,能够高效地处理大量数据,并且减少了内存占用。
高效性能:采用流式读写,适合处理大数据量的Excel文件。简单易用:提供了简洁的API和丰富的功能。稳定性好:经过大量测试,稳定性较高。HutoolHutool是一个Java工具包,提供了丰富的API,包括Excel的导入导出功能。它使用简单,支持多种Excel格式,并提供了丰富的功能来操作Excel文件。
功能丰富:除了Excel操作外,还提供了其他多种实用工具。使用简单:API设计简洁明了,易于上手。社区支持:有一定的社区支持,可以获取帮助和教程。
2、技术选型
根据你的具体需求,可以选择适合的库来导出Excel文件。Apache POI功能全面且性能稳定,适合复杂的Excel操作;JExcelApi适合简单的Excel读写操作;EasyExcel适合处理大数据量的Excel文件;Hutool则提供了丰富的功能和简单易用的API。
注:新项目可以根据自己情况。老项目,尤其是二次开发的,在不打算升级的情况下,尽量使用项目本身的技术。比如:Apache POI 就很常见。但是如果你想使用新技术,注意依赖冲突问题。(Apache POI与EasyExcel就很容易发生冲突)
因项目需求,我这里使用Apache POI。
3、常用技术:Apache POI 与 EasyExcel的对比
Apache POI和EasyExcel都是用于处理Excel文件的Java库,但它们在设计目标、使用方式和性能上存在一些差异。以下是Apache POI和EasyExcel之间的区别、推荐理由以及性能比较:
区别:
设计目标:
Apache POI:Apache POI是一个全面的Excel处理库,它提供了对Excel文件格式的深入访问,支持从简单的数据操作到复杂的格式化和样式设置。EasyExcel:EasyExcel则专注于简化大数据量的Excel读写操作,通过流式读写和事件驱动的方式,实现高效的内存管理和快速的数据处理。 使用方式:
Apache POI:Apache POI的API相对复杂,需要更多的代码来实现Excel的读写操作,但它提供了更多的灵活性和控制力。EasyExcel:EasyExcel的API设计简洁,通过注解和监听器的方式,可以更方便地实现Excel的读写操作,减少代码量。 内存管理:
Apache POI:在处理大数据量的Excel文件时,Apache POI可能会消耗较多的内存,因为它通常会将整个Excel文件加载到内存中。EasyExcel:EasyExcel采用了流式读写的方式,逐行读取和写入数据,从而大大减少了内存占用,适合处理大数据量的Excel文件。
推荐理由:
Apache POI:如果你需要处理复杂的Excel文件,包括格式设置、样式调整等高级功能,并且数据量适中,那么Apache POI是一个很好的选择。EasyExcel:如果你需要处理大数据量的Excel文件,并且更注重性能和内存管理,那么EasyExcel是一个更好的选择。
性能比较:
内存占用:在处理大数据量的Excel文件时,EasyExcel的内存占用通常低于Apache POI,因为它采用了流式读写的方式,逐行处理数据,而不是将整个文件加载到内存中。处理速度:由于EasyExcel采用了流式读写和事件驱动的方式,它在处理大数据量的Excel文件时通常比Apache POI更快。功能丰富度:Apache POI提供了更丰富的Excel操作功能,包括格式设置、样式调整等,而EasyExcel则更注重于基本的读写操作。
综上所述,Apache POI和EasyExcel各有优势,选择哪个库取决于你的具体需求。如果你需要处理复杂的Excel文件并且数据量适中,Apache POI是一个更好的选择;如果你需要处理大数据量的Excel文件并且更注重性能和内存管理,那么EasyExcel是一个更好的选择。
二、Apache POI实现Excel功能的具体示例
1、maven依赖:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2、创建工具类:ExcelUtil.java
注:该工具类主要包含以下内容
(1)获取工作簿方法:getWorkBook(InputStream in)
(2)判断单元格的类型 并获取单元格的值: getCellValue(Cell cell)
(3)读取excel数据: readDataToMap(Workbook workbook, int titleLength)
(4)生成Excel表格:单个Sheet页形式:createExcel(HttpServletResponse response, String fileName,String sheetName, List
(5)创建表头样式:createHeadCellStyle(XSSFWorkbook wb)
(6)创建内容样式:createContentCellStyle(XSSFWorkbook wb)
(7)导出excel表格,考虑多个Sheet页,性能比较好,适合大数据量:exportDataToExcel(HttpServletResponse response, String fileName, String sheetName, List
package com.lc.ibps.user.util;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.lang.reflect.Field;
/**
* excel工具类
*/
public class ExcelUtil {
private final static Logger logger = LoggerFactory.getLogger(ExcelUtil.class);
/**
* 获取工作簿
* @param in excel文件
* @return
*/
public static Workbook getWorkBook(InputStream in){
// 03/07 通用版本
Workbook workbook = null;
try {
// 创建工作簿
workbook = WorkbookFactory.create(in);
} catch (Exception e) {
logger.error("获取工作簿出现异常:",e);
}
return workbook;
}
/**
* 判断单元格的类型 并获取单元格的值
* @param cell 单元格
* @return
*/
public static Object getCellValue(Cell cell){
// 当单元格为空时 则返回空字符
if(cell == null) return "";
// 单元格的值
Object cellValue = null;
// 获取单元格的类型
switch (cell.getCellTypeEnum()){
// date类型
case NUMERIC:
if(DateUtil.isCellDateFormatted(cell)){
// 获取单元格的值
Date dateCellValue = cell.getDateCellValue();
// 时间格式化
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 将单元格的值格式化
cellValue = sdf.format(dateCellValue);
}else
// 设置单元格为String类型
cell.setCellType(CellType.STRING);
cellValue = cell.toString();
break;
// String类型
case STRING:
cellValue = cell.getStringCellValue();
break;
// 布尔类型
case BOOLEAN:
cellValue = cell.getBooleanCellValue();
break;
// 空值
case BLANK:
cellValue = null;
break;
// 错误类型
case ERROR:
cellValue = Byte.toString(cell.getErrorCellValue());
break;
// 公式类型
case FORMULA:
cellValue = cell.getCellFormula();
break;
// 默认值
default:
cellValue = "";
}
// 将单元格的值返回
return cellValue;
}
/**
* 判断数组是否为空
* @param array 数组
* @return true:为空 false:不为空
*/
public static boolean isArrayEmpty(Object[] array){
// 数组为空则返回true
if(ArrayUtils.isEmpty(array)) return true;
boolean flag = true;
// 遍历数组
for(Object arr : array){
// 数组不为空则返回false
if(StringUtils.isNotEmpty(ObjectUtils.toString(arr))){
flag = false;
break;
}
}
return flag;
}
/**
* 读取excel数据
* @param titleLength 表头长度
* @return 存储数据的map集合
*/
public static Map
// 创建存储list数据的map集合
Map
// 获取工作簿
//Workbook workbook = getWorkBook(in);
// 获取工作表的页数
int sheets = workbook.getNumberOfSheets();
// 创建List集合 一个工作表对应一个list集合 Object数组对应每一行数据
List
// 遍历页数
for(int i=0; i list = new ArrayList<>(); // 获取工作表 Sheet sheet = workbook.getSheetAt(i); // 获取工作表的名字 getSheetName(sheet.getSheetName()); // 获取有内容的第一行 下标0开始 int firstRowNum = sheet.getFirstRowNum(); // 获取有内容的最后一行 下标0开始 int lastRowNum = sheet.getLastRowNum(); // 遍历行 for(int h=firstRowNum; h<=lastRowNum; h++){ // 获取行 Row row = sheet.getRow(h); // 行为空 则开始新的循环 if(row == null) continue; // 获取第一个有内容的单元格 下标0开始 int firstCellNum = row.getFirstCellNum(); // 获取最后一个有内容的单元格 下标1开始 int lastCellNum = row.getLastCellNum(); // 获取单元格的长度 int length = lastCellNum - firstCellNum; // 创建存储excel每行数据的数组 以excel表头的作为长度 Object[] cellArr = new Object[titleLength]; // 遍历单元格 for(int g=0; g < titleLength; g++){ // 获取单元格 Cell cell = row.getCell(g); // 获取单元格的值并存储到数组中 cellArr[g] = getCellValue(cell); } // 当数组不为空 将元素添加到List集合中 if(!isArrayEmpty(cellArr)) list.add(cellArr); } // list集合不为空添加到map中 if(CollectionUtils.isNotEmpty(list)) // 以工作表的名字作为key map.put(sheet.getSheetName().trim(), list); } // 返回存储数据的map集合 return map; } // 获取工作表的名字 public static String getSheetName(String sheetName){ return sheetName; } /** * 生成Excel表格:单个Sheet页 * @param fileName 导出的文件名 * @param sheetName sheet页名称 * @param titleList 表头列表 * @param dataList 数据列表 * @return HSSFWorkbook对象 * */ public static void createExcel(HttpServletResponse response, String fileName, String sheetName, List //创建HSSFWorkbook对象,采用try-with-resources结构中,在try-catch执行完毕后,XSSFWorkbook、OutputStream将会自动关闭 try(XSSFWorkbook wb = new XSSFWorkbook();OutputStream output=response.getOutputStream()){ //创建sheet对象 XSSFSheet sheet = wb.createSheet(sheetName); //设置样式 XSSFCellStyle titleCellStyle = createHeadCellStyle(wb); //在sheet里创建第一行,这里即是表头 XSSFRow rowTitle = sheet.createRow(0); //写入表头的每一个列 for (int i = 0; i < titleList.size(); i++) { //创建单元格 XSSFCell cell = rowTitle.createCell(i); cell.setCellValue(titleList.get(i)); cell.setCellStyle(titleCellStyle); } //写入每一行的记录 for (int i = 0; i < dataList.size(); i++) { //创建新的一行,递增 XSSFRow rowData = sheet.createRow(i + 1); //通过反射,获取POJO对象 Class cl = dataList.get(i).getClass(); //获取类的所有字段 Field[] fields = cl.getDeclaredFields(); for (int j = 0; j < fields.length; j++) { //设置字段可见,否则会报错,禁止访问 fields[j].setAccessible(true); //创建单元格 XSSFCell cell = rowData.createCell(j); cell.setCellValue((String) fields[j].get(dataList.get(i))); } } //输出Excel文件 response.reset(); //中文名称要进行编码处理 response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); response.setContentType("application/x-xls"); wb.write(output); }catch (Exception e){ logger.error("生成Excel表格,出现异常:",e); } } /** * 创建表头样式 * @param wb * @return */ private static XSSFCellStyle createHeadCellStyle(XSSFWorkbook wb) { XSSFCellStyle cellStyle = wb.createCellStyle(); cellStyle.setWrapText(true);// 设置自动换行 cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());//背景颜色 cellStyle.setAlignment(HorizontalAlignment.CENTER); //水平居中 cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); //垂直对齐 cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); cellStyle.setBottomBorderColor(IndexedColors.BLACK.index); cellStyle.setBorderBottom(BorderStyle.THIN); //下边框 cellStyle.setBorderLeft(BorderStyle.THIN); //左边框 cellStyle.setBorderRight(BorderStyle.THIN); //右边框 cellStyle.setBorderTop(BorderStyle.THIN); //上边框 XSSFFont headerFont = (XSSFFont) wb.createFont(); // 创建字体样式 headerFont.setBold(true); //字体加粗 headerFont.setFontName("黑体"); // 设置字体类型 headerFont.setFontHeightInPoints((short) 12); // 设置字体大小 cellStyle.setFont(headerFont); // 为标题样式设置字体样式 return cellStyle; } /** * 创建内容样式 * @param wb * @return */ private static XSSFCellStyle createContentCellStyle(XSSFWorkbook wb) { XSSFCellStyle cellStyle = wb.createCellStyle(); cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直居中 cellStyle.setAlignment(HorizontalAlignment.CENTER);// 水平居中 cellStyle.setWrapText(true);// 设置自动换行 cellStyle.setBorderBottom(BorderStyle.THIN); //下边框 cellStyle.setBorderLeft(BorderStyle.THIN); //左边框 cellStyle.setBorderRight(BorderStyle.THIN); //右边框 cellStyle.setBorderTop(BorderStyle.THIN); //上边框 // 生成12号字体 XSSFFont font = wb.createFont(); font.setColor((short)8); font.setFontHeightInPoints((short) 10); cellStyle.setFont(font); return cellStyle; } /** * 导出excel表格,考虑多个Sheet页 * @param response * @param fileName 文件名 * @param sheetName sheet页名 * @param titleList 表头 * @param dataList 导出的数据 * @throws IOException * @throws IllegalAccessException */ public static void exportDataToExcel(HttpServletResponse response, String fileName, String sheetName, List int sheetSize = 100000; // 每个工作表的最大数据量,大于就分页 int totalSheets = (int) Math.ceil((double) dataList.size() / sheetSize); // 计算总的工作表数量 // 创建新的 Excel 工作簿,采用try-with-resources结构,在try-catch执行完毕后,XSSFWorkbook、OutputStream将会自动关闭 try(XSSFWorkbook workbook = new XSSFWorkbook();OutputStream output=response.getOutputStream()){ for (int i = 0; i < totalSheets; i++) { Sheet sheet = workbook.createSheet(sheetName + (i + 1)); // 创建新的工作表 //设置样式 XSSFCellStyle titleCellStyle = createHeadCellStyle(workbook); // 创建表头 //在sheet里创建第一行,这里即是表头 Row rowTitle = sheet.createRow(0); //写入表头的每一个列 for (int j = 0; j < titleList.size(); j++) { //创建单元格 Cell cell = rowTitle.createCell(j); cell.setCellValue(titleList.get(j)); cell.setCellStyle(titleCellStyle); } int startIndex = i * sheetSize; int endIndex = Math.min(startIndex + sheetSize, dataList.size()); List sheetDataList = dataList.subList(startIndex, endIndex); //写入每一行的记录 for (int j = 0; j < sheetDataList.size(); j++) { //创建新的一行,递增 Row rowData = sheet.createRow(j + 1); //通过反射,获取POJO对象 Class cl = sheetDataList.get(j).getClass(); //获取类的所有字段 Field[] fields = cl.getDeclaredFields(); for (int k = 0; k < fields.length; k++) { //设置字段可见,否则会报错,禁止访问 fields[k].setAccessible(true); //创建单元格 Cell cell = rowData.createCell(k); cell.setCellValue((String) fields[k].get(sheetDataList.get(j))); } } } //输出Excel文件 response.reset(); //中文名称要进行编码处理 response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); response.setContentType("application/x-xls"); workbook.write(output); }catch (Exception e){ logger.error("生成Excel表格,出现异常:",e); } } } 3、导出功能举例: (1)主要功能如下: 导出全部数据 导出选中数据 根据搜索条件导出数据 (2)Controller层 @Validated @RequestMapping(value = "/yysq") @RestController @Api(tags = "用印申请、合同签署台账接口") public interface IYysqService { /** * 导出全部数据 * @param response */ @ApiOperation("导出全部数据") @RequestMapping(value = "/exportAllData", method = RequestMethod.GET, produces = "application/json;charset=UTF-8") void exportAllData(HttpServletResponse response) throws Exception; /** * 导出选中数据 * @param response */ @ApiOperation("导出选中数据") @RequestMapping(value = "/exportChooseData", method = RequestMethod.GET, produces = "application/json;charset=UTF-8") void exportChooseData(@RequestParam("ids") String[] ids ,HttpServletResponse response) throws Exception; /** * 根据搜索条件导出数据 * @param response */ @ApiOperation("根据搜索条件导出数据") @RequestMapping(value = "/exportSearchData", method = RequestMethod.GET, produces = "application/json;charset=UTF-8") void exportSearchData(@RequestParam("htmc") String htmc , @RequestParam("heTongBianMa") String heTongBianMa , @RequestParam("heTongJiaFang") String heTongJiaFang , @RequestParam("heTongYiFang") String heTongYiFang , @RequestParam("shenQingBuMen") String shenQingBuMen , @RequestParam("yongYinLeiBie") String yongYinLeiBie , @RequestParam("shenQingRen") String shenQingRen , HttpServletResponse response) throws Exception; } (3)Service业务层 @Api(tags = "用印申请表管理", value = "用印申请表数据") @Service public class YysqProvider extends GenericProvider implements IYysqService{ @Resource private YysqRepository yysqRepository; @Resource private YysqQueryDao yysqQueryDao; @Resource protected CurrentContext currentContext; /** * jdbcTemplateProvider:请不要与mybatis事务混用,它们是无法保持数据一致性的,不同事务中执行的数据! */ @Autowired protected JdbcTemplateProvider jdbcTemplateProvider; //导出全部 @Override public void exportAllData(HttpServletResponse response) throws Exception { List commonExport(yysqPos,response); } //导出选中数据 @Override public void exportChooseData(String[] ids,HttpServletResponse response) throws Exception { List if(ids != null){ list = Arrays.asList(ids); } List commonExport(yysqPos,response); } //根据搜索条件导出数据 @Override public void exportSearchData(String htmc, String heTongBianMa, String heTongJiaFang, String heTongYiFang, String shenQingBuMen, String yongYinLeiBie, String shenQingRen, HttpServletResponse response) throws Exception { String newYongYinLeiBie="%"+yongYinLeiBie+"%"; YysqRcVo yysqRcVo = new YysqRcVo(htmc,heTongBianMa,heTongJiaFang,heTongYiFang,shenQingBuMen,newYongYinLeiBie,shenQingRen); List commonExport(yysqPos,response); } /** * 导出的数据,通用数据处理 * @param yysqPos * @param response * @throws Exception */ private void commonExport(List List yysqPos.stream().forEach(yysqPo -> { YysqVo yysqVo = ObjectConvertUtil.objectConvert(yysqPo, YysqVo.class); Date qianDingTime = yysqPo.getQianDingTime(); Optional if(optionalDate.isPresent()){ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); yysqVo.setQianDingTime(simpleDateFormat.format(qianDingTime)); } String sfnbht = "T".equals(yysqVo.getSfnbht())?"是":"否"; yysqVo.setSfnbht(sfnbht); yysqVos.add(yysqVo); }); exportExcelCommon(response,yysqVos); } /** * 合同签署台账,导出excel,通用方法 * @param response * @param yysqVos 导出的数据 * @throws Exception */ private void exportExcelCommon(HttpServletResponse response,List String filename = "合同签署台账-"+System.currentTimeMillis(); //文件名 String fileName = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20"); //sheet名 String sheetName = "合同签署台账"; //表头集合,作为表头参数 List titleList.add("申请人"); titleList.add("申请部门"); titleList.add("单据编号"); titleList.add("所属企业"); titleList.add("所属部门"); titleList.add("合同名称"); titleList.add("合同编码"); titleList.add("是否内部合同"); titleList.add("合同金额"); titleList.add("用印类别"); titleList.add("签订时间"); titleList.add("用印说明"); titleList.add("合同甲方"); titleList.add("合同乙方"); titleList.add("合同签署方式"); titleList.add("流程状态"); //调取封装的方法,传入相应的参数 ExcelUtil.exportDataToExcel(response,fileName,sheetName,titleList,yysqVos); } /** * 合同签署台账/用印申请的过滤条件 */ private List //申请人所属企业id String orgId = queryDepartmentIdByOrgId(); //层级码 String wbs = queryXmWbsSj(); //合同乙方id String yfId = orgId; //合同乙方 String yf = queryDepartmentNameByOrgId(); Map map.put("orgId",orgId); if(StringUtil.isEmpty(wbs)){ map.put("wbs",wbs); }else { map.put("wbs",wbs+"%"); } map.put("yfId",yfId); map.put("yf",yf); if(CollectionUtils.isNotEmpty(ids)){ map.put("ids",ids); }else { map.put("ids",null); } if(ObjectUtil.isNotEmpty(yysqRcVo)){ map.put("htmc",yysqRcVo.getHtmc()); map.put("heTongBianMa",yysqRcVo.getHeTongBianMa()); map.put("heTongJiaFang",yysqRcVo.getHeTongJiaFang()); map.put("heTongYiFang",yysqRcVo.getHeTongYiFang()); map.put("shenQingBuMen",yysqRcVo.getShenQingBuMen()); map.put("yongYinLeiBie",yysqRcVo.getYongYinLeiBie()); map.put("shenQingRen",yysqRcVo.getShenQingRen()); }else { map.put("htmc",null); map.put("heTongBianMa",null); map.put("heTongJiaFang",null); map.put("heTongYiFang",null); map.put("shenQingBuMen",null); map.put("yongYinLeiBie",null); map.put("shenQingRen",null); } List return yysqPos; } /** * 根据当前用户所在部门查询其所在公司ID */ public String queryDepartmentIdByOrgId() { String userId=currentContext.getCurrentUserId(); String sql="select a.COMPANYID_ from ibps_party_employee a where a.ID_='"+userId+"'"; List String departmentId=""; if(listData.size()>0){ departmentId=listData.get(0).get("COMPANYID_")==null?"":listData.get(0).get("COMPANYID_").toString(); return departmentId; }else{ return departmentId; } } public String queryXmWbsSj() { String userId=currentContext.getCurrentUserId(); String sql="select PARTY_ALIAS_ from ( " + " select PARTY_ALIAS_ from ibps_party_entity WHERE ID_ in (select parent_id_ from ibps_party_entity where ID_ in (select pk_dept from t_parttime_job where pk_psndoc='"+userId+"')) " + " union all " + " select PARTY_ALIAS_ from ibps_party_entity WHERE ID_ = (select a.COMPANYID_ from ibps_party_employee a where a.ID_='"+userId+"')) aa ORDER BY length(aa.PARTY_ALIAS_) " + " "; List String departmentId=""; if(listData.size()>0){ departmentId=listData.get(0).get("PARTY_ALIAS_").toString(); return departmentId; }else{ return departmentId; } } /** * 根据当前用户所在部门查询其所在公司名称 */ public String queryDepartmentNameByOrgId() { String userId=currentContext.getCurrentUserId(); String sql="select b.NAME_ from ibps_party_employee a LEFT JOIN ibps_party_org b on a.COMPANYID_=b.ID_ where a.ID_='"+userId+"'"; List String departmentName=""; if(listData.size()>0){ departmentName=listData.get(0).get("NAME_").toString(); return departmentName; }else{ return departmentName; } } } 4、导出效果: 相关阅读
发表评论