在某些框架中,导出功能会根据内置的POI依赖编写自己的Excel工具类,能满足基本的数据导出,一般只是局限于正常表单的、固定表头的导出形式,不满足于灵活展示的数据表导出,所以还是需要使用原始的手写workbook+字符流的方式导出特殊的数据表。
本次举例横表转纵表展示的表单进行数据导出Excel。
实现思路:
- 先查询数据,确定数据展示形式;
- 规范导出Excel之后的样式,以便设置style;
- 根据数据,循环将数据写入到cell中;
- 将workbook写入字符流中,再将字符流写入到响应中;
具体代码如下:
第一部分:
// 先查询产线名称 作为页面展示的横表字段
List<TimsA021Vo> mtrlList = this.timsA021Service.queryIronBlastStatisticalMtrlList(timsA021QueryVo);
List<Map<String, Object>> timsA021VoMapList = this.timsA021Service.queryIronBlastStatisticalMtrlDataList(timsA021QueryVo, mtrlList);
List<String> headers = new ArrayList<>();
headers.add("账务日期");
headers.add("产线");
headers.add("班别");
if (ListUtils.isNotEmpty(mtrlList)) {
for (TimsA021Vo a021Vo : mtrlList) {
headers.add(a021Vo.getMtrlname());
}
} else {
return;
}
第二部分:
Workbook workbook = new HSSFWorkbook();
// 生成一个表格
Sheet sheet = workbook.createSheet();
workbook.setSheetName(0, "");
// 设置表格默认列宽度为20个字节
sheet.setDefaultColumnWidth((short) 20);
// 生成一个样式
CellStyle cellStyle = workbook.createCellStyle();
// 设置这些样式
cellStyle.setFillForegroundColor(HSSFColor.PALE_BLUE.index);
cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
// 生成一个字体
Font font = workbook.createFont();
font.setColor(HSSFColor.BLACK.index);
font.setFontHeightInPoints((short) 12);
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
// 把字体应用到当前的样式
cellStyle.setFont(font);
// 指定当单元格内容显示不下时自动换行
cellStyle.setWrapText(true);
// 产生表格标题行
Row headerRow = sheet.createRow(0);
int rowNum = 0;
for (String header : headers) {
Cell cell = headerRow.createCell(rowNum++);
cell.setCellValue(header);
cell.setCellStyle(cellStyle);
}
第三部分:
// 遍历集合数据,产生数据行
if (ListUtils.isNotEmpty(timsA021VoMapList)) {
for (int i = 0; i < timsA021VoMapList.size(); i++) {
// 一共多少行数据
Row row = sheet.createRow(i + 1);
// 第一列
Cell cell1 = row.createCell(0);
if (Objects.nonNull(timsA021VoMapList.get(i).get("ACCOUNTDATE"))) {
cell1.setCellValue(timsA021VoMapList.get(i).get("ACCOUNTDATE").toString());
} else {
cell1.setCellValue("");
}
// 第二列
Cell cell2 = row.createCell(1);
if (Objects.nonNull(timsA021VoMapList.get(i).get("EQUIPNO"))) {
String label = DictUtils.getDictLabel("IRON_PLAN_LINE_NO", timsA021VoMapList.get(i).get("EQUIPNO").toString(), "");
cell2.setCellValue(label);
} else {
cell2.setCellValue("");
}
// 第三列
Cell cell3 = row.createCell(2);
if (Objects.nonNull(timsA021VoMapList.get(i).get("PRODUCTSHIFT"))) {
String label = DictUtils.getDictLabel("product_shift_1500", timsA021VoMapList.get(i).get("PRODUCTSHIFT").toString(), "");
cell3.setCellValue(label);
} else {
cell3.setCellValue("");
}
// 一共多少列表头
// 第三至第N列
for (int k = 0; k < mtrlList.size(); k++) {
Cell cell4 = row.createCell(k + 3);
// 根据产线的数量 获取对应数据
if (Objects.isNull(timsA021VoMapList.get(i).get("E"+mtrlList.get(k).getMtrlno()))) {
cell4.setCellValue("0.00");
} else {
cell4.setCellValue(timsA021VoMapList.get(i).get("E"+mtrlList.get(k).getMtrlno()).toString());
}
}
}
}
// 增加合计行
int createRow = sheet.getLastRowNum() + 1;
Row row = sheet.createRow(createRow);
Cell cell1 = row.createCell(0);
cell1.setCellValue("合计");
cell1.setCellStyle(cellStyle);
CellRangeAddress cellRangeAddress = new CellRangeAddress(createRow, createRow, 0, 2);
sheet.addMergedRegion(cellRangeAddress);
int flag = 3;
for (TimsA021Vo cell : mtrlList) {
row.createCell(flag).setCellValue(cell.getNetwgt().toString());
flag++;
}
第四部分:
String fileName = "-";
fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
// 将工作簿写入输出流中
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
workbook.write(byteArrayOutputStream);
byte[] bytes = byteArrayOutputStream.toByteArray();
// 设置响应头,指定文件名和文件类型
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename="+fileName+"-"+ DateUtils.getDate("yyyyMMddHHmmss")+".xls");
response.setContentLength(bytes.length);
// 关闭输出流和工作簿对象
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
总结:该问题主要难度在于写清活动列将要展示在Excel的位置,循环时要注意,有空值的时候要做好判断。