直接上代码,注意使用 SXSSFWorkbook 此类在构造表格和处理行高的时候效率极高,刚开始时我使用的 XSSFWorkbook 就出现构造表格效率极低,一万行基本需要3秒左右,那当导出百万级数据就慢的要死啦,而且他会让内存溢出
1. CommentController
/** * excel导出功能 * @param commentSearch * @param response * @param request * @return * @throws Exception */ @RequestMapping("/exportCommentInfo") @ResponseBody @NoRepeatRequest public BaseDTO exportCommentInfo(CommentSearch commentSearch, HttpServletResponse response, HttpServletRequest request) throws Exception{ LOGGER.info("CommentController.exportCommentInfo start"); long startTime = System.currentTimeMillis(); LOGGER.info("开始下载........................................."); List<ErrorInfo> errors = null; int result = 0; String fileName = FileNameUtils.getExportCommontExcelFileName(); OutputStream fileOut = null; SXSSFWorkbook workbook = null;try { LOGGER.debug("classpath: " + fileName); workbook = new SXSSFWorkbook(10000);//内存中实时存在10000个对象,超过的实时写入磁盘,保证内存消耗不会过大 commentService.exportCommentInfo(request,workbook, commentSearch); // 定义excel文件名 response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\""); // 定义输出流 fileOut = response.getOutputStream(); // 调用导出方法 workbook.write(fileOut); } catch (Exception e) { LOGGER.error("InterfaceInfoController.exportInterfaceInfo Exception: ", e); ErrorInfo errorInfo = new ErrorInfo("system.error", "系统异常!"); errors = Arrays.asList(errorInfo); }finally { workbook.close(); } LOGGER.info("下载完成....|||||.......用时:" + (System.currentTimeMillis() - startTime)); return tranferBaseDTO(errors, result); }
2.导出实现类 exportCommentInfo
/** * excel 导出 * @param: [request, workbook, commentSearch] * @return: void * @auther: 011336 * @date: 2018/12/7 15:03 */ @Override public void exportCommentInfo(HttpServletRequest request, SXSSFWorkbook workbook, CommentSearch commentSearch) { //excel样式 CellStyle centerStyle = workbook.createCellStyle(); CellStyle cellStyleCenter = workbook.createCellStyle(); CellStyle cellStyleLeft = workbook.createCellStyle(); cellStyleCenter.setAlignment(HSSFCellStyle.ALIGN_CENTER); //水平布局:居中 cellStyleCenter.setVerticalAlignment(CellStyle.VERTICAL_CENTER);//垂直居中 cellStyleCenter.setWrapText(true); cellStyleLeft.setAlignment(HSSFCellStyle.ALIGN_LEFT); //水平布局:居左 cellStyleLeft.setVerticalAlignment(CellStyle.VERTICAL_CENTER);//垂直居中 cellStyleLeft.setWrapText(true); Font font =workbook.createFont(); font.setColor(Font.COLOR_NORMAL); //字体颜色 font.setFontName("黑体"); //字体 font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //宽度 // 设置标题单元格类型 centerStyle.setFont(font); centerStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); //水平布局:居中 centerStyle.setWrapText(true); centerStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER); centerStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);//设置前景填充样式 centerStyle.setFillForegroundColor(HSSFColor.GREY_50_PERCENT.index);//前景填充色 CreationHelper createHelper = workbook.getCreationHelper(); // 设置标题边框 centerStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); centerStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); centerStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); centerStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); //分批导出 int totalCount = countCommentNum(commentSearch); //如果导出数据量大于 设定的最大数据量 则最多不能超过设定的数量 if(pageNumExport != null && totalCount > pageNumExport*limitExport){ totalCount = pageNumExport*limitExport; } int number = (totalCount % limitExport) == 0 ? totalCount / limitExport : totalCount / limitExport + 1;
List<CommentVo> commentVOs = new ArrayList<>();
for (int i = 0; i < number; i++) {long startTime = System.currentTimeMillis(); LOGGER.info("【第"+i+"】次开始查询数据库........................................."); commentVOs = getCommentVoExport(commentSearch, i*limitExport, limitExport); LOGGER.info("【第"+i+"】次数据库查询完成....|||||.......用时:" + (System.currentTimeMillis() - startTime)); int page = i+1; Sheet sheet = workbook.createSheet("评论清单"+( page<10 ? "0"+page : page ) );
batchExport(request,sheet, commentSearch, commentVOs, centerStyle, cellStyleCenter, cellStyleLeft);
commentVOs.clear(); } }
3. batchExport(request,sheet, commentSearch, commentVOs, centerStyle, cellStyleCenter, cellStyleLeft); 的实现
/** * 构造excel,赋值,样式 * @param: [request, sheet, commentSearch, commentVOs, centerStyle, cellStyleCenter, cellStyleLeft] * @return: void * @auther: 011336 * @date: 2018/12/7 15:20 */ public void batchExport(HttpServletRequest request,Sheet sheet , CommentSearch commentSearch,List<CommentVo> commentVOs, CellStyle centerStyle,CellStyle cellStyleCenter,CellStyle cellStyleLeft){ if (CollectionUtils.isEmpty(commentVOs)) { LOGGER.debug("exportCommentInfo finish: " + commentVOs); } Row newRowOfInparamTitle = sheet.createRow(0); String[] headerOfInParam = { "序号", "评价日期", "来源", "星级", "评论内容", "类型一级", "类型二级", "类型三级", "情感识别","建议类","订单号", "航班号", "航班日期","订单联系人","联系电话","备注"}; for (int j = 0; j < headerOfInParam.length; j++) { newRowOfInparamTitle.createCell(j); } for (int j = 0; j < headerOfInParam.length; j++) { sheet.getRow(0).getCell(j).setCellValue(new XSSFRichTextString(headerOfInParam[j])); sheet.getRow(0).getCell(j).setCellStyle(centerStyle); } long startTime = System.currentTimeMillis(); LOGGER.info("构造表格开始........................................."); for (int i = 0; i < commentVOs.size(); i++) { CommentVo commentVo = commentVOs.get(i); int index = i + 1; Row createRow = sheet.createRow(i + 1); for (int j = 0; j < headerOfInParam.length; j++) { createRow.createCell(j); } sheet.getRow(i + 1).getCell(0).setCellValue(index + ""); sheet.getRow(i + 1).getCell(0).setCellStyle(cellStyleCenter); sheet.getRow(i + 1).getCell(1).setCellValue(dealTrim(dateToStr2(commentVo.getCmtTime()))); sheet.getRow(i + 1).getCell(1).setCellStyle(cellStyleCenter); sheet.getRow(i + 1).getCell(2).setCellValue(dealTrim(commentVo.getTerminalId())); sheet.getRow(i + 1).getCell(2).setCellStyle(cellStyleCenter); sheet.getRow(i + 1).getCell(3).setCellValue(dealTrim(commentVo.getCmtLevel())); sheet.getRow(i + 1).getCell(3).setCellStyle(cellStyleCenter); sheet.getRow(i + 1).getCell(4).setCellValue(dealTrim(commentVo.getCmtText())); sheet.getRow(i + 1).getCell(4).setCellStyle(cellStyleLeft); sheet.getRow(i + 1).getCell(5).setCellValue(dealTrim(dealTrim(commentVo.getClassfyFirst()))); sheet.getRow(i + 1).getCell(5).setCellStyle(cellStyleLeft); sheet.getRow(i + 1).getCell(6).setCellValue(dealTrim(commentVo.getClassfySecond())); sheet.getRow(i + 1).getCell(6).setCellStyle(cellStyleLeft); sheet.getRow(i + 1).getCell(7).setCellValue(dealTrim(commentVo.getClassfyThird())); sheet.getRow(i + 1).getCell(7).setCellStyle(cellStyleLeft); String emotion = commentVo.getEmotion(); if("0".equals(emotion)){ emotion="差评"; }else if("1".equals(emotion)){ emotion="好评"; } sheet.getRow(i + 1).getCell(8).setCellValue(dealTrim(emotion)); sheet.getRow(i + 1).getCell(8).setCellStyle(cellStyleCenter); String isSuggestion = commentVo.getIsSuggestion(); if("0".equals(isSuggestion)){ isSuggestion="否"; }else if("1".equals(isSuggestion)){ isSuggestion="是"; } sheet.getRow(i + 1).getCell(9).setCellValue(dealTrim(isSuggestion)); sheet.getRow(i + 1).getCell(9).setCellStyle(cellStyleCenter); sheet.getRow(i + 1).getCell(10).setCellValue(dealTrim(commentVo.getOrderNo())); sheet.getRow(i + 1).getCell(10).setCellStyle(cellStyleCenter); sheet.getRow(i + 1).getCell(11).setCellValue(dealTrim(commentVo.getFlightNo())); sheet.getRow(i + 1).getCell(11).setCellStyle(cellStyleCenter); sheet.getRow(i + 1).getCell(12).setCellValue(dateToStr(commentVo.getFlightDate())); sheet.getRow(i + 1).getCell(12).setCellStyle(cellStyleCenter); sheet.getRow(i + 1).getCell(13).setCellValue(dealTrim(commentVo.getcName())); sheet.getRow(i + 1).getCell(13).setCellStyle(cellStyleLeft); sheet.getRow(i + 1).getCell(14).setCellValue(dealTrim(commentVo.getcTel())); sheet.getRow(i + 1).getCell(14).setCellStyle(cellStyleLeft); sheet.getRow(i + 1).getCell(15).setCellValue(dealTrim(commentVo.getRemark())); sheet.getRow(i + 1).getCell(15).setCellStyle(cellStyleLeft); if(i%1000 == 0){ request.getSession().setAttribute("currentNum",i+1); } } LOGGER.info("构造表格结束....|||||.......用时:" + (System.currentTimeMillis() - startTime)); long startTime2 = System.currentTimeMillis(); LOGGER.info("处理行高开始........................................."); dealColumWidth( headerOfInParam, sheet,commentVOs, request); LOGGER.info("处理行高结束....|||||.......用时:" + (System.currentTimeMillis() - startTime2)); }
4. 处理行高代码的实现 dealColumWidth( headerOfInParam, sheet,commentVOs, request);这里主要是设置固定列宽,然后自适应行高,二期自适应行高他是不提供方法的,只能自己去计算
/** * 设置列宽 * @param: [headerOfInParam, sheet, commentVOs, request] * @return: void * @auther: 011336 * @date: 2018/12/7 15:20 */ public void dealColumWidth(String[] headerOfInParam,Sheet sheet,List<CommentVo> commentVOs,HttpServletRequest request){ //单独处理 评论内容 和 评论备注 以及 分类 的宽度 和高度 sheet.setColumnWidth(1, 5500);//11个汉字 评价日期 sheet.setColumnWidth(2, 1500);//3个汉字 来源 sheet.setColumnWidth(3, 1500);//3个汉字 星级 sheet.setColumnWidth(4, 12000);//24个汉字 评论 sheet.setColumnWidth(5, 3000);//6个汉字 分类1 sheet.setColumnWidth(6, 3000);//6个汉字 分类2 sheet.setColumnWidth(7, 4000);//8个汉字 分类3 sheet.setColumnWidth(8, 2500);//5个汉字 情感识别 sheet.setColumnWidth(9, 2000);//4个汉字 建议类 sheet.setColumnWidth(10, 2500);//5个汉字 订单号 sheet.setColumnWidth(11, 2500);//5个汉字 航班号 sheet.setColumnWidth(12, 3500);//7个汉字 航班日期 sheet.setColumnWidth(13, 4000);//8个汉字 订单联系人 sheet.setColumnWidth(14, 4000);//8个汉字 联系电话 sheet.setColumnWidth(15, 10000);//20个汉字 备注 double rn = 20.00 , cn = 24.00 ,c1 = 6.00 ,c2 = 6.00 ,c3 = 8.00 , nn = 8.00;//ca应该为20可是导出结果却换行不对所以改成16保险一点 //计算 设置自定义行高 for (int i = 0; i < commentVOs.size(); i++) { //计算行高 CommentVo commentVo = commentVOs.get(i); int charNumC1 = (int)Math.ceil( dealTrim(commentVo.getClassfyFirst()).length() / c1 );//分类1 int charNumC2 = (int)Math.ceil( dealTrim(commentVo.getClassfySecond()).length() / c2 );//分类2 int charNumC3 = (int)Math.ceil( dealTrim(commentVo.getClassfyThird()).length() / c3 );//分类2 int charNumRemark = (int)Math.ceil( dealTrim(commentVo.getRemark()).length() / rn );//备注 int charNumComent = (int)Math.ceil( dealTrim(commentVo.getCmtText()).length() / cn);//评论 int charNumName = (int)Math.ceil( dealTrim(commentVo.getcName()).length() / nn);//订单联系人 List<Integer> input = new ArrayList<Integer>(); //input.add(charNum+num);//charNum 是分类的条数,每条一行。num是分类字数大于20的情况就多分配一行 input.add(charNumC1); input.add(charNumC2); input.add(charNumC3); input.add(charNumRemark); input.add(charNumComent); input.add(charNumName); int rowNum = Collections.max(input); Row row = sheet.getRow(i+1); row.setHeight((short)(sheet.getDefaultRowHeight()*rowNum)); } request.getSession().setAttribute("currentNum",commentVOs.size()); }
代码中的request可忽略,那是我做的进度条,