一個基於POI的通用excel匯入匯出工具類的簡單實現及使用方法
阿新 • • 發佈:2018-12-29
前言:
最近PM來了一個需求,簡單來說就是在錄入資料時一條一條插入到系統顯得非常麻煩,讓我實現一個直接通過excel匯入的方法一次性錄入所有資料。網上關於excel匯入匯出的例子很多,但大多相互借鑑。經過思考,認為一百個客戶在錄入excel的時候,就會有一百個格式版本,所以在實現這個功能之前,所以要統一excel的格式。於是提供了一個通用excel模版的下載功能。當所有客戶用模版錄入好資料再上傳到系統,後端對excel進行解析,然後再持久化到資料庫。
概述:
此工具類的幾大特點
1、基本匯入匯出
2、提供excel模版自動生成及下載功能
3、建立模版過程簡單通用,只需要在實體類上進行註解
4、springMVC框架
5、模版可以生成下拉框選擇列
廢話不多說,上程式碼......
一、引入poi相關依賴(及spring上傳檔案相關配置,不再解釋)
1 <!-- apache poi start --> 2 <poi.version>3.14</poi.version> 3 <dependency> 4 <groupId>org.apache.poi</groupId> 5 <artifactId>poi</artifactId> 6 <version>${poi.version}</version> 7 </dependency> 8 9 <dependency> 10 <groupId>org.apache.poi</groupId> 11 <artifactId>poi-scratchpad</artifactId> 12 <version>${poi.version}</version> 13 </dependency> 14 15 <dependency> 16 <groupId>org.apache.poi</groupId> 17 <artifactId>poi-ooxml</artifactId> 18 <version>${poi.version}</version> 19 </dependency> 20 <!-- apache poi end -->
二、excel匯入匯出工具類的實現
1 /** 2 * @Description 3 * @author zhaomin E-mail:[email protected] 4 * @date 建立時間:2017年2月14日 下午2:13:30 5 * @version 1.0 6 */ 7 public class ImportExcelUtil { 8 final static String notnullerror = "請填入第{0}行的{1},{2}不能為空"; 9 final static String errormsg = "第{0}行的{1}資料匯入錯誤"; 10 11 /** 12 * 匯入Excel 13 * 14 * @param clazz 15 * @param xls 16 * @return 17 * @throws Exception 18 */ 19 @SuppressWarnings("rawtypes") 20 public static List importExcel(Class<?> clazz, InputStream xls) throws Exception { 21 try { 22 // 取得Excel 23 HSSFWorkbook wb = new HSSFWorkbook(xls); 24 HSSFSheet sheet = wb.getSheetAt(0); 25 Field[] fields = clazz.getDeclaredFields(); 26 List<Field> fieldList = new ArrayList<Field>(fields.length); 27 for (Field field : fields) { 28 if (field.isAnnotationPresent(ModelProp.class)) { 29 ModelProp modelProp = field.getAnnotation(ModelProp.class); 30 if (modelProp.colIndex() != -1) { 31 fieldList.add(field); 32 } 33 } 34 } 35 EmployeeDTO employee = new EmployeeDTO(); 36 // 行迴圈 37 List<ImportModel> modelList = new ArrayList<ImportModel>(sheet.getPhysicalNumberOfRows() * 2); 38 for (int i = 2; i < sheet.getPhysicalNumberOfRows(); i++) { 39 // 資料模型 40 ImportModel model = (ImportModel) clazz.newInstance(); 41 int nullCount = 0; 42 Exception nullError = null; 43 for (Field field : fieldList) { 44 ModelProp modelProp = field.getAnnotation(ModelProp.class); 45 HSSFCell cell = sheet.getRow(i).getCell(modelProp.colIndex()); 46 try { 47 if (cell == null || cell.toString().length() == 0) { 48 nullCount++; 49 if (!modelProp.nullable()) { 50 nullError = new Exception(StringUtil.format(notnullerror, 51 new String[] { "" + (1 + i), modelProp.name(), modelProp.name() })); 52 53 } 54 } else if (field.getType().equals(Date.class)) { 55 if (Cell.CELL_TYPE_STRING == cell.getCellType()) { 56 BeanUtils.setProperty(model, field.getName(), new Date(parseDate(parseString(cell)))); 57 } else { 58 BeanUtils.setProperty(model, field.getName(), 59 new Date(cell.getDateCellValue().getTime())); 60 61 } 62 } else if (field.getType().equals(Timestamp.class)) { 63 if (Cell.CELL_TYPE_STRING == cell.getCellType()) { 64 BeanUtils.setProperty(model, field.getName(), 65 new Timestamp(parseDate(parseString(cell)))); 66 } else { 67 BeanUtils.setProperty(model, field.getName(), 68 new Timestamp(cell.getDateCellValue().getTime())); 69 } 70 71 } else if (field.getType().equals(java.sql.Date.class)) { 72 if (Cell.CELL_TYPE_STRING == cell.getCellType()) { 73 BeanUtils.setProperty(model, field.getName(), 74 new java.sql.Date(parseDate(parseString(cell)))); 75 } else { 76 BeanUtils.setProperty(model, field.getName(), 77 new java.sql.Date(cell.getDateCellValue().getTime())); 78 } 79 } else if (field.getType().equals(java.lang.Integer.class)) { 80 if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) { 81 BeanUtils.setProperty(model, field.getName(), (int) cell.getNumericCellValue()); 82 } else if (Cell.CELL_TYPE_STRING == cell.getCellType()) { 83 BeanUtils.setProperty(model, field.getName(), Integer.parseInt(parseString(cell))); 84 } 85 } else if (field.getType().equals(java.math.BigDecimal.class)) { 86 if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) { 87 BeanUtils.setProperty(model, field.getName(), 88 new BigDecimal(cell.getNumericCellValue())); 89 } else if (Cell.CELL_TYPE_STRING == cell.getCellType()) { 90 BeanUtils.setProperty(model, field.getName(), new BigDecimal(parseString(cell))); 91 } 92 } else { 93 if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) { 94 BeanUtils.setProperty(model, field.getName(), 95 new BigDecimal(cell.getNumericCellValue())); 96 } else if (Cell.CELL_TYPE_STRING == cell.getCellType()) { 97 BeanUtils.setProperty(model, field.getName(), parseString(cell)); 98 } 99 } 100 } catch (Exception e) { 101 e.printStackTrace(); 102 throw new Exception(StringUtil.format(errormsg, new String[] { "" + (1 + i), modelProp.name() }) 103 + "," + e.getMessage()); 104 } 105 } 106 if (nullCount == fieldList.size()) { 107 break; 108 } 109 if (nullError != null) { 110 throw nullError; 111 } 112 modelList.add(model); 113 } 114 return modelList; 115 116 } finally { 117 xls.close(); 118 } 119 } 120 121 private final static int colsizeN = 630; 122 private final static int colsizeM = 1000; 123 124 /** 125 * 下載Excel模版 126 * 127 * @param clazz 128 * @param map 129 * @param rowSize 130 * @return 131 */ 132 public static InputStream excelModelbyClass(Class<?> clazz, Map<Integer, String[]> map, Integer rowSize) { 133 try { 134 if (!clazz.isAnnotationPresent(ModelTitle.class)) { 135 throw new Exception("請在此型別中加上ModelTitle註解"); 136 } 137 if (rowSize == null) { 138 rowSize = 1000; 139 } 140 HSSFWorkbook wb = new HSSFWorkbook(); 141 HSSFSheet sheet = wb.createSheet(); 142 /** 143 * 設定標題樣式 144 */ 145 HSSFCellStyle titleStyle = wb.createCellStyle(); 146 titleStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); 147 HSSFFont font = wb.createFont(); 148 font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); 149 font.setFontHeight((short) 400); 150 titleStyle.setFont(font); 151 HSSFCell titleCell = sheet.createRow(0).createCell(0); // 建立第一行,並在該行建立單元格,設定內容,做為標題行 152 /** 153 * 獲取標題 154 */ 155 ModelTitle modelTitle = clazz.getAnnotation(ModelTitle.class); 156 titleCell.setCellValue(new HSSFRichTextString(modelTitle.name())); 157 titleCell.setCellStyle(titleStyle); 158 159 Field[] fields = clazz.getDeclaredFields(); 160 HSSFRow headRow = sheet.createRow(1); 161 int colSzie = 0; 162 /** 163 * 設定表頭樣式 164 */ 165 HSSFCellStyle headStyle = wb.createCellStyle(); 166 headStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); 167 HSSFFont headFont = wb.createFont(); 168 headFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); 169 headFont.setFontHeight((short) 240); 170 headStyle.setFont(headFont); 171 List<Integer> cells = new ArrayList<Integer>(); 172 173 for (Field field : fields) { 174 if (field.isAnnotationPresent(ModelProp.class)) { 175 ModelProp modelProp = field.getAnnotation(ModelProp.class); 176 if (modelProp.colIndex() == -1) 177 continue; 178 cells.add(modelProp.colIndex()); 179 HSSFCell cell = headRow.createCell(modelProp.colIndex()); 180 cell.setCellValue(new HSSFRichTextString(modelProp.name())); 181 cell.setCellStyle(headStyle); 182 colSzie++; 183 sheet.autoSizeColumn((short) modelProp.colIndex()); 184 sheet.setColumnWidth(modelProp.colIndex(), modelProp.name().length() * colsizeN + colsizeM); 185 186 // 設定列為下拉框格式 187 if (map != null && map.get(new Integer(modelProp.colIndex())) != null) { 188 DVConstraint constraint = DVConstraint 189 .createExplicitListConstraint(map.get(modelProp.colIndex())); 190 CellRangeAddressList regions = new CellRangeAddressList(2, rowSize, modelProp.colIndex(), 191 modelProp.colIndex()); 192 HSSFDataValidation dataValidation = new HSSFDataValidation(regions, constraint); 193 sheet.addValidationData(dataValidation); 194 } 195 } 196 } 197 HSSFCellStyle cellStyle = wb.createCellStyle(); 198 HSSFDataFormat format = wb.createDataFormat(); 199 cellStyle.setDataFormat(format.getFormat("@")); 200 for (int i = 2; i < rowSize; i++) { 201 HSSFRow row = sheet.createRow(i); 202 for (Integer integer : cells) { 203 HSSFCell cell = row.createCell(integer); 204 cell.setCellStyle(cellStyle); 205 } 206 } 207 sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, colSzie - 1)); 208 if (map != null) { 209 for (Integer colIndex : map.keySet()) { 210 DVConstraint constraint = DVConstraint.createExplicitListConstraint(map.get(colIndex)); 211 CellRangeAddressList regions = new CellRangeAddressList(2, 1000, colIndex, colIndex); 212 HSSFDataValidation dataValidation = new HSSFDataValidation(regions, constraint); 213 sheet.addValidationData(dataValidation); 214 } 215 } 216 217 ByteArrayOutputStream os = new ByteArrayOutputStream(); 218 try { 219 wb.write(os); 220 } catch (IOException e) { 221 e.printStackTrace(); 222 } 223 224 byte[] b = os.toByteArray(); 225 226 ByteArrayInputStream in = new ByteArrayInputStream(b); 227 return in; 228 } catch (Exception e) { 229 e.printStackTrace(); 230 return null; 231 } 232 } 233 234 private static String parseString(HSSFCell cell) { 235 return String.valueOf(cell).trim(); 236 } 237 238 private static long parseDate(String dateString) throws ParseException { 239 if (dateString.indexOf("/") == 4) { 240 return new SimpleDateFormat("yyyy/MM/dd").parse(dateString).getTime(); 241 } else if (dateString.indexOf("-") == 4) { 242 return new SimpleDateFormat("yyyy-MM-dd").parse(dateString).getTime(); 243 } else if (dateString.indexOf("年") == 4) { 244 return new SimpleDateFormat("yyyy年MM月dd").parse(dateString).getTime(); 245 } else if (dateString.length() == 8) { 246 return new SimpleDateFormat("yyyyMMdd").parse(dateString).getTime(); 247 } else { 248 return new Date().getTime(); 249 } 250 } 251 252 }
三、自定義spring註解
1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.FIELD) 3 public @interface ModelProp{ 4 public String name(); 5 public int colIndex() default -1; 6 public boolean nullable() default true; 7 public String interfaceXmlName() default ""; 8 }
1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.TYPE) 3 public @interface ModelTitle{ 4 public String name(); 5 }
四、定義實體類父類
1 public class ImportModel { 2 3 }
五、定義實體類
1 @ModelTitle(name="人員列表") 2 public class EmployeeDTO extends ImportModel implements Serializable { 3 4 private static final long serialVersionUID = -3434719712955859295L; 5 6 private Long id; 7 @ModelProp(name = "電話", colIndex = 1, nullable = false) 8 private String telephone; 9 10 @ModelProp(name = "名稱", colIndex = 0, nullable = false) 11 private String name; 12 13 @ModelProp(name = "性別", colIndex = 2, nullable = false) 14 private Integer sex; 15 }
六、定義controller
1 @RestController 2 @RequestMapping("/api/excelOpera") 3 public class ImportEmployeeController extends BaseController { 4 5 private static Logger logger = LoggerFactory.getLogger(ImportEmployeeController.class); 6 /** 7 * 匯入excel表 8 * @version 1.0 9 * @since 1.0 10 */ 11 @RequestMapping(path = "/importEmployee", method = RequestMethod.POST) 12 public RespMsg uploadExcel(@RequestParam("file") MultipartFile file, HttpServletRequest request) { 13 try{ 14 15 List<EmployeeDTO> employeeDTOList = ImportExcelUtil.importExcel(EmployeeDTO.class, file.getInputStream()); 16 //可做持久化操作,現只打印觀察 17 for(EmployeeDTO employeeDTO : employeeDTOList){ 18 logger.info("name=" + employeeDTO.getName() + ",telephone=" + employeeDTO.getTelephone()+",sex=" + employeeDTO.getSex()); 19 } 20 }catch(Exception e){ 21 logger.error(e.getMessage()); 22 } 23 return null; 24 } 25 /** 26 * 匯出excel模版 27 * @version 1.0 28 * @since 1.0 29 */ 30 @RequestMapping(path = "/downloadEmployeeModel", method = RequestMethod.GET) 31 public RespMsg downloadEmployeeModel(HttpServletResponse response) { 32 try{ 33 response.setContentType("application/xls"); 34 response.addHeader("Content-Disposition", "attachment;filename="+new String(("eeelist").getBytes("UTF-8"),"iso-8859-1")+".xls"); 35 Map<Integer,String[]> paramMap = new HashMap<Integer,String[]>(); 36 //excel第三行為下拉選擇框 37 paramMap.put(2, new String[]{"man","women"}); 38 BufferedInputStream input = new BufferedInputStream(ImportExcelUtil.excelModelbyClass(EmployeeDTO.class, paramMap, null)); 39 byte buffBytes[] = new byte[1024]; 40 OutputStream os = response.getOutputStream(); 41 int read = 0; 42 while ((read = input.read(buffBytes)) != -1) { 43 os.write(buffBytes, 0, read); 44 } 45 os.flush(); 46 os.close(); 47 input.close(); 48 return success("下載成功!"); 49 }catch(Exception e){ 50 logger.error("downloadEmployeeModel() catch Exception ",e); 51 return fail("下載失敗!"); 52 } 53 } 54 55 } 56
至此全部工具類的實現已經完成,可以請求訪問檢驗一下結果。
下載下來的excel模版
填寫資料上傳
後臺控制檯列印輸出結果
檢驗結果能夠達到預期效果且能通用,是不是很簡單呢,歡迎大神們提出意見,小女子感謝了。