註解Excel導入導出
目錄
-
excel目錄結構:
-
excel
-
annotation
-
excelExport
package com.hahashujia.basic.excel.annotation; import com.hahashujia.basic.excel.enums.DecimalType; import com.hahashujia.basic.excel.enums.TimeType; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Excel 導入導出註解 * @author hahashujia */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelExport { /** 定義字段排序 */ int order(); /** * 表頭中文 * * @return */ String titleName() default ""; /** * 列寬 * * @return */ int titleSize() default 20; /** * 是否允許空值 ,默認不允許 * * @return */ boolean empty() default false; /** * 是否允許空值 ,默認不允許 * * @return */ boolean template() default true; /** * 內部類 * * @return */ ExcelExport.CellType cellType() default @ExcelExport.CellType; /** * 內部類 * * @return */ ExcelExport.SheetType sheetType() default @ExcelExport.SheetType; /** * 設置格式 * * @return */ @interface CellType { TimeType timeType() default TimeType.TIMEF_FORMAT; DecimalType decimalType() default DecimalType.TWO; /** * true : 結合decimalType使用 * @return */ boolean isMoney() default false; } /** * 設置sheet * * @return */ @interface SheetType { /** * true : 啓用以下兩個配置 * @return */ boolean isSheet() default false; int order() default 0; String name() default ""; } }
-
excelImport
package com.hahashujia.basic.excel.annotation; import com.hahashujia.basic.excel.enums.DecimalType; import com.hahashujia.basic.excel.enums.TimeType; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Excel 導入註解 * @author hahashujia */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelImport { /** * 數據開始列 */ int column() default 0; /** * 表頭中文 * * @return */ String titleName() default ""; /** * 是否允許空值 ,默認不允許 * * @return */ boolean empty() default false; /** * 內部類 * * @return */ ExcelImport.CellType cellType() default @ExcelImport.CellType; /** * 設置格式 * * @return */ @interface CellType { TimeType timeType() default TimeType.TIMEF_FORMAT; DecimalType decimalType() default DecimalType.TWO; /** * true : 結合decimalType使用 * @return */ boolean isMoney() default false; } }
-
ExcelSheet
package com.hahashujia.basic.excel.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Excel 導入導出註解 * @author hahashujia */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelSheet { /** * true : 啓用以下配置 * @return */ boolean isSheet() default true; /** * sheet 位置 * @return */ int sheetNum(); /** * sheet名稱 * @return */ String sheetName(); /** * Title行 * @return */ int titleRow() default 0; /** * 數據開始行 * @return */ int row() default 1; /** * 數據開始列 */ int column() default 0; /** * 是否允許空值 ,默認不允許 * * @return */ boolean empty() default false; }
-
-
enums
-
DecimalType
package com.hahashujia.basic.excel.enums; /** * <p> * 小數點格式 * </p> * * @author hahashujia * @version V0.0.1 * @date 2019年09月20日 */ public enum DecimalType { /** * 零位 */ ZERO(0, "#"), /** * 一位 */ ONE(1, "#.0"), /** * 兩位 */ TWO(2, "#.00"), /** * 三位 */ THREE(3, "#.000"), /** * 四位 */ FOUR(4, "#.0000"), /** * 五位 */ FIVE(5, "#.00000"), /** * 六位 */ SIX(6, "#.000000"), /** * 七位 */ SEVEN(7, "#.0000000"), /** * 八位 */ EIGHT(8, "#.00000000"), /** * 九位 */ NAN(9, "#.000000000"), /** * 十位 */ TEN(10, "#.0000000000"); /** * 日期格式 */ private String decimal; private int scale; /** * 日期格式 * * @param scale * @param decimal */ DecimalType(int scale, String decimal) { this.scale = scale; this.decimal = decimal; } /** * 獲取日期格式 * * @return */ public String getDecimal() { return decimal; } /** * 獲取日期格式 * * @return */ public int getScale() { return scale; } }
-
ExcelType
package com.hahashujia.basic.excel.enums; /** * <p> * Excel 類型 * </p> * * @author hahashujia * @version V0.0.1 * @date 2019年09月12日 */ public enum ExcelType { /** * XLS */ XLS, /** * XLS_X */ XLS_X }
-
TimeType
package com.hahashujia.basic.excel.enums; /** * <p> * 日期格式 * </p> * * @author hahashujia * @version V0.0.1 * @date 2019年09月20日 */ public enum TimeType { /** * yyyy-MM-dd */ DATE_FORMAT("yyyy-MM-dd"), /** * yyyy-MM */ YEAR_S_MONTH("yyyy-MM"), /** * yyyyMM */ YEAR_MONTH("yyyyMM"), /** * yyyy-MM-dd HH:mm:ss */ TIMEF_FORMAT("yyyy-MM-dd HH:mm:ss"), /** * yyyy-MM-dd HH:mm:ss.SSS */ MSEL_FORMAT("yyyy-MM-dd HH:mm:ss.SSS"), /** * yyyy年MM月dd日 */ ZHCN_DATE_FORMAT("yyyy年MM月dd日"), /** * yyyy年MM月dd日HH時mm分ss秒 */ ZHCN_TIME_FORMAT("yyyy年MM月dd日HH時mm分ss秒"), /** * yyyy年MM月dd日HH時mm分ss秒SSS毫秒 */ ZHCN_MSEL_FORMAT("yyyy年MM月dd日HH時mm分ss秒SSS毫秒"), /** * yyyyMMdd */ DATE_STR_FORMAT("yyyyMMdd"), /** * yyyyMMddHHmmss */ TIME_STR_FORMAT("yyyyMMddHHmmss"), /** * yyyyMMddHHmmssSSS */ MSEL_STR_FORMAT("yyyyMMddHHmmssSSS"), /** * yyyy-MM-dd HH:mm */ MSEL_MIU_FORMAT("yyyy-MM-dd HH:mm"), /** * yyyyMMddHH */ MS_MIU_FORMAT("yyyyMMddHH"); /** * 日期格式 */ private String timeType; /** * 日期格式 * * @param timeType */ TimeType(String timeType) { this.timeType = timeType; } /** * 獲取日期格式 * * @return */ public String getTimeType() { return timeType; } }
-
-
exmple
-
ImpExpTemplateMultiple
package com.hahashujia.basic.excel.exmple; import com.hahashujia.basic.excel.annotation.ExcelSheet; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; /** * 導入導出測試 * @author hahashujia */ @Builder @Data @NoArgsConstructor @AllArgsConstructor public class ImpExpTemplateMultiple { @ExcelSheet(sheetNum = 1, sheetName = "列表1", titleRow = 1, row = 6, column = 1) public List<ImpExpTemplateSingle> singleList; @ExcelSheet(sheetNum = 2, sheetName = "列表2", titleRow = 1, row = 6, column = 1) public List<ImpExpTemplateSingle> templateSingleList; }
-
ImpExpTemplateSingle
package com.hahashujia.basic.excel.exmple; import com.hahashujia.basic.excel.annotation.ExcelExport; import com.hahashujia.basic.excel.annotation.ExcelImport; import com.hahashujia.basic.excel.enums.DecimalType; import com.hahashujia.basic.excel.enums.TimeType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigDecimal; import java.util.Date; /** * 導入導出測試 * @author hahashujia */ @Builder @Data @NoArgsConstructor @AllArgsConstructor public class ImpExpTemplateSingle { @ExcelImport(titleName = "主鍵ID", column = 1) @ExcelExport(titleName = "主鍵ID", order = 0) private Long id; @ExcelImport(titleName = "類型", column = 2) @ExcelExport(titleName = "類型", order = 1) private String type; @ExcelImport(titleName = "數字", column = 3, cellType = @ExcelImport.CellType(isMoney = true, decimalType = DecimalType.TWO)) @ExcelExport(titleName = "數字", order = 2, cellType = @ExcelExport.CellType(isMoney = true, decimalType = DecimalType.TWO)) private Double number; @ExcelImport(titleName = "金額", column = 4) @ExcelExport(titleName = "金額", order = 3) private BigDecimal amount; @ExcelImport(titleName = "日期", column = 5, cellType = @ExcelImport.CellType(timeType = TimeType.DATE_FORMAT)) @ExcelExport(titleName = "日期", order = 4, cellType = @ExcelExport.CellType(timeType = TimeType.DATE_FORMAT)) private Date date; @ExcelExport(titleName = "錯誤原因", order = 5, template = false) private String error; }
-
multiple-imp-data.xlsx
-
single-imp-data.xls
-
-
utils
-
ExcelUtil
package com.hahashujia.basic.excel.utils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.hahashujia.basic.excel.annotation.ExcelExport; import com.hahashujia.basic.excel.annotation.ExcelImport; import com.hahashujia.basic.excel.annotation.ExcelSheet; import com.hahashujia.basic.excel.enums.ExcelType; import com.hahashujia.common.enums.BasicCode; import com.hahashujia.common.exceptions.BasicException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.DataFormat; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.VerticalAlignment; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.beans.PropertyDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.net.URLEncoder; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * <p> * excel 工具類 * </p> * * @author hahashujia * @version V3 * @date 2019年11月13日 */ @Slf4j public class ExcelUtil { /** * xls 後綴 */ public static final String XLS = "xls"; /** * xlsx 後綴 */ public static final String XLS_X = "xlsx"; /** * 列不對等 */ public static final String ROW_NUM_ERROR = "導入模板異常!"; /** * 文件不存在 */ public static final String FILE_NOT_ERROR = "文件不存在!"; /** * 表頭錯誤 */ public static final String NAME_ERROR = "表頭錯誤!"; /** * 實體空異常 */ public static final String BEAN_ERROR = "實體空異常!"; /** * 科學計數 */ public static final String E = "e"; /** * 傳入文本對象輸出list集合(導入) * * @param file 流文件 * @param clazz 要轉義成的類對象 * @return */ public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz) { // 檢查文件 Workbook workbook = getWorkBook(file); List<T> list = new ArrayList<T>(); checkFile(file); // 獲得HSSFWorkbook工作薄對象 //獲取對象總數量並按註解排序 Field[] fields = getSortFieldsImport(clazz); if (workbook != null) { for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) { // 獲得當前sheet工作表 Sheet sheet = workbook.getSheetAt(sheetNum); if (sheet == null || sheet.getLastRowNum() == 0) { continue; } // 獲取當前sheet工作表的列總數 int firstLine = sheet.getRow(0).getPhysicalNumberOfCells(); if (fields.length != firstLine) { throw new BasicException(BasicCode.FAIL.code, ROW_NUM_ERROR); } // 獲得當前sheet的開始行 int firstRowNum = sheet.getFirstRowNum(); // 獲得當前sheet的結束行 int lastRowNum = sheet.getLastRowNum(); // 循環所有行 for (int rowNum = firstRowNum; rowNum <= lastRowNum; rowNum++) { // 獲得當前行 Row row = sheet.getRow(rowNum); if (row == null) { continue; } Object obj = getNewInstance(clazz); for (int cellNum = 0; cellNum < firstLine; cellNum++) { // 取出對應註解 ExcelImport excelImport = fields[cellNum].getAnnotation(ExcelImport.class); Cell cell = row.getCell(cellNum); if (rowNum == 0) { // 第一行 判斷表頭名稱 if (cell == null || StringUtils.isEmpty(cell.getStringCellValue()) || !cell.getStringCellValue().equals(excelImport.titleName())) { throw new BasicException(BasicCode.FAIL.code, NAME_ERROR); } continue; } Object value = getCellValue(cell); // 判斷註解是否允許空值 if (!excelImport.empty()) { if (value == null || "".equals(value)) { log.error("【excel導入】{} 列不能爲空!", excelImport.titleName()); throw new BasicException(BasicCode.FAIL.code, excelImport.titleName() + "列不能爲空"); } } // 根絕類型 實體類賦值 createBean(fields[cellNum], obj, value); } if (rowNum == 0) { // 表頭不做記錄 continue; } list.add((T) obj); } } } return list; } /** * 傳入文本對象輸出Class(導入) * * @param file 流文件 * @param clazz 要轉義成的類對象 * @return */ public static <T> T importMultiple(MultipartFile file, Class<T> clazz) { // 檢查文件 checkFile(file); // 獲得HSSFWorkbook工作薄對象 Workbook workbook = getWorkBook(file); //獲取對象總數量 Field[] fields = getSortFieldsByExcelSheet(clazz); Object obj = getNewInstance(clazz); if (workbook != null) { ExcelSheet excel = null; Field field = null; int order = 0; Sheet sheet = null; for (int fieldNum = 0; fieldNum < fields.length; fieldNum++) { field = fields[fieldNum]; excel = field.getAnnotation(ExcelSheet.class); order = excel.sheetNum(); sheet = workbook.getSheetAt(order); Type genericType = field.getGenericType(); ParameterizedType pt = (ParameterizedType) genericType; //得到泛型裏的class類型對象 Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0]; createBean(field, obj, importMultiple(sheet, genericClazz, excel)); } } return (T) obj; } private static <T> List<T> importMultiple(Sheet sheet, Class<T> clazz, ExcelSheet excelSheet) { int titleRow = excelSheet.titleRow(); Field[] fields = getSortFieldsImport(clazz); List<T> list = new ArrayList<T>(); // 獲取當前sheet工作表的列總數 int firstLine = sheet.getRow(titleRow).getPhysicalNumberOfCells(); if (fields.length != firstLine) { throw new BasicException(BasicCode.FAIL.code, ROW_NUM_ERROR); } // 獲得當前sheet的開始行 int firstRowNum = excelSheet.row(); // 獲得當前sheet的結束行 int lastRowNum = sheet.getLastRowNum(); // 驗證表頭名稱 Row row = null; // 循環所有行 for (int rowNum = titleRow; rowNum <= lastRowNum; rowNum++) { // 獲得當前行 row = sheet.getRow(rowNum); if (row == null) { continue; } Object obj = getNewInstance(clazz); for (int fieldNum = 0; fieldNum < fields.length; fieldNum++) { ExcelImport excel = fields[fieldNum].getAnnotation(ExcelImport.class); Cell cell = row.getCell(excel.column()); if (rowNum == titleRow) { // 第一行 判斷表頭名稱 if (cell == null || StringUtils.isEmpty(cell.getStringCellValue()) || !cell.getStringCellValue().equals(excel.titleName())) { log.info("【excel導入】-{}-{}", excelSheet.sheetName(), excel.titleName() + NAME_ERROR); throw new BasicException(BasicCode.FAIL.code, "【" + excelSheet.sheetName() + "】【" + excel.titleName() + "】" + NAME_ERROR); } continue; } Object value = getCellValue(cell); // 判斷註解是否允許空值 if (!excel.empty()) { if (value == null || "".equals(value)) { log.error("【excel導入】-{}-{}列不能爲空!", excelSheet.sheetName(), excel.titleName()); throw new BasicException(BasicCode.FAIL.code, "【" + excelSheet.sheetName() + "】【" + excel.titleName() + "】" + "列不能爲空!"); } } // 根絕類型 實體類賦值 createBean(fields[fieldNum], obj, value); } if (rowNum == titleRow) { // 表頭不做記錄 rowNum = firstRowNum - 1; continue; } list.add((T) obj); } if (list.size() == 0 && !excelSheet.empty()) { log.error("【excel導入】{} 頁籤不能爲空!", excelSheet.sheetName()); throw new BasicException(BasicCode.FAIL.code, excelSheet.sheetName() + "頁籤不能爲空!"); } return list; } /** * 導出模版 * * @param excelName excel 名稱 * @param clazz 數據集 * @param response 使用response可以導出到瀏覽器 * @param <T> * @return */ public static <T> Boolean exportTemplate(String excelName, Class<T> clazz, HttpServletResponse response) { return export(excelName, null, clazz, ExcelType.XLS, response, false); } /** * 導出模版 * * @param excelName excel 名稱 * @param clazz 數據集 * @param type excel 類型 * @param response 使用response可以導出到瀏覽器 * @param <T> * @return */ public static <T> Boolean exportTemplate(String excelName, Class<T> clazz, ExcelType type, HttpServletResponse response) { return export(excelName, null, clazz, type, response, false); } /** * excel 導出 (對象) * * @param excelName excel 名稱 * @param list 數據集 * @param clazz 反射clazz * @param response 使用response可以導出到瀏覽器 * @param <T> * @return */ public static <T> Boolean exportExcel(String excelName, List<T> list, Class<T> clazz, HttpServletResponse response) { return export(excelName, list, clazz, ExcelType.XLS, response, true); } /** * excel 導出 (對象) * * @param excelName excel 名稱 * @param list 數據集 * @param clazz 反射clazz * @param type excel 類型 * @param response 使用response可以導出到瀏覽器 * @param <T> * @return */ public static <T> Boolean exportExcel(String excelName, List<T> list, Class<T> clazz, ExcelType type, HttpServletResponse response) { return export(excelName, list, clazz, type, response, true); } /** * excel 導出 (Map) * * @param excelName excel 名稱 * @param clazz 反射clazz * @param list 數據集 * @param response 使用response可以導出到瀏覽器 * @param <T> * @return */ public static <T> Boolean exportExcel(String excelName, Class<T> clazz, List<Map<String, Object>> list, HttpServletResponse response) { return exportExcel(excelName, clazz, list, ExcelType.XLS, response, true); } /** * excel 導出 (Map) * * @param excelName excel 名稱 * @param clazz * @param list 數據集 * @param type excel 類型 * @param response 使用response可以導出到瀏覽器 * @param <T> * @return */ public static <T> Boolean exportExcel(String excelName, Class<T> clazz, List<Map<String, Object>> list, ExcelType type, HttpServletResponse response) { return exportExcel(excelName, clazz, list, type, response, false); } /** * excel 導出 (Map) * * @param excelName excel 名稱 * @param clazz * @param list 數據集 * @param type excel 類型 * @param response 使用response可以導出到瀏覽器 * @param flag true:數據導出 false:模版導出 * @param <T> * @return */ private static <T> Boolean exportExcel(String excelName, Class<T> clazz, List<Map<String, Object>> list, ExcelType type, HttpServletResponse response, boolean flag) { if (list == null || list.size() == 0) { log.error("【excel導出】{}", "excel導出數據空異常!"); return false; } List<T> ts = JSONArray.parseArray(JSON.toJSONString(list), clazz); return export(excelName, ts, clazz, type, response, flag); } /** * 模板導出多種數據 * * @param excelName * @param clazz * @param response * @param <T> * @return */ public static <T> Boolean exportMultipleTemplate(String excelName, Class<T> clazz, HttpServletResponse response) { return exportMultiple(excelName, null, clazz, ExcelType.XLS, response, false); } /** * 導出多種數據 * * @param excelName * @param model * @param clazz * @param response * @param <T> * @return */ public static <T> Boolean exportMultiple(String excelName, T model, Class<T> clazz, HttpServletResponse response) { return exportMultiple(excelName, model, clazz, ExcelType.XLS, response, true); } /** * 導出多種數據 * * @param excelName * @param model * @param clazz * @param type * @param response * @param <T> * @return */ public static <T> Boolean exportMultiple(String excelName, T model, Class<T> clazz, ExcelType type, HttpServletResponse response) { return exportMultiple(excelName, model, clazz, type, response, true); } /** * 導出多種數據 * * @param excelName * @param model * @param clazz * @param type * @param response * @param flag * @param <T> * @return */ public static <T> Boolean exportMultiple(String excelName, T model, Class<T> clazz, ExcelType type, HttpServletResponse response, boolean flag) { // 設置默認文件名爲當前時間:年月日時分秒 if (StringUtils.isEmpty(excelName)) { log.info("【excel導出】{}", "excel導出未設置文件名,默認使用時間戳代替!"); excelName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); } createResponse(excelName, response, type); //獲取對象總數量 Field[] fields = getSortFieldsByExcelSheet(clazz); Workbook workbook = getWorkbook(type); CellStyle titleCellStyle = getTitleCellStyle(workbook); for (int fieldNum = 0; fieldNum < fields.length; fieldNum++) { Field field = fields[fieldNum]; Type genericType = field.getGenericType(); List<?> list = null; if (flag) { try { list = (List<?>) field.get(model); } catch (IllegalAccessException e) { e.printStackTrace(); throw new BasicException(BasicCode.FAIL.code, "excel導出異常"); } } ParameterizedType pt = (ParameterizedType) genericType; //得到泛型裏的class類型對象 Class<?> sheetClazz = (Class<?>) pt.getActualTypeArguments()[0]; String sheetName = field.getAnnotation(ExcelSheet.class).sheetName(); createSheet(workbook, titleCellStyle, sheetName, list, sheetClazz, flag); } //將文件輸出 OutputStream outputStream = null; try { outputStream = response.getOutputStream(); workbook.write(outputStream); outputStream.flush(); outputStream.close(); return true; } catch (IOException e) { log.error("excel導出異常!{}", e); throw new BasicException(BasicCode.FAIL.code, "excel導出異常"); } } /** * excel 導出 (對象) * * @param excelName excel 名稱 * @param list 數據集 * @param clazz 反射clazz * @param type excel 類型 * @param response 使用response可以導出到瀏覽器 * @param flag true:數據導出 false:模版導出 * @param <T> * @return */ private static <T> Boolean export(String excelName, List<T> list, Class<T> clazz, ExcelType type, HttpServletResponse response, boolean flag) { if (flag) { // 非模版導出,判斷數據是否爲空! if (list == null || list.size() == 0) { log.error("【excel導出】{}", "excel導出數據空異常!"); return false; } } // 設置默認文件名爲當前時間:年月日時分秒 if (StringUtils.isEmpty(excelName)) { log.info("【excel導出】{}", "excel導出未設置文件名,默認使用時間戳代替!"); excelName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); } createResponse(excelName, response, type); Workbook workbook = getWorkbook(type); CellStyle titleCellStyle = getTitleCellStyle(workbook); createSheet(workbook, titleCellStyle, excelName, list, clazz, flag); try { //將文件輸出 OutputStream outputStream = response.getOutputStream(); workbook.write(outputStream); outputStream.flush(); outputStream.close(); return true; } catch (IOException e) { e.printStackTrace(); throw new BasicException(BasicCode.FAIL.code, "excel導出異常"); } } /** * 創建excel工作簿 * * @param workbook 工作簿 * @param sheetName sheet 名稱 * @param list 數據集 * @param sheetClazz 反射clazz * @param flag true:數據導出 false:模版導出 */ private static void createSheet(Workbook workbook, CellStyle titleCellStyle, String sheetName, List<?> list, Class<?> sheetClazz, boolean flag) { Field[] fields = getSortFieldsExcelExport(sheetClazz); // 創建一個工作表sheet 默認是表名是sheet0 Sheet sheet = workbook.createSheet(sheetName); setWorkBook(workbook, titleCellStyle, sheet, fields, flag); // CellStyle 緩存 Map<String, CellStyle> hashMap = new HashMap<>(8); try { if (flag) { // 開始生成excel for (int rowIndex = 1; rowIndex <= list.size(); rowIndex++) { Object obj = list.get(rowIndex - 1); Field[] sortFields = getSortFieldsExcelExport(obj.getClass()); //創建第 rowIndex 行) Row row = sheet.createRow(rowIndex); for (int i = 0; i < sortFields.length; i++) { Field field = sortFields[i]; if (!field.isAccessible()) { field.setAccessible(true); } Object object = new PropertyDescriptor(field.getName(), sheetClazz).getReadMethod().invoke(obj); if (!field.getAnnotation(ExcelExport.class).empty() && object == null) { log.error("【excel導出】class映射地址:{},空指針參數:{},{}", sheetClazz.getCanonicalName(), field.getName(), "數據集空指針"); throw new BasicException(BasicCode.FAIL.code, "【excel導出】class映射地址:" + sheetClazz.getCanonicalName() + ",空指針參數:" + field.getName() + ",數據集空指針"); } setValue(getCell(workbook, hashMap, row, i, object, field), object, field); } } } } catch (Exception e) { log.error("【excel導出】sheetName:{}, sheetClazz:{}, e:{}", sheetName, sheetClazz, e); e.printStackTrace(); throw new BasicException(BasicCode.FAIL.code, "excel導出異常"); } } // ---------------------------------------------- excel 工具 --------------------------------------------- /** * 設置表格內容的值 * * @param cell 單元格對象 * @param value 單元格的值 */ private static void setValue(Cell cell, Object value, Field field) { if (value == null || "".equals(value)) { return; } else if (value instanceof String) { cell.setCellValue(value.toString()); } else if (value instanceof Integer || value instanceof Double || value instanceof Float || value instanceof Long || value instanceof Short || value instanceof BigDecimal) { if (field.getAnnotation(ExcelExport.class).cellType().isMoney()) { // 判斷類型 BigDecimal bi1 = new BigDecimal(value.toString()); int scale = field.getAnnotation(ExcelExport.class).cellType().decimalType().getScale(); cell.setCellValue(bi1.setScale(scale, BigDecimal.ROUND_HALF_UP).toString()); } else { cell.setCellValue(value.toString()); } } else if (value instanceof Date) { SimpleDateFormat sdf = new SimpleDateFormat( field.getAnnotation(ExcelExport.class).cellType().timeType().getTimeType()); cell.setCellValue(sdf.format((Date) value)); } } /** * 設置excel單元格樣式 * * @param workbook * @param hashMap * @param row * @param num * @param value * @param field * @return */ private static Cell getCell(Workbook workbook, Map<String, CellStyle> hashMap, Row row, int num, Object value, Field field) { CellStyle cellStyle; // 獲取指定單元格 Cell cell = row.createCell(num); // 設置類型 DataFormat format = workbook.createDataFormat(); if (value instanceof Integer || value instanceof Double || value instanceof Float || value instanceof Long || value instanceof Short || value instanceof BigDecimal) { if (field.getAnnotation(ExcelExport.class).cellType().isMoney()) { cellStyle = hashMap.get( field.getAnnotation(ExcelExport.class).cellType().decimalType().getDecimal()); if (cellStyle == null) { cellStyle = getCellStyle(workbook); cellStyle.setDataFormat(format.getFormat( field.getAnnotation(ExcelExport.class).cellType().decimalType().getDecimal())); hashMap.put(field.getAnnotation(ExcelExport.class).cellType().decimalType().getDecimal(), cellStyle); } } else { cellStyle = hashMap.get("@"); if (cellStyle == null) { cellStyle = getCellStyle(workbook); cellStyle.setDataFormat(format.getFormat("@")); hashMap.put("@", cellStyle); } } } else if (value instanceof Date) { cellStyle = hashMap.get(field.getAnnotation(ExcelExport.class).cellType().timeType().getTimeType()); if (cellStyle == null) { cellStyle = getCellStyle(workbook); cellStyle.setDataFormat(format.getFormat( field.getAnnotation(ExcelExport.class).cellType().timeType().getTimeType())); hashMap.put(field.getAnnotation(ExcelExport.class).cellType().timeType().getTimeType(), cellStyle); } } else { cellStyle = hashMap.get("@"); if (cellStyle == null) { cellStyle = getCellStyle(workbook); cellStyle.setDataFormat(format.getFormat("@")); hashMap.put("@", cellStyle); } } cell.setCellStyle(cellStyle); return cell; } /** * 設置excel 樣式 (第一行格式) * * @param workbook * @param cellStyle * @param sheet * @param fields * @param flag true:數據導出 false:模版導出 */ private static void setWorkBook(Workbook workbook, CellStyle cellStyle, Sheet sheet, Field[] fields, boolean flag) { //寫入excel的表頭(創建第一行) Row row = sheet.createRow(0); // 設置類型 DataFormat format = workbook.createDataFormat(); // 設置列寬、表頭、數據類型 for (int i = 0; i < fields.length; i++) { if (!fields[i].getAnnotation(ExcelExport.class).template() && !flag) { continue; } //設置寬度 sheet.setColumnWidth(i, fields[i].getAnnotation(ExcelExport.class).titleSize() * 256); //創建第一行 Cell cell = row.createCell(i); //設置表頭名稱 cell.setCellValue(fields[i].getAnnotation(ExcelExport.class).titleName()); cell.setCellStyle(cellStyle); cellStyle.setDataFormat(format.getFormat("@")); //sheet.setDefaultColumnStyle(i, cellStyle); } } /** * 初始化樣式屬性 * * @param workbook * @return */ private static CellStyle getTitleCellStyle(Workbook workbook) { CellStyle cellStyle = workbook.createCellStyle(); // 設置對齊方式爲居中對齊 cellStyle.setAlignment(HorizontalAlignment.CENTER); // 設置自動換行 cellStyle.setWrapText(true); // 設置單元格內容垂直對其方式爲居中 cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 設置字體 Font font = workbook.createFont(); font.setFontName("宋體"); font.setFontHeightInPoints((short) 14); font.setBold(true); cellStyle.setFont(font); cellStyle.setBorderTop(BorderStyle.THIN); cellStyle.setBorderLeft(BorderStyle.THIN); cellStyle.setBorderRight(BorderStyle.THIN); cellStyle.setBorderBottom(BorderStyle.THIN); cellStyle.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.getIndex()); cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); return cellStyle; } /** * 初始化樣式屬性 * * @param workbook * @return */ private static CellStyle getCellStyle(Workbook workbook) { CellStyle cellStyle = workbook.createCellStyle(); // 設置對齊方式爲居中對齊 cellStyle.setAlignment(HorizontalAlignment.CENTER); // 設置自動換行 cellStyle.setWrapText(true); // 設置單元格內容垂直對其方式爲居中 cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 設置字體 Font font = workbook.createFont(); font.setFontName("宋體"); cellStyle.setFont(font); cellStyle.setBorderLeft(BorderStyle.THIN); cellStyle.setBorderRight(BorderStyle.THIN); cellStyle.setBorderBottom(BorderStyle.THIN); return cellStyle; } /** * 創建excel 導出 response信息 * * @param excelName * @param response */ private static void createResponse(String excelName, HttpServletResponse response, ExcelType type) { // 設置response頭信息 // response.reset(); // 改成輸出excel文件 response.setContentType("application/vnd.ms-excel"); response.setHeader("Access-Control-Expose-Headers", "Content-disposition"); try { switch (type) { case XLS: response.setHeader("Content-disposition", "attachment; filename=" + new String(URLEncoder.encode(excelName, "UTF-8").getBytes("UTF-8"), "ISO8859-1") + ".xls"); break; case XLS_X: response.setHeader("Content-disposition", "attachment; filename=" + new String(URLEncoder.encode(excelName, "UTF-8").getBytes("UTF-8"), "ISO8859-1") + ".xlsx"); break; default: log.error("【excel導出】{}", "excel類型錯誤,只支持xls與xlsx!"); throw new BasicException(BasicCode.FAIL.code, "【excel導出】excel類型錯誤,只支持xls與xlsx!"); } } catch (UnsupportedEncodingException e) { log.error("【excel導出】{}", "設置response信息異常!"); throw new BasicException(BasicCode.FAIL.code, "【excel導出】設置response信息異常!"); } } /** * 根據實體類型 賦值數據 * * @param field * @param newInstance * @param value * @param <T> */ private static <T> void createBean(Field field, T newInstance, Object value) { if (!field.isAccessible()) { field.setAccessible(true); } try { if (value == null) { field.set(newInstance, null); } else if (Long.class.equals(field.getType())) { field.set(newInstance, Long.valueOf(String.valueOf(value))); } else if (String.class.equals(field.getType())) { field.set(newInstance, String.valueOf(value)); } else if (Integer.class.equals(field.getType())) { field.set(newInstance, Integer.valueOf(String.valueOf(value))); } else if (Date.class.equals(field.getType())) { SimpleDateFormat sdf = new SimpleDateFormat( field.getAnnotation(ExcelImport.class).cellType().timeType().getTimeType()); if (value instanceof Date) { field.set(newInstance, sdf.parse(sdf.format(value))); } else { field.set(newInstance, sdf.parse(value.toString())); } } else if (Boolean.class.equals(field.getType())) { field.set(newInstance, (Boolean) value); } else if (Double.class.equals(field.getType())) { field.set(newInstance, Double.valueOf(String.valueOf(value))); } else if (Float.class.equals(field.getType())) { field.set(newInstance, Float.valueOf(String.valueOf(value))); } else if (BigDecimal.class.equals(field.getType())) { field.set(newInstance, new BigDecimal(String.valueOf(value))); } else { field.set(newInstance, value); } } catch (Exception e) { log.error("【excel導入】excel實體轉換異常!字段【{}】,值({}), {}, {}", field.getAnnotation(ExcelImport.class).titleName(), value, newInstance, e); throw new BasicException(BasicCode.FAIL.code, "【excel導入】excel實體轉換異常! 字段【" + field.getAnnotation(ExcelImport.class).titleName() + "】,值(" + value + ")"); } } /** * 實體判空,註解判空 * * @param clazz * @return */ private static Field[] getSortFieldsImport(Class clazz) { //獲取對象總數量 Field[] fields = clazz.getDeclaredFields(); if (fields == null || fields.length == 0) { log.error("【excel導入】clazz映射地址:{},{}", clazz.getCanonicalName(), "實體空異常!"); throw new BasicException(BasicCode.FAIL.code, BEAN_ERROR); } List<Field> list = new ArrayList<>(); for (Field field : fields) { if (field.isAnnotationPresent(ExcelImport.class)) { list.add(field); } } fields = new Field[list.size()]; int num = 0; for (Field field : list) { fields[num++] = field; } Arrays.sort(fields, (field, fieldAfter) -> { return field.getAnnotation(ExcelImport.class).column() - fieldAfter.getAnnotation(ExcelImport.class).column(); }); return fields; } /** * 實體判空,註解判空 * * @param clazz * @return */ private static Field[] getSortFieldsExcelExport(Class clazz) { //獲取對象總數量 Field[] fields = clazz.getDeclaredFields(); if (fields == null || fields.length == 0) { log.error("【excel導入】clazz映射地址:{},{}", clazz.getCanonicalName(), "實體空異常!"); throw new BasicException(BasicCode.FAIL.code, BEAN_ERROR); } List<Field> list = new ArrayList<>(); for (Field field : fields) { if (field.isAnnotationPresent(ExcelExport.class)) { list.add(field); } } fields = new Field[list.size()]; int num = 0; for (Field field : list) { fields[num++] = field; } Arrays.sort(fields, (field, fieldAfter) -> { return field.getAnnotation(ExcelExport.class).order() - fieldAfter.getAnnotation(ExcelExport.class).order(); }); return fields; } /** * 實體判空,註解判空 * * @param clazz * @return */ private static Field[] getSortFieldsByExcelSheet(Class clazz) { //獲取對象總數量 Field[] fields = clazz.getDeclaredFields(); if (fields == null || fields.length == 0) { log.error("【excel導入】clazz映射地址:{},{}", clazz.getCanonicalName(), "實體空異常!"); throw new BasicException(BasicCode.FAIL.code, BEAN_ERROR); } List<Field> list = new ArrayList<>(); for (Field field : fields) { if (field.isAnnotationPresent(ExcelSheet.class)) { list.add(field); } } fields = new Field[list.size()]; int num = 0; for (Field field : list) { fields[num++] = field; } Arrays.sort(fields, (field, fieldAfter) -> { return field.getAnnotation(ExcelSheet.class).sheetNum() - fieldAfter.getAnnotation(ExcelSheet.class).sheetNum(); }); return fields; } /** * 列轉化值 * * @param cell 列值 * @throws IOException */ private static Object getCellValue(Cell cell) { if (cell == null) { return null; } Object cellValue; // 把數字當成String來讀,避免出現1讀成1.0的情況 // 判斷數據的類型 switch (cell.getCellTypeEnum()) { case NUMERIC: if (DateUtil.isValidExcelDate(cell.getNumericCellValue())) { CellStyle style = cell.getCellStyle(); if (style == null) { return false; } int formatIndex = style.getDataFormat(); String formatString = style.getDataFormatString(); boolean isDate = DateUtil.isADateFormat(formatIndex, formatString); if (isDate) { Date date = cell.getDateCellValue(); return date; } } if ((long) cell.getNumericCellValue() != cell.getNumericCellValue()) { // double 類型 cellValue = new BigDecimal(String.valueOf(cell.getNumericCellValue())); } else { cellValue = (long)cell.getNumericCellValue(); } break; // 字符串 case STRING: cellValue = String.valueOf(cell.getStringCellValue()); break; // Boolean case BOOLEAN: cellValue = String.valueOf(cell.getBooleanCellValue()); break; // 公式 case FORMULA: cellValue = String.valueOf(cell.getCellFormula()); break; // 空值 case BLANK: cellValue = null; break; // 故障 case ERROR: cellValue = "非法字符"; break; default: cellValue = "未知類型"; break; } return cellValue; } /** * 創建工作簿 * * @param type * @return */ private static Workbook getWorkbook(ExcelType type) { Workbook workbook = null; switch (type) { case XLS: workbook = new HSSFWorkbook(); break; case XLS_X: workbook = new XSSFWorkbook(); break; default: log.error("【excel導出】{}", "excel類型錯誤,只支持xls與xlsx!"); throw new BasicException(BasicCode.FAIL.code, "【excel導出】excel類型錯誤,只支持xls與xlsx!"); } return workbook; } /** * 由文件生成 poi Workbook * * @param file * @return */ private static Workbook getWorkBook(MultipartFile file) { // 獲得文件名 String fileName = file.getOriginalFilename(); // 創建Workbook工作薄對象,表示整個excel Workbook workbook = null; // 獲取excel文件的io流 InputStream is = null; try { is = file.getInputStream(); // 根據文件後綴名不同(xls和xlsx)獲得不同的Workbook實現類對象 if (fileName.endsWith(XLS)) { // 2003 workbook = new HSSFWorkbook(is); } else if (fileName.endsWith(XLS_X)) { // 2007 workbook = new XSSFWorkbook(is); } } catch (IOException e) { throw new BasicException(BasicCode.FAIL.code, "excel 轉換 HSSFWorkbook 異常!"); } return workbook; } /** * 檢查文件 * * @param file * @throws IOException */ private static void checkFile(MultipartFile file) { // 判斷文件是否存在 if (null == file) { throw new BasicException(BasicCode.FAIL.code, FILE_NOT_ERROR); } // 獲得文件名 String fileName = file.getOriginalFilename(); // 判斷文件是否是excel文件 if (!fileName.endsWith(XLS) && !fileName.endsWith(XLS_X)) { log.error(fileName + "不是excel文件"); throw new BasicException(BasicCode.FAIL.code, fileName + "不是excel文件"); } } /** * 初始化實體 * * @param clazz * @param <T> * @return */ private static <T> Object getNewInstance(Class<T> clazz) { try { return clazz.newInstance(); } catch (IllegalAccessException e) { log.error("【excel導入】clazz映射地址:{},{}", clazz.getCanonicalName(), "excel導入異常!"); throw new BasicException(BasicCode.FAIL.code, "excel導入異常"); } catch (InstantiationException e) { log.error("【excel導入】clazz映射地址:{},{}", clazz.getCanonicalName(), "excel導入異常!"); throw new BasicException(BasicCode.FAIL.code, "excel導入異常"); } } }
-
-
下面就是調用了:
-
ImpExpTemplateController
package com.hahashujia.controller; import com.hahashujia.basic.excel.exmple.ImpExpTemplateMultiple; import com.hahashujia.basic.excel.utils.ExcelUtil; import com.hahashujia.basic.excel.enums.ExcelType; import com.hahashujia.basic.excel.exmple.ImpExpTemplateSingle; import com.hahashujia.common.response.ResultModel; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @author hahashujia * @describe * @createTime 2019-11-01 */ @RestController @RequestMapping("/imp-exp-template/") @Api(value = "導入導出模板", description = "導入導出模板") public class ImpExpTemplateController { @ApiOperation(value = "導入模板下載(單一數據)", notes = "導入模板下載(單一數據)") @GetMapping(value = "single/exp/template") public void singleExpTemplate(HttpServletResponse response) { String fileName = "導入模板下載(單一數據)"; ExcelUtil.exportTemplate(fileName, ImpExpTemplateSingle.class, ExcelType.XLS, response); } @ApiOperation(value = "數據導出(單一數據)", notes = "數據導出(單一數據)") @GetMapping(value = "single/exp/data") public void singleExpData(HttpServletResponse response) { List<ImpExpTemplateSingle> list = new ArrayList<>(); list.add(getImpExpTemplateModel(1L)); list.add(getImpExpTemplateModel(2L)); list.add(getImpExpTemplateModel(3L)); list.add(getImpExpTemplateModel(4L)); list.add(getImpExpTemplateModel(5L)); list.add(getImpExpTemplateModel(6L)); list.add(getImpExpTemplateModel(7L)); list.add(getImpExpTemplateModel(8L)); list.add(getImpExpTemplateModel(9L)); list.add(getImpExpTemplateModel(10L)); String fileName = "數據導出-" + LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")); ExcelUtil.exportExcel(fileName, list, ImpExpTemplateSingle.class, ExcelType.XLS, response); } @ApiOperation(value = "數據導入(單一數據)", notes = "數據導入(單一數據)") @PostMapping(value = "single/imp/data") public ResponseEntity<ResultModel> singleImpData(MultipartFile file) { List<ImpExpTemplateSingle> list = ExcelUtil.importExcel(file, ImpExpTemplateSingle.class); System.err.println(list.toString()); return ResponseEntity.ok(ResultModel.ok(null)); } @ApiOperation(value = "導入模板下載(多種數據)", notes = "導入模板下載(多種數據)") @GetMapping(value = "multiple/exp/template") public void multipleExpTemplate(HttpServletResponse response) { String fileName = "導入模板下載(單一數據)"; ExcelUtil.exportMultipleTemplate(fileName, ImpExpTemplateMultiple.class, response); } @ApiOperation(value = "數據導入(多種數據)", notes = "數據導入(多種數據)") @PostMapping(value = "multiple/imp/data") public ResponseEntity<ResultModel> multipleImpData(MultipartFile file) { ImpExpTemplateMultiple multiple = ExcelUtil.importMultiple(file, ImpExpTemplateMultiple.class); System.err.println(multiple.toString()); return ResponseEntity.ok(ResultModel.ok(null)); } @ApiOperation(value = "數據導出(多種數據)", notes = "數據導出(多種數據)") @GetMapping(value = "multiple/exp/data") public void multipleExpData(HttpServletResponse response) { List<ImpExpTemplateSingle> list = new ArrayList<>(); list.add(getImpExpTemplateModel(1L)); list.add(getImpExpTemplateModel(2L)); list.add(getImpExpTemplateModel(3L)); list.add(getImpExpTemplateModel(4L)); list.add(getImpExpTemplateModel(5L)); list.add(getImpExpTemplateModel(6L)); list.add(getImpExpTemplateModel(7L)); list.add(getImpExpTemplateModel(8L)); list.add(getImpExpTemplateModel(9L)); list.add(getImpExpTemplateModel(10L)); ImpExpTemplateMultiple multiple = ImpExpTemplateMultiple.builder() .singleList(list) .templateSingleList(list) .build(); String fileName = "數據導出-" + LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")); ExcelUtil.exportMultiple(fileName, multiple, ImpExpTemplateMultiple.class, response); } private ImpExpTemplateSingle getImpExpTemplateModel(Long id) { ImpExpTemplateSingle model = ImpExpTemplateSingle.builder() .id(id) .type("字符串") .number(Math.random() * 10) .amount(new BigDecimal(200.5)) .date(new Date()) .error("錯誤信息") .build(); System.err.println(model.toString()); return model; } }
-