POI导入导出及动态复杂表头生成
在此之前打开POI分类已存在三篇文章介绍了POI的使用及流下载,近期项目发现之前的使用中是有缺陷的,今天做一下总结,从现在起我们可以忽略前三篇文章对POI的使用,但是流下载方式还是可以参考的,这里还是再提一笔,流下载使用ajax会出异常,不生效,我是用的是提交form表单的方式来进行流下载,很遗憾,暂时取不到返回值
下面我们继续谈一下POI的使用
首先需要强调几点,POI对于Excel2003和Excel2007+是区别对待的,分别使用了HSSFWorkbook和XSSFWorkbook,但它们实现了同一个接口Workbook,这对我们的编程实现提供了一定的灵活性
建议:对于导出,我们直接使用XSSFWorkbook即可,HSSFWorkbook是存在一定缺陷,之前使用过HSSFWorkbook,数据量达到65536就会报错导出失败,近期做新的项目才发现原因
相对于导入工作,需要动态对文件识别Excel2003或是Excel2007+,有些文章写到通过后缀名来辨别,这存在一个问题,用户修改后缀之后,结果可想而知,幸亏POI提供了一个便利的方法Workbook create(InputStream inp),通过输入流动态创建,GOOD!!!下面,我也会提供相关的用法。在这里还要给一个提示,使用3.9版本这种方法报错,在此,我来提供一个maven info
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.15</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.15</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>3.15</version> </dependency>
没错,这三个包都是需要的,正是为了实现这个功能,我对此封装了一个POI的工具包
1 import org.apache.poi.hssf.usermodel.*; 2 import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 3 import org.apache.poi.ss.usermodel.*; 4 import org.apache.poi.ss.util.CellRangeAddress; 5 import org.apache.poi.ss.util.NumberToTextConverter; 6 import org.apache.poi.xssf.usermodel.XSSFWorkbook; 7 8 import java.io.FileOutputStream; 9 import java.io.IOException; 10 import java.io.InputStream; 11 import java.text.DateFormat; 12 import java.text.SimpleDateFormat; 13 import java.util.*; 14 15 public class PoiUtil { 16 private PoiUtil() { 17 } 18 19 /** 20 * Excel2003和Excel2007+创建方式不同 21 * Excel2003使用HSSFWorkbook 后缀xls 22 * Excel2007+使用XSSFWorkbook 后缀xlsx 23 * 此方法可保证动态创建Workbook 24 * 25 * @param is 26 * @return 27 */ 28 public static Workbook createWorkbook(InputStream is) throws IOException, InvalidFormatException { 29 return WorkbookFactory.create(is); 30 } 31 32 /** 33 *导入数据获取数据列表 34 * @param wb 35 * @return 36 */ 37 public static List<List<Object>> getDataList(Workbook wb) { 38 List<List<Object>> rowList = new ArrayList<>(); 39 for (int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) { 40 Sheet sheet = wb.getSheetAt(sheetNum); 41 for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) { 42 Row row = sheet.getRow(i); 43 if (null == row) 44 continue; 45 List<Object> cellList = new ArrayList<>(); 46 for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) { 47 Cell cell = row.getCell(j); 48 cellList.add(getCellValue(cell)); 49 } 50 rowList.add(cellList); 51 } 52 } 53 return rowList; 54 } 55 56 private static String getCellValue(Cell cell) { 57 String cellvalue = ""; 58 if (cell != null) { 59 // 判断当前Cell的Type 60 switch (cell.getCellType()) { 61 // 如果当前Cell的Type为NUMERIC 62 case HSSFCell.CELL_TYPE_NUMERIC: { 63 short format = cell.getCellStyle().getDataFormat(); 64 if (format == 14 || format == 31 || format == 57 || format == 58) { //excel中的时间格式 65 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 66 double value = cell.getNumericCellValue(); 67 Date date = DateUtil.getJavaDate(value); 68 cellvalue = sdf.format(date); 69 } 70 // 判断当前的cell是否为Date 71 else if (HSSFDateUtil.isCellDateFormatted(cell)) { //先注释日期类型的转换,在实际测试中发现HSSFDateUtil.isCellDateFormatted(cell)只识别2014/02/02这种格式。 72 // 如果是Date类型则,取得该Cell的Date值 // 对2014-02-02格式识别不出是日期格式 73 Date date = cell.getDateCellValue(); 74 DateFormat formater = new SimpleDateFormat("yyyy-MM-dd"); 75 cellvalue = formater.format(date); 76 } else { // 如果是纯数字 77 // 取得当前Cell的数值 78 cellvalue = NumberToTextConverter.toText(cell.getNumericCellValue()); 79 80 } 81 break; 82 } 83 // 如果当前Cell的Type为STRIN 84 case HSSFCell.CELL_TYPE_STRING: 85 // 取得当前的Cell字符串 86 cellvalue = cell.getStringCellValue().replaceAll("'", "''"); 87 break; 88 case HSSFCell.CELL_TYPE_BLANK: 89 cellvalue = null; 90 break; 91 // 默认的Cell值 92 default: { 93 cellvalue = " "; 94 } 95 } 96 } else { 97 cellvalue = ""; 98 } 99 return cellvalue; 100 } 101 102 /** 103 * 此方法生成表头并写入表头名称 104 * 105 * @param nodes 节点 106 * @param sheet 工作簿 107 * @param style 单元格样式 108 * @return 数据加载开始行 109 */ 110 public static int generateHeader(List<HeaderNode> nodes, Sheet sheet, CellStyle style) { 111 Map<RowKey, Row> hssfRowMap = new HashMap<>(); 112 int dataStartRow = 0; 113 for (HeaderNode node : nodes) { 114 if (!(node.firstRow == node.getLastCol() || node.getFirstCol() == node.getLastCol())) { 115 CellRangeAddress cra = new CellRangeAddress(node.getFirstRow(), node.getLastRow(), 116 node.getFirstCol(), node.getLastCol()); 117 sheet.addMergedRegion(cra); 118 } 119 dataStartRow = dataStartRow >= node.getLastRow() ? dataStartRow : node.getLastRow(); 120 RowKey key = new RowKey(); 121 key.setFirstRow(node.getFirstRow()); 122 key.setLastRow(node.getLastRow()); 123 Row row = hssfRowMap.get(key); 124 if (null == row) { 125 row = sheet.createRow(node.getFirstRow()); 126 hssfRowMap.put(key, row); 127 } 128 Cell cell = row.createCell(node.getFirstCol()); 129 cell.setCellValue(node.getName()); 130 cell.setCellStyle(style); 131 } 132 return dataStartRow+1; 133 } 134 135 public static class HeaderNode { 136 private String name; 137 private int firstRow; 138 private int lastRow; 139 private int firstCol; 140 private int lastCol; 141 142 public String getName() { 143 return name; 144 } 145 146 public void setName(String name) { 147 this.name = name; 148 } 149 150 public int getFirstRow() { 151 return firstRow; 152 } 153 154 public void setFirstRow(int firstRow) { 155 this.firstRow = firstRow; 156 } 157 158 public int getLastRow() { 159 return lastRow; 160 } 161 162 public void setLastRow(int lastRow) { 163 this.lastRow = lastRow; 164 } 165 166 public int getFirstCol() { 167 return firstCol; 168 } 169 170 public void setFirstCol(int firstCol) { 171 this.firstCol = firstCol; 172 } 173 174 public int getLastCol() { 175 return lastCol; 176 } 177 178 public void setLastCol(int lastCol) { 179 this.lastCol = lastCol; 180 } 181 } 182 183 private static class RowKey { 184 private int firstRow; 185 private int lastRow; 186 187 public int getFirstRow() { 188 return firstRow; 189 } 190 191 public void setFirstRow(int firstRow) { 192 this.firstRow = firstRow; 193 } 194 195 public int getLastRow() { 196 return lastRow; 197 } 198 199 public void setLastRow(int lastRow) { 200 this.lastRow = lastRow; 201 } 202 203 @Override 204 public boolean equals(Object o) { 205 if (this == o) return true; 206 if (!(o instanceof RowKey)) return false; 207 RowKey key = (RowKey) o; 208 return firstRow == key.firstRow && 209 lastRow == key.lastRow; 210 } 211 212 @Override 213 public int hashCode() { 214 return Objects.hash(firstRow, lastRow); 215 } 216 } 217 218 public static void main(String[] args) { 219 // 第一步,创建一个webbook,对应一个Excel文件 220 Workbook workbook = new XSSFWorkbook(); 221 // 第二步,在webbook中添加一个sheet,对应Excel文件中的sheet 222 Sheet sheet = workbook.createSheet("测试"); 223 // 第四步,创建单元格,并设置值表头 设置表头居中 224 CellStyle style = workbook.createCellStyle(); 225 style.setAlignment(HorizontalAlignment.CENTER); // 水平居中格式 226 style.setVerticalAlignment(VerticalAlignment.CENTER); //垂直居中 227 List<HeaderNode> nodes = new ArrayList<>(); 228 HeaderNode headerNode1 = new HeaderNode(); 229 headerNode1.setName("test1"); 230 headerNode1.setFirstRow(0); 231 headerNode1.setLastRow(1); 232 headerNode1.setFirstCol(0); 233 headerNode1.setLastCol(5); 234 nodes.add(headerNode1); 235 HeaderNode headerNode34 = new HeaderNode(); 236 headerNode34.setName("test4"); 237 headerNode34.setFirstRow(3); 238 headerNode34.setLastRow(4); 239 headerNode34.setFirstCol(0); 240 headerNode34.setLastCol(5); 241 nodes.add(headerNode34); 242 HeaderNode headerNode2 = new HeaderNode(); 243 headerNode2.setName("test2"); 244 headerNode2.setFirstRow(2); 245 headerNode2.setLastRow(2); 246 headerNode2.setFirstCol(0); 247 headerNode2.setLastCol(4); 248 nodes.add(headerNode2); 249 HeaderNode headerNode3 = new HeaderNode(); 250 headerNode3.setName("test3"); 251 headerNode3.setFirstRow(2); 252 headerNode3.setLastRow(2); 253 headerNode3.setFirstCol(5); 254 headerNode3.setLastCol(5); 255 nodes.add(headerNode3); 256 generateHeader(nodes, sheet, style); 257 try { 258 FileOutputStream output = new FileOutputStream("e:\\workbook.xls"); 259 workbook.write(output); 260 output.flush(); 261 } catch (IOException e) { 262 e.printStackTrace(); 263 } 264 } 265 }
下面是导出报表及流下载的代码
1 List<PoiUtil.HeaderNode> nodes = new ArrayList<>(); 2 for (RpStyleItem item : styleItems){ 3 PoiUtil.HeaderNode headerNode = new PoiUtil.HeaderNode(); 4 headerNode.setFirstRow(item.getRow() - 1); 5 headerNode.setLastRow(headerNode.getFirstRow()+item.getSizeY() - 1); 6 headerNode.setFirstCol(item.getCol() - 1); 7 headerNode.setLastCol(headerNode.getFirstCol() + item.getSizeX() - 1); 8 headerNode.setName(item.getName()); 9 nodes.add(headerNode); 10 } 11 // 第一步,创建一个webbook,对应一个Excel文件 12 // XSSFWorkbook针对于excel2007+ 13 // HSSFWorkbook针对于Excel2003(数据超过65536会报错) 14 Workbook workbook = new XSSFWorkbook(); 15 // 第二步,在webbook中添加一个sheet,对应Excel文件中的sheet 16 Sheet sheet = workbook.createSheet(template.getName()); 17 // 第三步,创建单元格样式 18 CellStyle style = workbook.createCellStyle(); 19 style.setAlignment(HorizontalAlignment.CENTER); // 水平居中格式 20 style.setVerticalAlignment(VerticalAlignment.CENTER); //垂直居中 21 int rowNum = PoiUtil.generateHeader(nodes,sheet,style); 22 String sql = template.getReportSql().replace("\n", " "); 23 String reportDate = com.jrq.components.date.DateUtil.dateFormat(new Date(), "yyyyMMdd"); 24 //测试数据 String reportDate = "20170711"; 25 List<Map<String, Object>> datas = reportService.listReportData(reportDate, sql); 26 for (Map<String, Object> map : datas){ 27 Collection<Object> values = map.values(); 28 int index = 0; //cell单元格索引 29 Row row = sheet.createRow(rowNum++); 30 for (Object obj : values){ 31 row.createCell(index++).setCellValue(obj.toString()); 32 } 33 } 34 /* 直接将将文件保存在本地,供测试样式使用 35 try { 36 OutputStream output=new FileOutputStream("e:\\workbook.xls"); 37 workbook.write(output); 38 output.flush(); 39 output.close(); 40 } catch (IOException e) { 41 e.printStackTrace(); 42 }*/ 43 String downFileName = new String(template.getType().getName()+reportDate+".xls"); 44 try { 45 //若不进行编码在IE下会乱码 46 downFileName = URLEncoder.encode(downFileName, "UTF-8"); 47 } catch (UnsupportedEncodingException e) { 48 e.printStackTrace(); 49 } 50 try { 51 // 清空response 52 response.reset(); 53 response.setContentType("application/msexcel");//设置生成的文件类型 54 response.setCharacterEncoding("UTF-8");//设置文件头编码方式和文件名 55 response.setHeader("Content-Disposition", "attachment; filename=" + downFileName); 56 OutputStream os=response.getOutputStream(); 57 workbook.write(os); 58 os.flush(); 59 os.close(); 60 } catch (IOException e) { 61 LOG.error("文件流操作异常"); 62 jr.setRet("文件流操作异常"); 63 return jr; 64 } 65 jr.setSuc(JsonResponse.SUCCESS); 66 return jr;
下面是导入功能的代码
1 MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; 2 MultipartFile file = multipartRequest.getFile("fileUpload"); 3 if (!file.isEmpty()) { 4 Workbook workbook = PoiUtil.createWorkbook(file.getInputStream()); 5 if (null != workbook) { 6 List<List<Object>> list = PoiUtil.getDataList(workbook);}}
可以看到这里是前端上传了excel文件这里,前端的HTML也贴一下
<form method="POST" enctype="multipart/form-data" id="form1" action="/center/addition/list/uploadfile"> <input class="typeahead scrollable" type="file" id="fileUpload" name="fileUpload"/> <input type="submit" value="上传" id="btn_submit"/> </form>
https://blog.csdn.net/ab7253957/article/details/76076600