相信在大部分的web项目中都会有导出导入Excel的需求,今天我们就来看看如何用Java代码去实现 用POI导出Excel表格。
一、pom引用
pom文件中,添加以下依赖
查看代码
org.apache.poi poi 5.2.2 compile org.apache.poi poi-ooxml 5.2.2 compile
二、工具类util
1.ExcelClassField
查看代码
package com.***; import lombok.Data; import java.util.LinkedHashMap; /** * @description: excel字段 * @author: *** * @date: 2022/6/21 */@Datapublic class ExcelClassField { /** * 字段名称 */ private String fieldName; /** * 表头名称 */ private String name; /** * 映射关系 */ private LinkedHashMap kvMap; /** * 示例值 */ private Object example; /** * 排序 */ private int sort; /** * 是否为注解字段:0-否,1-是 */ private int hasAnnotation; }
2.ExcelExport(导出Excel用)
查看代码
package com.***; import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target; /** * @description: excel导出 * @author: *** * @date: 2022/6/21 */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ExcelExport { /** * 字段名称 */ String value(); /** * 导出排序先后: 数字越小越靠前(默认按Java类字段顺序导出) */ int sort() default 0; /** * 导出映射,格式如:0-未知;1-男;2-女 */ String kv() default ""; /** * 导出模板示例值(有值的话,直接取该值,不做映射) */ String example() default ""; }
3.ExcelImport(导入Excel用)
查看代码
package com.***; import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target; /** * @description: excel导入 * @author: *** * @date: 2022/6/21 */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ExcelImport { /** * 字段名称 */ String value(); /** * 导出映射,格式如:0-未知;1-男;2-女 */ String kv() default ""; /** * 是否为必填字段(默认为非必填) */ boolean required() default false; /** * 最大长度(默认255) */ int maxLength() default 255; /** * 导入唯一性验证(多个字段则取联合验证) */ boolean unique() default false; }
4.ExcelUtils
查看代码
package com.***;import com.***.utils.DateUtil;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import com.***.uitl.StringUtils;import org.apache.poi.hssf.usermodel.HSSFDataValidation;import org.apache.poi.hssf.usermodel.HSSFWorkbook;import org.apache.poi.poifs.filesystem.POIFSFileSystem;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;import org.apache.poi.ss.util.CellRangeAddress;import org.apache.poi.ss.util.CellRangeAddressList;import org.apache.poi.xssf.streaming.SXSSFWorkbook;import org.apache.poi.xssf.usermodel.XSSFClientAnchor;import org.apache.poi.xssf.usermodel.XSSFWorkbook;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.*;import java.lang.reflect.Field;import java.math.BigDecimal;import java.math.RoundingMode;import java.net.URL;import java.net.URLEncoder;import java.text.NumberFormat;import java.text.SimpleDateFormat;import java.util.*;import java.util.Map.Entry;import java.util.regex.Pattern; /** * @description: 导出报表util * @author: *** * @date: 2022/6/21 */public class ExcelUtils { public static final String ROW_MERGE = "row_merge"; public static final String COLUMN_MERGE = "column_merge"; private static final String XLSX = ".xlsx"; private static final String XLS = ".xls"; private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; private static final String ROW_NUM = "rowNum"; private static final String ROW_DATA = "rowData"; private static final String ROW_TIPS = "rowTips"; private static final int CELL_OTHER = 0; private static final int CELL_ROW_MERGE = 1; private static final int CELL_COLUMN_MERGE = 2; private static final int IMG_HEIGHT = 30; private static final int IMG_WIDTH = 30; private static final char LEAN_LINE = '/'; private static final int BYTES_DEFAULT_LENGTH = 10240; private static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance(); public static List readFile(File file, Class clazz) throws Exception { JSONArray array = readFile(file); return getBeanList(array, clazz); } public static List readMultipartFile(MultipartFile mFile, Class clazz) throws Exception { JSONArray array = readMultipartFile(mFile); return getBeanList(array, clazz); } public static JSONArray readFile(File file) throws Exception { return readExcel(null, file); } public static JSONArray readMultipartFile(MultipartFile mFile) throws Exception { return readExcel(mFile, null); } public static Map readFileManySheet(File file) throws Exception { return readExcelManySheet(null, file); } public static Map readFileManySheet(MultipartFile file) throws Exception { return readExcelManySheet(file, null); } private static List getBeanList(JSONArray array, Class clazz) throws Exception { List list = new ArrayList(); Map uniqueMap = new HashMap(16); for (int i = 0; i < array.size(); i++) { list.add(getBean(clazz, array.getJSONObject(i), uniqueMap)); } return list; } /** * 获取每个对象的数据 */ private static T getBean(Class c, JSONObject obj, Map uniqueMap) throws Exception { T t = c.newInstance(); Field[] fields = c.getDeclaredFields(); List errMsgList = new ArrayList(); boolean hasRowTipsField = false; StringBuilder uniqueBuilder = new StringBuilder(); int rowNum = 0; for (Field field : fields) { // 行号 if (field.getName().equals(ROW_NUM)) { rowNum = obj.getInteger(ROW_NUM); field.setAccessible(true); field.set(t, rowNum); continue; } // 是否需要设置异常信息 if (field.getName().equals(ROW_TIPS)) { hasRowTipsField = true; continue; } // 原始数据 if (field.getName().equals(ROW_DATA)) { field.setAccessible(true); field.set(t, obj.toString()); continue; } // 设置对应属性值 setFieldValue(t, field, obj, uniqueBuilder, errMsgList); } // 数据唯一性校验 if (uniqueBuilder.length() > 0) { if (uniqueMap.containsValue(uniqueBuilder.toString())) { Set rowNumKeys = uniqueMap.keySet(); for (Integer num : rowNumKeys) { if (uniqueMap.get(num).equals(uniqueBuilder.toString())) { errMsgList.add(String.format("数据唯一性校验失败,(%s)与第%s行重复)", uniqueBuilder, num)); } } } else { uniqueMap.put(rowNum, uniqueBuilder.toString()); } } // 失败处理 if (errMsgList.isEmpty() && !hasRowTipsField) { return t; } StringBuilder sb = new StringBuilder(); int size = errMsgList.size(); for (int i = 0; i < size; i++) { if (i == size - 1) { sb.append(errMsgList.get(i)); } else { sb.append(errMsgList.get(i)).append(";"); } } // 设置错误信息 for (Field field : fields) { if (field.getName().equals(ROW_TIPS)) { field.setAccessible(true); field.set(t, sb.toString()); } } return t; } private static void setFieldValue(T t, Field field, JSONObject obj, StringBuilder uniqueBuilder, List errMsgList) { // 获取 ExcelImport 注解属性 ExcelImport annotation = field.getAnnotation(ExcelImport.class); if (annotation == null) { return; } String cname = annotation.value(); if (cname.trim().length() == 0) { return; } // 获取具体值 String val = null; if (obj.containsKey(cname)) { val = getString(obj.getString(cname)); } if (val == null) { return; } field.setAccessible(true); // 判断是否必填 boolean require = annotation.required(); if (require && val.isEmpty()) { errMsgList.add(String.format("[%s]不能为空", cname)); return; } // 数据唯一性获取 boolean unique = annotation.unique(); if (unique) { if (uniqueBuilder.length() > 0) { uniqueBuilder.append("--").append(val); } else { uniqueBuilder.append(val); } } // 判断是否超过最大长度 int maxLength = annotation.maxLength(); if (maxLength > 0 && val.length() > maxLength) { errMsgList.add(String.format("[%s]长度不能超过%s个字符(当前%s个字符)", cname, maxLength, val.length())); } // 判断当前属性是否有映射关系 LinkedHashMap kvMap = getKvMap(annotation.kv()); if (!kvMap.isEmpty()) { boolean isMatch = false; for (String key : kvMap.keySet()) { if (kvMap.get(key).equals(val)) { val = key; isMatch = true; break; } } if (!isMatch) { errMsgList.add(String.format("[%s]的值不正确(当前值为%s)", cname, val)); return; } } // 其余情况根据类型赋值 String fieldClassName = field.getType().getSimpleName(); try { if ("String".equalsIgnoreCase(fieldClassName)) { field.set(t, val); } else if ("boolean".equalsIgnoreCase(fieldClassName)) { field.set(t, Boolean.valueOf(val)); } else if ("int".equalsIgnoreCase(fieldClassName) || "Integer".equals(fieldClassName)) { try { field.set(t, Integer.valueOf(val)); } catch (NumberFormatException e) { errMsgList.add(String.format("[%s]的值格式不正确(当前值为%s)", cname, val)); } } else if ("double".equalsIgnoreCase(fieldClassName)) { field.set(t, Double.valueOf(val)); } else if ("long".equalsIgnoreCase(fieldClassName)) { field.set(t, Long.valueOf(val)); } else if ("BigDecimal".equalsIgnoreCase(fieldClassName)) { if (StringUtils.isEmpty(val)) { field.set(t, new BigDecimal(0)); } else { field.set(t, new BigDecimal(val)); } } else if ("Date".equalsIgnoreCase(fieldClassName)) { field.set(t, DateUtil.getDate(val, DateUtil.YYYYMMDDHHMMSS)); } else { field.set(t, val); } } catch (Exception e) { e.printStackTrace(); } } private static Map readExcelManySheet(MultipartFile mFile, File file) throws IOException { Workbook book = getWorkbook(mFile, file); if (book == null) { return Collections.emptyMap(); } Map map = new LinkedHashMap(); for (int i = 0; i < book.getNumberOfSheets(); i++) { Sheet sheet = book.getSheetAt(i); JSONArray arr = readSheet(sheet); map.put(sheet.getSheetName(), arr); } book.close(); return map; } private static JSONArray readExcel(MultipartFile mFile, File file) throws IOException { Workbook book = getWorkbook(mFile, file); if (book == null) { return new JSONArray(); } JSONArray array = readSheet(book.getSheetAt(0)); book.close(); return array; } private static Workbook getWorkbook(MultipartFile mFile, File file) throws IOException { boolean fileNotExist = (file == null || !file.exists()); if (mFile == null && fileNotExist) { return null; } // 解析表格数据 InputStream in; String fileName; if (mFile != null) { // 上传文件解析 in = mFile.getInputStream(); fileName = getString(mFile.getOriginalFilename()).toLowerCase(); } else { // 本地文件解析 in = new FileInputStream(file); fileName = file.getName().toLowerCase(); } Workbook book; if (fileName.endsWith(XLSX)) { book = new XSSFWorkbook(in); } else if (fileName.endsWith(XLS)) { POIFSFileSystem poifsFileSystem = new POIFSFileSystem(in); book = new HSSFWorkbook(poifsFileSystem); } else { return null; } in.close(); return book; } private static JSONArray readSheet(Sheet sheet) { // 首行下标 int rowStart = sheet.getFirstRowNum(); // 尾行下标 int rowEnd = sheet.getLastRowNum(); // 获取表头行 Row headRow = sheet.getRow(rowStart); if (headRow == null) { return new JSONArray(); } int cellStart = headRow.getFirstCellNum(); int cellEnd = headRow.getLastCellNum(); Map keyMap = new HashMap(); for (int j = cellStart; j < cellEnd; j++) { // 获取表头数据 String val = getCellValue(headRow.getCell(j)); if (val != null && val.trim().length() != 0) { keyMap.put(j, val); } } // 如果表头没有数据则不进行解析 if (keyMap.isEmpty()) { return (JSONArray) Collections.emptyList(); } // 获取每行JSON对象的值 JSONArray array = new JSONArray(); // 如果首行与尾行相同,表明只有一行,返回表头数据 if (rowStart == rowEnd) { JSONObject obj = new JSONObject(); // 添加行号 obj.put(ROW_NUM, 1); for (int i : keyMap.keySet()) { obj.put(keyMap.get(i), ""); } array.add(obj); return array; } for (int i = rowStart + 1; i <= rowEnd; i++) { Row eachRow = sheet.getRow(i); JSONObject obj = new JSONObject(); // 添加行号 obj.put(ROW_NUM, i + 1); StringBuilder sb = new StringBuilder(); for (int k = cellStart; k 0) { array.add(obj); } } return array; } private static String getCellValue(Cell cell) { // 空白或空 if (cell == null || cell.getCellType() == CellType.BLANK) { return ""; } // String类型 if (cell.getCellType() == CellType.STRING) { String val = cell.getStringCellValue(); if (val == null || val.trim().length() == 0) { return ""; } return val.trim(); } // 数字类型 if (cell.getCellType() == CellType.NUMERIC) { String s = cell.getNumericCellValue() + ""; // 去掉尾巴上的小数点0 if (Pattern.matches(".*\\.0*", s)) { return s.split("\\.")[0]; } else { return s.trim(); } } // 布尔值类型 if (cell.getCellType() == CellType.BOOLEAN) { return cell.getBooleanCellValue() + ""; } // 错误类型 return cell.getCellFormula(); } public static void exportTemplate(HttpServletResponse response, String fileName, Class clazz, String dateFormat) { exportTemplate(response, fileName, fileName, clazz, false, dateFormat); } public static void exportTemplate(HttpServletResponse response, String fileName, String sheetName, Class clazz, String dateFormat) { exportTemplate(response, fileName, sheetName, clazz, false, dateFormat); } public static void exportTemplate(HttpServletResponse response, String fileName, Class clazz, boolean isContainExample, String dateFormat) { exportTemplate(response, fileName, fileName, clazz, isContainExample, dateFormat); } public static void exportTemplate(HttpServletResponse response, String fileName, String sheetName, Class clazz, boolean isContainExample, String dateFormat) { // 获取表头字段 List headFieldList = getExcelClassFieldList(clazz); // 获取表头数据和示例数据 List<List
三、相关业务代码
1.service层
/*** 车运运单信息导出*/ List exportBillInfo(List billIdList);
2.impl实现类
实现类里的代码,需求各位根据自己的业务场景进行改动,将需要导出的数据查询出来
查看代码
@Override public List exportBillInfo(List billIdList) { if (StringUtils.isEmpty(billIdList)) { throw new ServiceException("车辆运单ID不能为空"); } BillDto billDto = new BillDto(); billDto.setBillIdList(billIdList); List billList = billFacadeService.queryBillList(billDto); if (StringUtils.isEmpty(billList)) { billList = new ArrayList(); } return InvoiceWrapper.instance.convertTo(billList); }
3.controller层
controller层的代码需要注意的是:
1.因为导出Excel一般都是通过浏览器进行下载的,所以入参中需要加入HttpServletResponse
2.调用封装的工具类ExcelUtils中的export方法就可以了
查看代码
/** * 车运运单信息导出 */ @Log @PostMapping("/exportBillInfo") public void exportBillInfo(@RequestBody @Valid BillListDto dto, HttpServletResponse response) { List list = invoiceFacadeService.exportBillInfo(dto.getBillIdList()); ExcelUtils.export(response, "车运运单信息", list, ExportBillVo.class); }
最终导出的Excel文件: