前段时间,写(shui)了一个上传文件的博客(Java 上传附件后端接口大体流程和逻辑),今天做项目时,刚好又遇到导出excel的功能,前期就一直想做个归纳记录,但一直都是被各种各样的事(tuo)情(yan)耽搁了,今天刚好遇到导出数据到多 sheet的需求,开发完后,顺手来记录一下。
凡事都是由简入繁,我们首先从Java导出excel 单sheet开始,这个场景遇到的比较多,比较普遍。
前期准备:
单 sheet 和 多 sheet,用到的jar都是一样的,无非就是多创建一个 sheet的问题,以下是需要用到的jar包,在对应模块的pom.xml 中引入即可
org.apache.poi
poi
3.9
org.apache.poi
poi-ooxml
3.9
导出开始:
从前到后,首先我们要在controller 层中开放一个GET请求接口,因为当前场景设定为列表导出,会涉及到页面查询条件,所以controller 层代码如下:
/**
* 导出列表
* @return
*/
@RequestMapping(value = "/export", method = RequestMethod.GET)
public String selectExport(@RequestParam(name = "name", required = false) String name,
@RequestParam(value = "ids", required = false) String ids,
HttpServletRequest request, HttpServletResponse response) {
Map dataMap = new HashMap();
dataMap.put("name", name);
dataMap.put("ids", ids);
return getResult(Result.OK,iProService.selectExportList(dataMap, request, response));
}
如上述代码所示, @RequestMapping 中设置当前请求为 GET请求,方便前端调用,方法参数中使用 @RequetParam 设置入参名称,并使用 required=false 设置当前参数非必填。
底部的 getResult(***) 这个为我们当前项目封装的返回值方法,cmsPropertyModelService.selectExportCmsPropertyModelList 为处理当前导出任务的接口。
service层接口:
/**
* 导出列表
* @return
*/
public boolean selectExportList(Map dataMap, HttpServletRequest request, HttpServletResponse response);
讲了那么多,接下来是重点部分,导出接口的实现类,处理我们的业务逻辑和导出逻辑
单 sheet 导出 serviceImpl:
/**
* 导出列表
*
* @param dataMap
* @param request
* @param response
* @return
*/
@Override
public boolean selectExportList(Map dataMap, HttpServletRequest request, HttpServletResponse response) {
String ids = MapUtils.getString(dataMap, "ids");
if (StringUtils.isNotEmpty(ids)) {
dataMap.put("ids", ids.split(","));
}
//查询需要导出的数据
List cmsPropertyModelList = Mapper层数据库查询;
//第一步:创建一个webbook,对应一个Excel文件
HSSFWorkbook wb = new HSSFWorkbook();
//第一步:创建单元格,并设置值表头 设置表头居中
HSSFCellStyle style = wb.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 创建一个居中格式
//表头加粗
HSSFFont titleFont = wb.createFont();
titleFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
style.setFont(titleFont);
//第三步:创建 sheet,并分别设置 标题
HSSFSheet sheet1 = wb.createSheet("父级sheet");
HSSFRow row1 = sheet1.createRow(0);
String[] sheet1Title = {"名称", "备注"};
//创建sheet1标题
for (int i = 0 ; i < sheet1Title.length; i ++) {
Cell cell = row1.createCell(i);
cell.setCellStyle(style); //设置标题样式
cell.setCellValue(sheet1Title[i]);
sheet1.setColumnWidth(i, 10000); //设置列宽
}
//第四步:往sheet中设置值
if (null != cmsPropertyModelList && cmsPropertyModelList.size() > 0) {
for (int i = 0; i < cmsPropertyModelList.size(); i ++) {
CmsPropertyModel cmsPropertyModel = cmsPropertyModelList.get(i);
row1 = sheet1.createRow(sheet1.getLastRowNum() + 1); //获取当前行下一行
row1.createCell(0).setCellValue(cmsPropertyModel.getName());
row1.createCell(1).setCellValue(cmsPropertyModel.getDescription());
}
}
//第五步:导出
try {
//excel文件名
String fileName = "列表_" + DateFormatUtils.format(new Date(), "yyyyMMdd_HHmmss") + ".xls";
//导出设置
fileName = new String(fileName.getBytes(), "ISO8859-1");
response.setContentType("application/octet-stream;charset=ISO8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.addHeader("Pargam", "no-cache");
response.addHeader("Cache-Control", "no-cache");
//开始导出
OutputStream os = response.getOutputStream();
wb.write(os);
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
logger.error(">>> 导出列表发生异常!" + e.getMessage());
}
return true;
}
以上就是单个 sheet 的excel导出,查询出数据后,开始创建 excel 对象等操作,里面首先设置sheet页标题,设置好后,通过循环我们数据库查询到的数据,一条一条一次追加,其中 sheet1.createRow(sheet1.getLastRowNum() + 1); 这句代码中的 sheet1.getLastRowNum() 是获取当前sheet 最新的一行,获取后 + 1 ,表示创建在当前最新一下后面创建一行,一次来保证行连续,且我们可以不用去管具体行号是多少,当然如果具体业务有需要,可以修改这个跟当前循环次数进行关联。
DateFormatUtils.java 是 apache commons-langs 公共包中的工具类,maven导包如下:
org.apache.commons
commons-lang3
3.4
provided
以上就是单 sheet execl 导出过程,接下来是多个的,我这边用两个sheet来演示,其余的以此类推,我们的数据结构是父级中带有子集列表,如下所示:
{
"name":"父级名称",
"description":"描述",
"cmsPropertyList":[ //子集列表
{
"proName":"子集名称",
......
]
}
大家每个人的实际业务需求不同,处理和实现的逻辑也会有不用,这里只是介绍一个我遇到的,应该是比较常见的场景,好了,不废话了,直接上代码。
多 sheet 导出 serviceImpl:
/**
* 导出列表
*
* @param dataMap
* @param request
* @param response
* @return
*/
@Override
public boolean selectExportList(Map dataMap, HttpServletRequest request, HttpServletResponse response) {
String ids = MapUtils.getString(dataMap, "ids");
if (StringUtils.isNotEmpty(ids)) {
dataMap.put("ids", ids.split(","));
}
//查询需要导出的数据
List cmsPropertyModelList = Mapper层数据库查询数据
//第一步:创建一个webbook,对应一个Excel文件
HSSFWorkbook wb = new HSSFWorkbook();
//第一步:创建单元格,并设置值表头 设置表头居中
HSSFCellStyle style = wb.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 创建一个居中格式
//表头加粗
HSSFFont titleFont = wb.createFont();
titleFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
style.setFont(titleFont);
//第三步:分别创建 sheet,并分别设置 标题
//创建sheet1
HSSFSheet sheet1 = wb.createSheet("父级sheet");
HSSFRow row1 = sheet1.createRow(0);
String[] sheet1Title = {"名称", "备注"};
//设置sheet1标题
for (int i = 0 ; i < sheet1Title.length; i ++) {
Cell cell = row1.createCell(i);
cell.setCellStyle(style); //设置标题样式
cell.setCellValue(sheet1Title[i]);
sheet1.setColumnWidth(i, 10000); //设置列宽
}
//创建sheet1
HSSFSheet sheet2 = wb.createSheet("子级sheet");
HSSFRow row2 = sheet2.createRow(0);
String[] sheet2Title = {"父级名称", "子集名称", ……};
//设置sheet2标题
for (int i = 0 ; i < sheet2Title.length; i ++) {
Cell cell = row2.createCell(i);
cell.setCellStyle(style); //设置标题样式
cell.setCellValue(sheet2Title[i]);
sheet2.setColumnWidth(i, 10000); //设置列宽
}
//第四步:往sheet中设置值
if (null != cmsPropertyModelList && cmsPropertyModelList.size() > 0) {
for (int i = 0; i < cmsPropertyModelList.size(); i ++) {
CmsPropertyModel cmsPropertyModel = cmsPropertyModelList.get(i);
//设置 sheet1 中的值
row1 = sheet1.createRow(sheet1.getLastRowNum() + 1); //获取当前行下一行
row1.createCell(0).setCellValue(cmsPropertyModel.getName()); //父级名称
row1.createCell(1).setCellValue(cmsPropertyModel.getDescription()); //父级备注
//设置 sheet2 中的值
List cmsPropertyList = cmsPropertyModel.getCmsPropertyList();
if (null != cmsPropertyList && cmsPropertyList.size() > 0) {
for (int j = 0; j < cmsPropertyList.size(); j ++) {
CmsProperty cmsProperty = cmsPropertyList.get(j);
String proSeq = (null == cmsProperty.getProSeq()) ? "" : String.valueOf(cmsProperty.getProSeq());
row2 = sheet2.createRow(sheet2.getLastRowNum() + 1); //获取sheet2当前行下一行
row2.createCell(0).setCellValue(cmsProperty.getProModelName()); //子集名称
……………… 往 sheet2 当前行的单元格中设值 …………
}
}
}
}
//第五步:导出
try {
//excel文件名
String fileName = "列表_" + DateFormatUtils.format(new Date(), "yyyyMMdd_HHmmss") + ".xls";
//导出设置
fileName = new String(fileName.getBytes(), "ISO8859-1");
response.setContentType("application/octet-stream;charset=ISO8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.addHeader("Pargam", "no-cache");
response.addHeader("Cache-Control", "no-cache");
//开始设置
OutputStream os = response.getOutputStream();
wb.write(os);
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
logger.error(">>> 导出列表发生异常!" + e.getMessage());
}
return true;
}
单个sheet 和多个 sheet 其实差别并不大,无非就是多创建一个 sheet,并进行设值而已。
需要声明的是:这只是简单的excel导出,适用于普通的导出,并没有涉及到过多的excel样式设置,如果项目中对excel样式有要求,可以在前面几步,创建 HSSFWork对象 和 创建单元格对象时进行设置,复杂的我也没有进行深入研究,后续如果有遇到,我们再进行拓展补充。
好了,以上就是本文的全部内容,我有个臭毛病,不是很擅长言语表达,动不动就是直接上代码,直来直去,码文字,权当记录和锻炼,如果觉得还可以,给个回复或给个赞,感谢。