Java基于注解的Excel導出方式
Java注解的Excel導出
依賴:
?<dependency> ? ? ? ? ? ? <groupId>cn.afterturn</groupId> ? ? ? ? ? ? <artifactId>easypoi-base</artifactId> ? ? ? ? ? ? <version>4.1.2</version> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>cn.afterturn</groupId> ? ? ? ? ? ? <artifactId>easypoi-web</artifactId> ? ? ? ? ? ? <version>4.1.2</version> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>cn.afterturn</groupId> ? ? ? ? ? ? <artifactId>easypoi-annotation</artifactId> ? ? ? ? ? ? <version>4.1.2</version> ? ? ? ? </dependency>
注解配合工具類做了個小工具如下:
第一步:自定義注解:(讀者請直接復制)
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; ? /** ?* 自定義導出Excel數(shù)據(jù)注解 ?* ?* @author sunziwen ?* @version 1.0 ?* @date 2018-12-29 15:00 ?**/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Excel { ? ? /** ? ? ?* 導出到Excel中的名字. ? ? ?*/ ? ? public String name(); ? ? ? /** ? ? ?* 日期格式, 如: yyyy-MM-dd ? ? ?*/ ? ? public String dateFormat() default ""; ? ? ? /** ? ? ?* 讀取內(nèi)容轉表達式 (如: 0=男,1=女,2=未知) ? ? ?*/ ? ? public String readConverterExp() default ""; ? ? ? /** ? ? ?* 導出時在excel中每個列的高度 單位為字符 ? ? ?*/ ? ? public double height() default 14; ? ? ? /** ? ? ?* 導出時在excel中每個列的寬 單位為字符 ? ? ?*/ ? ? public double width() default 20; ? ? ? /** ? ? ?* 文字后綴,如% 90 變成90% ? ? ?*/ ? ? public String suffix() default ""; ? ? ? /** ? ? ?* 當值為空時,字段的默認值 ? ? ?*/ ? ? public String defaultValue() default ""; ? ? ? /** ? ? ?* 提示信息 ? ? ?*/ ? ? public String prompt() default ""; ? ? ? /** ? ? ?* 設置只能選擇不能輸入的列內(nèi)容. ? ? ?*/ ? ? public String[] combo() default {}; ? ? ? /** ? ? ?* 是否導出數(shù)據(jù),應對需求:有時我們需要導出一份模板,這是標題需要但內(nèi)容需要用戶手工填寫. ? ? ?*/ ? ? public boolean isExport() default true; }
第二步:實體類:(為每個需要的字段打上@Excel注解)
import java.time.LocalDateTime; ? import com.ciih.authcenter.client.util.excel.Excel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; ? /** ?* (User)表實體類 ?* ?* @author suzniwen ?* @since 2021-04-13 16:11:55 ?*/ @SuppressWarnings("serial") @EqualsAndHashCode(callSuper = true) @Data public class User { ? ? @Excel(name = "編號") ? ? @ApiModelProperty(value = "主鍵") ? ? private String id; ? ? @Excel(name = "賬號") ? ? @ApiModelProperty(value = "賬號") ? ? private String loginName; ? ? @Excel(name = "用戶名") ? ? @ApiModelProperty(value = "用戶名") ? ? private String userName; ? ? @ApiModelProperty(value = "用戶名拼音") ? ? private String namePinyin; ? ? @Excel(name = "性別", readConverterExp = "1=男,0=女") ? ? @ApiModelProperty(value = "性別") ? ? private String gender; ? ? @Excel(name = "證件類型",readConverterExp="1=居民身份證,2=香港居民來往內(nèi)地通行證,3=澳門居民來往內(nèi)地通行證,4=臺灣居民來往大陸通行證,6=護照") ? ? @ApiModelProperty(value = "證件類型") ? ? private String credType; ? ? @Excel(name = "證件號碼") ? ? @ApiModelProperty(value = "證件號碼") ? ? private String credNum; ? ? @ApiModelProperty(value = "機構id") ? ? private String orgId; ? ? @Excel(name = "機構名稱") ? ? @ApiModelProperty(value = "機構名稱") ? ? private String orgName; ? ? @Excel(name = "電話") ? ? @ApiModelProperty(value = "電話") ? ? private String phone; ? ? @Excel(name = "郵箱") ? ? @ApiModelProperty(value = "郵箱") ? ? private String email; ? ? @Excel(name = "人員類型",readConverterExp = "student=學生,teacher=教師,parent=家長,system=系統(tǒng)人員,developers=開發(fā)者,manager=管理員") ? ? @ApiModelProperty(value = "人員類型") ? ? private String personType; ? ? @Excel(name = "應用系統(tǒng)角色編碼") ? ? @ApiModelProperty(value = "應用系統(tǒng)角色編碼") ? ? private String appRoleCode; ? ? @ApiModelProperty(value = "創(chuàng)建時間") ? ? private LocalDateTime createTime; ? ? @ApiModelProperty(value = "更新時間") ? ? private LocalDateTime updateTime; }
第三步:解析工具類:(讀者請直接復制)
import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; ? import javax.servlet.http.HttpServletResponse; ? import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.DVConstraint; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFDataValidation; import org.apache.poi.hssf.usermodel.HSSFFont; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.HorizontalAlignment; 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.ss.usermodel.WorkbookFactory; import org.apache.poi.ss.util.CellRangeAddressList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; ? /** ?* Excel相關操作 ?* ?* @author sunziwen ?* @version 1.0 ?* @date 2022-03-25 15:20 ?**/ public class ExcelUtil<T> { ? ? private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); ? ? ? private Class<T> clazz; ? ? ? public ExcelUtil(Class<T> clazz) { ? ? ? ? this.clazz = clazz; ? ? } ? ? ? /** ? ? ?* 對excel表單默認第一個索引名轉換成list ? ? ?* ? ? ?* @param input 輸入流 ? ? ?* @return 轉換后集合 ? ? ?*/ ? ? public List<T> importExcel(InputStream input) throws Exception { ? ? ? ? return importExcel(StringUtils.EMPTY, input); ? ? } ? ? ? /** ? ? ?* 對excel表單指定表格索引名轉換成list ? ? ?* ? ? ?* @param sheetName 表格索引名 ? ? ?* @param input ? ? 輸入流 ? ? ?* @return 轉換后集合 ? ? ?*/ ? ? public List<T> importExcel(String sheetName, InputStream input) throws Exception { ? ? ? ? List<T> list = new ArrayList<T>(); ? ? ? ? ? Workbook workbook = WorkbookFactory.create(input); ? ? ? ? Sheet sheet = null; ? ? ? ? if (StringUtils.isNotEmpty(sheetName)) { ? ? ? ? ? ? // 如果指定sheet名,則取指定sheet中的內(nèi)容. ? ? ? ? ? ? sheet = workbook.getSheet(sheetName); ? ? ? ? } else { ? ? ? ? ? ? // 如果傳入的sheet名不存在則默認指向第1個sheet. ? ? ? ? ? ? sheet = workbook.getSheetAt(0); ? ? ? ? } ? ? ? ? ? if (sheet == null) { ? ? ? ? ? ? throw new IOException("文件sheet不存在"); ? ? ? ? } ? ? ? ? ? int rows = sheet.getPhysicalNumberOfRows(); ? ? ? ? ? if (rows > 0) { ? ? ? ? ? ? // 默認序號 // ? ? ? ? ? ?int serialNum = 0; ? ? ? ? ? ? // 有數(shù)據(jù)時才處理 得到類的所有field. ? ? ? ? ? ? Field[] allFields = clazz.getDeclaredFields(); ? ? ? ? ? ? ? /** ? ? ? ? ? ? ?* 這里是要將實體類的屬性與excel表的列序號對應上,有兩種方式: ? ? ? ? ? ? ?* 1.按照先后順序進行一一對應。 ? ? ? ? ? ? ?* 2.按照注解的name值與表頭對應起來 ? ? ? ? ? ? ?*/ ? ? ? ? ? ? ? // 定義一個map用于存放列的序號和field. ? ? ? ? ? ? Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>(); ? ? ? ? ? ? ? //定義一個name到Excel表的index的映射Map ? ? ? ? ? ? HashMap<String, Integer> name2index = new HashMap<>(); ? ? ? ? ? ? ? //默認第一行是表頭 ? ? ? ? ? ? Row r = sheet.getRow(0); ? ? ? ? ? ? for (int i = 0; i < allFields.length; i++) { ? ? ? ? ? ? ? ? Cell cell = r.getCell(i); ? ? ? ? ? ? ? ? if (cell == null) { ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? // 先設置Cell的類型,然后就可以把純數(shù)字作為String類型讀進來了 ? ? ? ? ? ? ? ? ? ? r.getCell(i).setCellType(CellType.STRING); ? ? ? ? ? ? ? ? ? ? cell = r.getCell(i); ? ? ? ? ? ? ? ? ? ? String c = cell.getStringCellValue(); ? ? ? ? ? ? ? ? ? ? if (StringUtils.isEmpty(c)) { ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? name2index.put(c, i); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? ? for (int col = 0; col < allFields.length; col++) { ? ? ? ? ? ? ? ? Field field = allFields[col]; ? ? ? ? ? ? ? ? // 將有注解的field存放到map中. ? ? ? ? ? ? ? ? if (field.isAnnotationPresent(Excel.class)) { ? ? ? ? ? ? ? ? ? ? Excel excel = field.getAnnotation(Excel.class); ? ? ? ? ? ? ? ? ? ? String name = excel.name(); ? ? ? ? ? ? ? ? ? ? Integer index = name2index.get(name); ? ? ? ? ? ? ? ? ? ? if (index != null) { ? ? ? ? ? ? ? ? ? ? ? ? field.setAccessible(true); ? ? ? ? ? ? ? ? ? ? ? ? fieldsMap.put(index, field); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? //下面這個是按序號一一對應的。不太友好,還是按照表頭名稱來對應 // ? ? ? ? ? ?for (int col = 0; col < allFields.length; col++) { // ? ? ? ? ? ? ? ?Field field = allFields[col]; // ? ? ? ? ? ? ? ?// 將有注解的field存放到map中. // ? ? ? ? ? ? ? ?if (field.isAnnotationPresent(Excel.class)) { // ? ? ? ? ? ? ? ? ? ?// 設置類的私有字段屬性可訪問. // ? ? ? ? ? ? ? ? ? ?field.setAccessible(true); // ? ? ? ? ? ? ? ? ? ?fieldsMap.put(++serialNum, field); // ? ? ? ? ? ? ? ?} // ? ? ? ? ? ?} ? ? ? ? ? ? for (int i = 1; i < rows; i++) { ? ? ? ? ? ? ? ? // 從第2行開始取數(shù)據(jù),默認第一行是表頭. ? ? ? ? ? ? ? ? Row row = sheet.getRow(i); ? ? ? ? ? ? ? ? int cellNum = allFields.length; ? ? ? ? ? ? ? ? T entity = null; ? ? ? ? ? ? ? ? for (int j = 0; j < cellNum; j++) { ? ? ? ? ? ? ? ? ? ? Cell cell = row.getCell(j); ? ? ? ? ? ? ? ? ? ? if (cell == null) { ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? ? ? // 先設置Cell的類型,然后就可以把純數(shù)字作為String類型讀進來了 ? ? ? ? ? ? ? ? ? ? ? ? row.getCell(j).setCellType(CellType.STRING); ? ? ? ? ? ? ? ? ? ? ? ? cell = row.getCell(j); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? String c = cell.getStringCellValue(); ? ? ? ? ? ? ? ? ? ? if (StringUtils.isEmpty(c)) { ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? // 如果不存在實例則新建. ? ? ? ? ? ? ? ? ? ? entity = (entity == null ? clazz.newInstance() : entity); ? ? ? ? ? ? ? ? ? ? // 從map中得到對應列的field. ? ? ? ? ? ? ? ? ? ? Field field = fieldsMap.get(j); ? ? ? ? ? ? ? ? ? ? // 取得類型,并根據(jù)對象類型設置值. ? ? ? ? ? ? ? ? ? ? Class<?> fieldType = field.getType(); ? ? ? ? ? ? ? ? ? ? if (String.class == fieldType) { ? ? ? ? ? ? ? ? ? ? ? ? field.set(entity, String.valueOf(c)); ? ? ? ? ? ? ? ? ? ? } else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) { ? ? ? ? ? ? ? ? ? ? ? ? field.set(entity, Integer.parseInt(c)); ? ? ? ? ? ? ? ? ? ? } else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) { ? ? ? ? ? ? ? ? ? ? ? ? field.set(entity, Long.valueOf(c)); ? ? ? ? ? ? ? ? ? ? } else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) { ? ? ? ? ? ? ? ? ? ? ? ? field.set(entity, Float.valueOf(c)); ? ? ? ? ? ? ? ? ? ? } else if ((Short.TYPE == fieldType) || (Short.class == fieldType)) { ? ? ? ? ? ? ? ? ? ? ? ? field.set(entity, Short.valueOf(c)); ? ? ? ? ? ? ? ? ? ? } else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) { ? ? ? ? ? ? ? ? ? ? ? ? field.set(entity, Double.valueOf(c)); ? ? ? ? ? ? ? ? ? ? } else if (Character.TYPE == fieldType) { ? ? ? ? ? ? ? ? ? ? ? ? if ((c != null) && (c.length() > 0)) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? field.set(entity, Character.valueOf(c.charAt(0))); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? } else if (Date.class == fieldType) { ? ? ? ? ? ? ? ? ? ? ? ? //對字符串解析成日期 ? ? ? ? ? ? ? ? ? ? ? ? ? SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ? ? ? ? ? ? ? ? ? ? ? ? ? String s = cell.getStringCellValue() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .replaceAll("/", "-") ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .replaceAll("上午", "") ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .replaceAll("下午", "") ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .replaceAll(" ?", " "); ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("----------------------------:" + s); ? ? ? ? ? ? ? ? ? ? ? ? Date parse = sdf.parse(s); ? ? ? ? ? ? ? ? ? ? ? ? field.set(entity, parse); // ? ? ? ? ? ? ? ? ? ? ? ?if (cell.getCellTypeEnum() == CellType.NUMERIC) { // ? ? ? ? ? ? ? ? ? ? ? ? ? ?SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // ? ? ? ? ? ? ? ? ? ? ? ? ? ?cell.setCellValue(sdf.format(cell.getNumericCellValue())); // ? ? ? ? ? ? ? ? ? ? ? ? ? ?c = sdf.format(cell.getNumericCellValue()); // ? ? ? ? ? ? ? ? ? ? ? ?} else { // ? ? ? ? ? ? ? ? ? ? ? ? ? ?c = cell.getStringCellValue(); // ? ? ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ? ? } else if (java.math.BigDecimal.class == fieldType) { ? ? ? ? ? ? ? ? ? ? ? ? c = cell.getStringCellValue(); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if (entity != null) { ? ? ? ? ? ? ? ? ? ? list.add(entity); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? return list; ? ? } ? ? ? /** ? ? ?* 對list數(shù)據(jù)源將其里面的數(shù)據(jù)導入到excel表單 ? ? ?* 針對List<Map>類型的數(shù)據(jù) ? ? ?* ? ? ?* @param list ? ? ?導出數(shù)據(jù)集合 ? ? ?* @param sheetName 工作表的名稱 ? ? ?* @return 結果 ? ? ?*/ ? ? public static void exportMapExcel(List<Map> list, String[] title, String sheetName, HttpServletResponse response) { ? ? ? ? if (null == list || list.size() == 0) { ? ? ? ? ? ? return; ? ? ? ? } ? ? ? ? ? HSSFWorkbook workbook = null; ? ? ? ? try { ? ? ? ? ? ? // 產(chǎn)生工作薄對象 ? ? ? ? ? ? workbook = new HSSFWorkbook(); ? ? ? ? ? ? // excel2003中每個sheet中最多有65536行 ? ? ? ? ? ? int sheetSize = 65536; ? ? ? ? ? ? // 取出一共有多少個sheet. ? ? ? ? ? ? double sheetNo = Math.ceil(list.size() / sheetSize); ? ? ? ? ? ? for (int index = 0; index <= sheetNo; index++) { ? ? ? ? ? ? ? ? // 產(chǎn)生工作表對象 ? ? ? ? ? ? ? ? HSSFSheet sheet = workbook.createSheet(); ? ? ? ? ? ? ? ? if (sheetNo == 0) { ? ? ? ? ? ? ? ? ? ? workbook.setSheetName(index, sheetName); ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? // 設置工作表的名稱. ? ? ? ? ? ? ? ? ? ? workbook.setSheetName(index, sheetName + index); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? HSSFRow row; ? ? ? ? ? ? ? ? HSSFCell cell; // 產(chǎn)生單元格 ? ? ? ? ? ? ? ? ? // 產(chǎn)生第一行,寫入標題 ? ? ? ? ? ? ? ? row = sheet.createRow(0); ? ? ? ? ? ? ? ? if (null == title || title.length == 0) { ? ? ? ? ? ? ? ? ? ? throw new RuntimeException("導出錯誤"); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? for (int i = 0; i < title.length; i++) { ? ? ? ? ? ? ? ? ? ? cell = row.createCell(i); ? ? ? ? ? ? ? ? ? ? cell.setCellType(CellType.STRING); ? ? ? ? ? ? ? ? ? ? cell.setCellValue(title[i]); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? int cell_idx = 0; ? ? ? ? ? ? ? ? int startNo = index * sheetSize; ? ? ? ? ? ? ? ? int endNo = Math.min(startNo + sheetSize, list.size()); ? ? ? ? ? ? ? ? // 寫入各條記錄,每條記錄對應excel表中的一行 ? ? ? ? ? ? ? ? for (int i = startNo; i < endNo; i++) { ? ? ? ? ? ? ? ? ? ? row = sheet.createRow(i + 1 - startNo); ? ? ? ? ? ? ? ? ? ? // 得到導出對象. ? ? ? ? ? ? ? ? ? ? Map map = list.get(i); ? ? ? ? ? ? ? ? ? ? Set keySet = map.keySet(); ? ? ? ? ? ? ? ? ? ? Iterator values = keySet.iterator(); ? ? ? ? ? ? ? ? ? ? cell_idx = 0; ? ? ? ? ? ? ? ? ? ? while (values.hasNext()) { ? ? ? ? ? ? ? ? ? ? ? ? Object value = map.get(values.next()); ? ? ? ? ? ? ? ? ? ? ? ? cell = row.createCell(cell_idx); ? ? ? ? ? ? ? ? ? ? ? ? cell.setCellValue(value.toString()); ? ? ? ? ? ? ? ? ? ? ? ? cell_idx++; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? //String filename = encodingFilename(sheetName); ? ? ? ? ? ? response.setHeader("content-Type", "application/vnd.ms-excel"); ? ? ? ? ? ? response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(sheetName, "UTF-8")); // ? ? ? ? ? ?response.setContentType("application/octet-stream"); // ? ? ? ? ? ?response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); ? ? ? ? ? ? response.flushBuffer(); ? ? ? ? ? ? workbook.write(response.getOutputStream()); ? ? ? ? ? ? //return filename; ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? log.error("導出Excel異常{}", e.getMessage()); ? ? ? ? ? ? throw new RuntimeException("導出Excel失敗,請聯(lián)系網(wǎng)站管理員!"); ? ? ? ? } finally { ? ? ? ? ? ? if (workbook != null) { ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? workbook.close(); ? ? ? ? ? ? ? ? } catch (IOException e1) { ? ? ? ? ? ? ? ? ? ? e1.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? ? /** ? ? ?* 對list數(shù)據(jù)源將其里面的數(shù)據(jù)導入到excel表單 ? ? ?* ? ? ?* @param list ? ? ?導出數(shù)據(jù)集合 ? ? ?* @param sheetName 工作表的名稱 ? ? ?* @return 結果 ? ? ?*/ ? ? public void exportExcel(List<?> list, String sheetName, HttpServletResponse response) { ? ? ? ? HSSFWorkbook workbook = null; ? ? ? ? try { ? ? ? ? ? ? // 得到所有定義字段 ? ? ? ? ? ? Field[] allFields = clazz.getDeclaredFields(); ? ? ? ? ? ? List<Field> fields = new ArrayList<Field>(); ? ? ? ? ? ? // 得到所有field并存放到一個list中. ? ? ? ? ? ? for (Field field : allFields) { ? ? ? ? ? ? ? ? if (field.isAnnotationPresent(Excel.class)) { ? ? ? ? ? ? ? ? ? ? fields.add(field); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? ? // 產(chǎn)生工作薄對象 ? ? ? ? ? ? workbook = new HSSFWorkbook(); ? ? ? ? ? ? // excel2003中每個sheet中最多有65536行 ? ? ? ? ? ? int sheetSize = 65536; ? ? ? ? ? ? // 取出一共有多少個sheet. ? ? ? ? ? ? double sheetNo = Math.ceil(list.size() / sheetSize); ? ? ? ? ? ? for (int index = 0; index <= sheetNo; index++) { ? ? ? ? ? ? ? ? // 產(chǎn)生工作表對象 ? ? ? ? ? ? ? ? HSSFSheet sheet = workbook.createSheet(); ? ? ? ? ? ? ? ? if (sheetNo == 0) { ? ? ? ? ? ? ? ? ? ? workbook.setSheetName(index, sheetName); ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? // 設置工作表的名稱. ? ? ? ? ? ? ? ? ? ? workbook.setSheetName(index, sheetName + index); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? HSSFRow row; ? ? ? ? ? ? ? ? HSSFCell cell; // 產(chǎn)生單元格 ? ? ? ? ? ? ? ? ? // 產(chǎn)生一行 ? ? ? ? ? ? ? ? row = sheet.createRow(0); ? ? ? ? ? ? ? ? // 寫入各個字段的列頭名稱 ? ? ? ? ? ? ? ? for (int i = 0; i < fields.size(); i++) { ? ? ? ? ? ? ? ? ? ? Field field = fields.get(i); ? ? ? ? ? ? ? ? ? ? Excel attr = field.getAnnotation(Excel.class); ? ? ? ? ? ? ? ? ? ? // 創(chuàng)建列 ? ? ? ? ? ? ? ? ? ? cell = row.createCell(i); ? ? ? ? ? ? ? ? ? ? // 設置列中寫入內(nèi)容為String類型 ? ? ? ? ? ? ? ? ? ? cell.setCellType(CellType.STRING); ? ? ? ? ? ? ? ? ? ? HSSFCellStyle cellStyle = workbook.createCellStyle(); ? ? ? ? ? ? ? ? ? ? cellStyle.setAlignment(HorizontalAlignment.CENTER); ? ? ? ? ? ? ? ? ? ? cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); ? ? ? ? ? ? ? ? ? ? if (attr.name().indexOf("注:") >= 0) { ? ? ? ? ? ? ? ? ? ? ? ? HSSFFont font = workbook.createFont(); ? ? ? ? ? ? ? ? ? ? ? ? font.setColor(HSSFFont.COLOR_RED); // ? ? ? ? ? ? ? ? ? ? ? ?cellStyle.setFont(font); ? ? ? ? ? ? ? ? ? ? ? ? //設置顏色 // ? ? ? ? ? ? ? ? ? ? ? ?cellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.YELLOW.getIndex()); ? ? ? ? ? ? ? ? ? ? ? ? sheet.setColumnWidth(i, 6000); ? ? ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? ? ? HSSFFont font = workbook.createFont(); ? ? ? ? ? ? ? ? ? ? ? ? // 粗體顯示 ? ? ? ? ? ? ? ? ? ? ? ? font.setBold(true); ? ? ? ? ? ? ? ? ? ? ? ? // 選擇需要用到的字體格式 // ? ? ? ? ? ? ? ? ? ? ? ?cellStyle.setFont(font); ? ? ? ? ? ? ? ? ? ? ? ? //設置顏色 // ? ? ? ? ? ? ? ? ? ? ? ?cellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.LIGHT_YELLOW.getIndex()); ? ? ? ? ? ? ? ? ? ? ? ? // 設置列寬 ? ? ? ? ? ? ? ? ? ? ? ? sheet.setColumnWidth(i, (int) ((attr.width() + 0.72) * 256)); ? ? ? ? ? ? ? ? ? ? ? ? row.setHeight((short) (attr.height() * 20)); ? ? ? ? ? ? ? ? ? ? } // ? ? ? ? ? ? ? ? ? ?cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // ? ? ? ? ? ? ? ? ? ?cellStyle.setWrapText(true); ? ? ? ? ? ? ? ? ? ? cell.setCellStyle(cellStyle); ? ? ? ? ? ? ? ? ? ? ? // 寫入列名 ? ? ? ? ? ? ? ? ? ? cell.setCellValue(attr.name()); ? ? ? ? ? ? ? ? ? ? ? // 如果設置了提示信息則鼠標放上去提示. ? ? ? ? ? ? ? ? ? ? if (StringUtils.isNotEmpty(attr.prompt())) { ? ? ? ? ? ? ? ? ? ? ? ? // 這里默認設了2-101列提示. ? ? ? ? ? ? ? ? ? ? ? ? setHSSFPrompt(sheet, "", attr.prompt(), 1, 100, i, i); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? // 如果設置了combo屬性則本列只能選擇不能輸入 ? ? ? ? ? ? ? ? ? ? if (attr.combo().length > 0) { ? ? ? ? ? ? ? ? ? ? ? ? // 這里默認設了2-101列只能選擇不能輸入. ? ? ? ? ? ? ? ? ? ? ? ? setHSSFValidation(sheet, attr.combo(), 1, 100, i, i); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? int startNo = index * sheetSize; ? ? ? ? ? ? ? ? int endNo = Math.min(startNo + sheetSize, list.size()); ? ? ? ? ? ? ? ? // 寫入各條記錄,每條記錄對應excel表中的一行 ? ? ? ? ? ? ? ? HSSFCellStyle cs = workbook.createCellStyle(); ? ? ? ? ? ? ? ? cs.setAlignment(HorizontalAlignment.CENTER); ? ? ? ? ? ? ? ? cs.setVerticalAlignment(VerticalAlignment.CENTER); ? ? ? ? ? ? ? ? for (int i = startNo; i < endNo; i++) { ? ? ? ? ? ? ? ? ? ? row = sheet.createRow(i + 1 - startNo); ? ? ? ? ? ? ? ? ? ? // 得到導出對象. ? ? ? ? ? ? ? ? ? ? T vo = (T) list.get(i); ? ? ? ? ? ? ? ? ? ? for (int j = 0; j < fields.size(); j++) { ? ? ? ? ? ? ? ? ? ? ? ? // 獲得field. ? ? ? ? ? ? ? ? ? ? ? ? Field field = fields.get(j); ? ? ? ? ? ? ? ? ? ? ? ? // 設置實體類私有屬性可訪問 ? ? ? ? ? ? ? ? ? ? ? ? field.setAccessible(true); ? ? ? ? ? ? ? ? ? ? ? ? Excel attr = field.getAnnotation(Excel.class); ? ? ? ? ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 設置行高 ? ? ? ? ? ? ? ? ? ? ? ? ? ? row.setHeight((short) (attr.height() * 20)); ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 根據(jù)Excel中設置情況決定是否導出,有些情況需要保持為空,希望用戶填寫這一列. ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (attr.isExport()) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 創(chuàng)建cell ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cell = row.createCell(j); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cell.setCellStyle(cs); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (vo == null) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 如果數(shù)據(jù)存在就填入,不存在填入空格. ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cell.setCellValue(""); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String dateFormat = attr.dateFormat(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String readConverterExp = attr.readConverterExp(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (StringUtils.isNotEmpty(dateFormat)) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cell.setCellValue(new SimpleDateFormat(dateFormat).format((Date) field.get(vo))); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } else if (StringUtils.isNotEmpty(readConverterExp)) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cell.setCellValue(convertByExp(String.valueOf(field.get(vo)), readConverterExp)); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cell.setCellType(CellType.STRING); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 如果數(shù)據(jù)存在就填入,不存在填入空格. ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cell.setCellValue(field.get(vo) == null ? attr.defaultValue() : field.get(vo) + attr.suffix()); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? log.error("導出Excel失敗{}", e.getMessage()); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? //String filename = encodingFilename(sheetName); ? ? ? ? ? ? response.setContentType("application/octet-stream"); ? ? ? ? ? ? response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(sheetName, "UTF-8")); ? ? ? ? ? ? response.flushBuffer(); ? ? ? ? ? ? workbook.write(response.getOutputStream()); ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? log.error("導出Excel異常{}", e.getMessage()); ? ? ? ? ? ? throw new RuntimeException("導出Excel失敗,請聯(lián)系網(wǎng)站管理員!"); ? ? ? ? } finally { ? ? ? ? ? ? if (workbook != null) { ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? workbook.close(); ? ? ? ? ? ? ? ? } catch (IOException e1) { ? ? ? ? ? ? ? ? ? ? e1.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? ? /** ? ? ?* 設置單元格上提示 ? ? ?* ? ? ?* @param sheet ? ? ? ? 要設置的sheet. ? ? ?* @param promptTitle ? 標題 ? ? ?* @param promptContent 內(nèi)容 ? ? ?* @param firstRow ? ? ?開始行 ? ? ?* @param endRow ? ? ? ?結束行 ? ? ?* @param firstCol ? ? ?開始列 ? ? ?* @param endCol ? ? ? ?結束列 ? ? ?* @return 設置好的sheet. ? ? ?*/ ? ? private static HSSFSheet setHSSFPrompt(HSSFSheet sheet, String promptTitle, String promptContent, int firstRow, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int endRow, int firstCol, int endCol) { ? ? ? ? // 構造constraint對象 ? ? ? ? DVConstraint constraint = DVConstraint.createCustomFormulaConstraint("DD1"); ? ? ? ? // 四個參數(shù)分別是:起始行、終止行、起始列、終止列 ? ? ? ? CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); ? ? ? ? // 數(shù)據(jù)有效性對象 ? ? ? ? HSSFDataValidation dataValidationView = new HSSFDataValidation(regions, constraint); ? ? ? ? dataValidationView.createPromptBox(promptTitle, promptContent); ? ? ? ? sheet.addValidationData(dataValidationView); ? ? ? ? return sheet; ? ? } ? ? ? /** ? ? ?* 設置某些列的值只能輸入預制的數(shù)據(jù),顯示下拉框. ? ? ?* ? ? ?* @param sheet ? ?要設置的sheet. ? ? ?* @param textlist 下拉框顯示的內(nèi)容 ? ? ?* @param firstRow 開始行 ? ? ?* @param endRow ? 結束行 ? ? ?* @param firstCol 開始列 ? ? ?* @param endCol ? 結束列 ? ? ?* @return 設置好的sheet. ? ? ?*/ ? ? private static HSSFSheet setHSSFValidation(HSSFSheet sheet, String[] textlist, int firstRow, int endRow, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int firstCol, int endCol) { ? ? ? ? // 加載下拉列表內(nèi)容 ? ? ? ? DVConstraint constraint = DVConstraint.createExplicitListConstraint(textlist); ? ? ? ? // 設置數(shù)據(jù)有效性加載在哪個單元格上,四個參數(shù)分別是:起始行、終止行、起始列、終止列 ? ? ? ? CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); ? ? ? ? // 數(shù)據(jù)有效性對象 ? ? ? ? HSSFDataValidation dataValidationList = new HSSFDataValidation(regions, constraint); ? ? ? ? sheet.addValidationData(dataValidationList); ? ? ? ? return sheet; ? ? } ? ? ? /** ? ? ?* 解析導出值 0=男,1=女,2=未知 ? ? ?* ? ? ?* @param propertyValue 參數(shù)值 ? ? ?* @param converterExp ?翻譯注解 ? ? ?* @return 解析后值 ? ? ?* @throws Exception ? ? ?*/ ? ? private static String convertByExp(String propertyValue, String converterExp) throws Exception { ? ? ? ? try { ? ? ? ? ? ? String[] convertSource = converterExp.split(","); ? ? ? ? ? ? for (String item : convertSource) { ? ? ? ? ? ? ? ? String[] itemArray = item.split("="); ? ? ? ? ? ? ? ? if (itemArray[0].equals(propertyValue)) { ? ? ? ? ? ? ? ? ? ? return itemArray[1]; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? throw e; ? ? ? ? } ? ? ? ? return propertyValue; ? ? } ? ? ? /** ? ? ?* 編碼文件名 ? ? ?*/ ? ? /*private static String encodingFilename(String filename) { ? ? ? ? filename = UUID.randomUUID().toString() + "_" + filename + ".xls"; ? ? ? ? return filename; ? ? }*/ }
第四步:無論導入導出,Excel表格的第一行都是跟實體類上@Excel注解的name屬性進行映射對應的。
以下是示例:導出Excel表(該方法最好是返回void或者返回null),否則后臺會報錯(不影響運行)。
? ? @PostMapping("/export") ? ? @ResponseBody ? ? public void export() ? ? { ? ? ? ? ExcelUtil<User> excelUtil = new ExcelUtil<>(User.class); ? ? ? ? //要導出的數(shù)據(jù)集 ? ? ? ? List<User> list = getList(); ? ? ? ? excelUtil.exportExcel(list, "userInformation.xlsx", ServletUtils.getResponse()); ? ? }
附件:前端代碼:
exportExcel() { ? ? let link = document.createElement('a'); ? ? link.style.display = 'none' ? ? axios({ ? ? ? ? url: 'http://test.cih.net/user/pageExport', ? ? ? ? methods: "get", ? ? ? ? data: {}, ? ? ? ? headers: { ? ? ? ? ? ? token: 'eyJ0eXB1234bklkIjoie1wiZWnm7jwkJtbvdcnqo' ? ? ? ? } ? ? }).then((res) => { ? ? ? ? let blob = new Blob([res.data], { ? ? ? ? ? ? type: "application/vnd.ms-excel" ? ? ? ? }); // 2.獲取請求返回的response對象中的blob 設置文件類型,這里以excel為例 ? ? ? ? let url = window.URL.createObjectURL(blob); // 3.創(chuàng)建一個臨時的url指向blob對象 ? ? ? ? ? // 4.創(chuàng)建url之后可以模擬對此文件對象的一系列操作,例如:預覽、下載 ? ? ? ? let a = document.createElement("a"); ? ? ? ? a.href = url; ? ? ? ? a.download = "導出表格.xlsx"; ? ? ? ? a.click(); ? ? ? ? // 5.釋放這個臨時的對象url ? ? ? ? window.URL.revokeObjectURL(url); ? ? }) }
java簡單的導出excel
1.pom 引入poi
? ? <!-- poi --> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.apache.poi</groupId> ? ? ? ? ? ? <artifactId>poi</artifactId> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.apache.poi</groupId> ? ? ? ? ? ? <artifactId>poi-ooxml</artifactId> ? ? ? ? </dependency>
2.創(chuàng)建excel導出工具類
@Slf4j public class ExportExcelUtils { ? ? ? /** ? ? ?* 導出Excel ? ? ?* ? ? ?* @param excelName 要導出的excel名稱 ? ? ?* @param list ? ? ?要導出的數(shù)據(jù)集合 ? ? ?* @param c ? ? ? ? 中英文字段對應Map,即要導出的excel表頭 ? ? ?* @param response ?使用response可以導出到瀏覽器 ? ? ?* @param <T> ? ? ?*/ ? ? public static <T> void export(String excelName, List<T> list, Class<T> c, HttpServletResponse response) { ? ? ? ? // 設置日期格式 ? ? ? ? SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); ? ? ? ? // 設置默認文件名為當前時間:年月日時分秒 ? ? ? ? if (excelName == null || excelName == "") { ? ? ? ? ? ? excelName = df.format(new Date()); ? ? ? ? } else { ? ? ? ? ? ? excelName = excelName + df.format(new Date()); ? ? ? ? } ? ? ? ? ? try { ? ? ? ? ? ? //創(chuàng)建一個WorkBook,對應一個Excel文件 ? ? ? ? ? ? HSSFWorkbook wb = new HSSFWorkbook(); ? ? ? ? ? ? //在Workbook中,創(chuàng)建一個sheet,對應Excel中的工作薄(sheet) ? ? ? ? ? ? HSSFSheet sheet = wb.createSheet(excelName); ? ? ? ? ? ? //設置 邊距、頁眉、頁腳 ? ? ? ? ? ? HSSFPrintSetup printSetup = sheet.getPrintSetup(); ? ? ? ? ? ? //打印方向,true:橫向,false:縱向(默認) ? ? ? ? ? ? printSetup.setLandscape(true); ? ? ? ? ? ? printSetup.setHeaderMargin(0.2); ? ? ? ? ? ? printSetup.setFooterMargin(0.2); ? ? ? ? ? ? //設置打印縮放為88% ? ? ? ? ? ? ///printSetup.setScale((short) 55); ? ? ? ? ? ? printSetup.setFitHeight((short) 0); ? ? ? ? ? ? printSetup.setFitWidth((short) 1); ? ? ? ? ? ? //列從左向右顯示② ? ? ? ? ? ? /// printSetup.setLeftToRight(true); ? ? ? ? ? ? // 紙張 ? ? ? ? ? ? printSetup.setPaperSize(HSSFPrintSetup.A4_PAPERSIZE); ? ? ? ? ? ? // 頁邊距(下) ? ? ? ? ? ? sheet.setMargin(HSSFSheet.BottomMargin, 0.8); ? ? ? ? ? ? // 頁邊距(左) ? ? ? ? ? ? sheet.setMargin(HSSFSheet.LeftMargin, 0); ? ? ? ? ? ? // 頁邊距(右) ? ? ? ? ? ? sheet.setMargin(HSSFSheet.RightMargin, 0); ? ? ? ? ? ? // 頁邊距(上) ? ? ? ? ? ? sheet.setMargin(HSSFSheet.TopMargin, 0.8); ? ? ? ? ? ? //設置打印頁面為水平居中 ? ? ? ? ? ? sheet.setHorizontallyCenter(true); ? ? ? ? ? ? sheet.setVerticallyCenter(true); ? ? ? ? ? ? sheet.setAutobreaks(false); ? ? ? ? ? ? sheet.setFitToPage(false); ? ? ? ? ? ? Footer footer = sheet.getFooter(); ? ? ? ? ? ? //設置頁數(shù) ? ? ? ? ? ? footer.setCenter("第" + HeaderFooter.page() + "頁,共 " + HeaderFooter.numPages() + "頁"); ? ? ? ? ? ? Header header = sheet.getHeader(); ? ? ? ? ? ? //自定義頁眉,并設置頁眉 左中右顯示信息 ? ? ? ? ? ? //居中 ? ? ? ? ? ? ///header.setCenter("Center Header"); ? ? ? ? ? ? //靠左 ? ? ? ? ? ? header.setLeft(HSSFHeader.font("宋體", "") + ? ? ? ? ? ? ? ? ? ? HSSFHeader.fontSize((short) 16) + excelName + ".xlsx"); ? ? ? ? ? ? //靠右 ? ? ? ? ? ? ///header.setRight(HSSFHeader.font("Stencil-Normal", "Italic") + ? ? ? ? ? ? ///HSSFHeader.fontSize((short) 16) + "Right w/ Stencil-Normal Italic font and size 16"); ? ? ? ? ? ? ? //創(chuàng)建單元格,并設置值表頭 設置表頭居中 ? ? ? ? ? ? HSSFCellStyle style = wb.createCellStyle(); ? ? ? ? ? ? //設置邊框 ? ? ? ? ? ? //下邊框 ? ? ? ? ? ? style.setBorderBottom(HSSFCellStyle.BORDER_THIN); ? ? ? ? ? ? //左邊框 ? ? ? ? ? ? style.setBorderLeft(HSSFCellStyle.BORDER_THIN); ? ? ? ? ? ? //上邊框 ? ? ? ? ? ? style.setBorderTop(HSSFCellStyle.BORDER_THIN); ? ? ? ? ? ? //右邊框 ? ? ? ? ? ? style.setBorderRight(HSSFCellStyle.BORDER_THIN); ? ? ? ? ? ? //自動換行 ? ? ? ? ? ? ///style.setWrapText(true); ? ? ? ? ? ? //創(chuàng)建一個居中格式 ? ? ? ? ? ? style.setAlignment(HSSFCellStyle.ALIGN_CENTER); ? ? ? ? ? ? //上下居中 ? ? ? ? ? ? style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); ? ? ? ? ? ? //設置字體 ? ? ? ? ? ? HSSFFont font = wb.createFont(); ? ? ? ? ? ? font.setFontName("宋體"); ? ? ? ? ? ? ? style.setFont(font); ? ? ? ? ? ? // 填充工作表 ? ? ? ? ? ? //獲取需要轉出的excel表頭的map字段 ? ? ? ? ? ? LinkedHashMap<String, String> fieldMap = new LinkedHashMap<>(); ? ? ? ? ? ? //循環(huán)注解里面的值 填入Link集合 ? ? ? ? ? ? Field[] declaredFields = c.getDeclaredFields(); ? ? ? ? ? ? //獲取屬性對應的枚舉類 ? ? ? ? ? ? LinkedHashMap<String, Class> converterMap = new LinkedHashMap<>(); ? ? ? ? ? ? ? for (Field declaredField : declaredFields) { ? ? ? ? ? ? ? ? //獲取注解對象 ? ? ? ? ? ? ? ? ExportEntityMap declaredAnnotation = declaredField.getDeclaredAnnotation(ExportEntityMap.class); ? ? ? ? ? ? ? ? ExportProperty converterAnnotation = declaredField.getDeclaredAnnotation(ExportProperty.class); ? ? ? ? ? ? ? ? if (declaredAnnotation != null) { ? ? ? ? ? ? ? ? ? ? fieldMap.put(declaredAnnotation.EnName(), declaredAnnotation.CnName()); ? ? ? ? ? ? ? ? ? ? if (converterAnnotation != null) { ? ? ? ? ? ? ? ? ? ? ? ? converterMap.put(declaredAnnotation.EnName(), converterAnnotation.Converter()); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? fillSheet(sheet, list, fieldMap, style, converterMap); ? ? ? ? ? ? // 設置response頭信息 ? ? ? ? ? ? response.reset(); ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? log.info("excelName:{}", excelName); ? ? ? ? ? ? ? ? String fileName = URLEncoder.encode(excelName + ".xlsx", "UTF-8"); ? ? ? ? ? ? ? ? response.setHeader("Content-disposition", "attachment; filename=" + fileName); ? ? ? ? ? ? } catch (UnsupportedEncodingException e1) { ? ? ? ? ? ? ? ? log.error("導出excel失敗,異常:", e1); ? ? ? ? ? ? } ? ? ? ? ? ? //將文件輸出 ? ? ? ? ? ? OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); ? ? ? ? ? ? response.setContentType("application/octet-stream"); ? ? ? ? ? ? response.flushBuffer(); ? ? ? ? ? ? ? wb.write(outputStream); ? ? ? ? ? ? outputStream.flush(); ? ? ? ? ? ? outputStream.close(); ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? log.error("導出Excel失敗,異常:", e); ? ? ? ? } ? ? } ? ? ? ? /** ? ? ?* 根據(jù)字段名獲取字段對象 ? ? ?* ? ? ?* @param fieldName 字段名 ? ? ?* @param clazz ? ? 包含該字段的類 ? ? ?* @return 字段 ? ? ?*/ ? ? public static Field getFieldByName(String fieldName, Class<?> clazz) { ? ? ? ? // 拿到本類的所有字段 ? ? ? ? Field[] selfFields = clazz.getDeclaredFields(); ? ? ? ? // 如果本類中存在該字段,則返回 ? ? ? ? for (Field field : selfFields) { ? ? ? ? ? ? //如果本類中存在該字段,則返回 ? ? ? ? ? ? if (field.getName().equals(fieldName)) { ? ? ? ? ? ? ? ? return field; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? // 否則,查看父類中是否存在此字段,如果有則返回 ? ? ? ? Class<?> superClazz = clazz.getSuperclass(); ? ? ? ? if (superClazz != null && superClazz != Object.class) { ? ? ? ? ? ? //遞歸 ? ? ? ? ? ? return getFieldByName(fieldName, superClazz); ? ? ? ? } ? ? ? ? // 如果本類和父類都沒有,則返回空 ? ? ? ? return null; ? ? } ? ? ? /** ? ? ?* 根據(jù)字段名獲取字段值 ? ? ?* ? ? ?* @param fieldName 字段名 ? ? ?* @param o ? ? ? ? 對象 ? ? ?* @return 字段值 ? ? ?* @throws Exception 異常 ? ? ?*/ ? ? public static String getFieldValueByName(String fieldName, Object o) ? ? ? ? ? ? throws Exception { ? ? ? ? String fieldValue = null; ? ? ? ? //根據(jù)字段名得到字段對象 ? ? ? ? Field field = getFieldByName(fieldName, o.getClass()); ? ? ? ? //如果該字段存在,則取出該字段的值 ? ? ? ? if (field != null) { ? ? ? ? ? ? //類中的成員變量為private,在類外邊使用屬性值,故必須進行此操作 ? ? ? ? ? ? field.setAccessible(true); ? ? ? ? ? ? //獲取當前對象中當前Field的value ? ? ? ? ? ? fieldValue = field.get(o) == null ? "" : field.get(o).toString(); ? ? ? ? ? ? String fieldType = field.getGenericType().toString(); ? ? ? ? ? ? if ("class java.math.BigDecimal".equals(fieldType) || "class java.lang.Double".equals(fieldType)) { ? ? ? ? ? ? ? ? fieldValue = new BigDecimal(fieldValue).setScale(2, BigDecimal.ROUND_HALF_UP).toString(); ? ? ? ? ? ? } else if ("class java.time.LocalDateTime".equals(fieldType) || "class java.time.LocalDate".equals(fieldType)) { ? ? ? ? ? ? ? ? fieldValue = fieldValue.replace("T", " "); ? ? ? ? ? ? } ? ? ? ? } else { ? ? ? ? ? ? throw new Exception(o.getClass().getSimpleName() + "類不存在字段名 " ? ? ? ? ? ? ? ? ? ? + fieldName); ? ? ? ? } ? ? ? ? return fieldValue; ? ? } ? ? ? /** ? ? ?* 根據(jù)帶路徑或不帶路徑的屬性名獲取屬性值,即接受簡單屬性名, 如userName等,又接受帶路徑的屬性名,如student.department.name等 ? ? ?* ? ? ?* @param fieldNameSequence 帶路徑的屬性名或簡單屬性名 ? ? ?* @param o ? ? ? ? ? ? ? ? 對象 ? ? ?* @return 屬性值 ? ? ?* @throws Exception 異常 ? ? ?*/ ? ? public static String getFieldValueByNameSequence(String fieldNameSequence, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Object o) throws Exception { ? ? ? ? String value = null; ? ? ? ? // 將fieldNameSequence進行拆分 ? ? ? ? String[] attributes = fieldNameSequence.split("\\."); ? ? ? ? if (attributes.length == 1) { ? ? ? ? ? ? value = getFieldValueByName(fieldNameSequence, o); ? ? ? ? } else { ? ? ? ? ? ? // 根據(jù)數(shù)組中第一個連接屬性名獲取連接屬性對象,如student.department.name ? ? ? ? ? ? Object fieldObj = getFieldValueByName(attributes[0], o); ? ? ? ? ? ? //截取除第一個屬性名之后的路徑 ? ? ? ? ? ? String subFieldNameSequence = fieldNameSequence ? ? ? ? ? ? ? ? ? ? .substring(fieldNameSequence.indexOf(".") + 1); ? ? ? ? ? ? //遞歸得到最終的屬性對象的值 ? ? ? ? ? ? value = getFieldValueByNameSequence(subFieldNameSequence, fieldObj); ? ? ? ? } ? ? ? ? return value; ? ? } ? ? ? /** ? ? ?* 向工作表中填充數(shù)據(jù) ? ? ?* ? ? ?* @param sheet ? ? ? ?excel的工作表名稱 ? ? ?* @param list ? ? ? ? 數(shù)據(jù)源 ? ? ?* @param fieldMap ? ? 中英文字段對應關系的Map ? ? ?* @param style ? ? ? ?表格中的格式 ? ? ?* @param converterMap 屬性對應轉化器的Map ? ? ?* @throws Exception 異常 ? ? ?*/ ? ? public static <T> void fillSheet(HSSFSheet sheet, List<T> list, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?LinkedHashMap<String, String> fieldMap, HSSFCellStyle style, LinkedHashMap<String, Class> converterMap) throws Exception { ? ? ? ? // 定義存放英文字段名和中文字段名的數(shù)組 ? ? ? ? String[] enFields = new String[fieldMap.size()]; ? ? ? ? String[] cnFields = new String[fieldMap.size()]; ? ? ? ? // 填充數(shù)組 ? ? ? ? int count = 0; ? ? ? ? for (Map.Entry<String, String> entry : fieldMap.entrySet()) { ? ? ? ? ? ? enFields[count] = entry.getKey(); ? ? ? ? ? ? cnFields[count] = entry.getValue(); ? ? ? ? ? ? count++; ? ? ? ? } ? ? ? ? ? //存儲最大列寬 ? ? ? ? Map<Integer, Integer> maxWidth = new HashMap<>(16); ? ? ? ? HSSFRow row = sheet.createRow(0); ? ? ? ? HSSFCell cell = null; ? ? ? ? // 填充表頭 ? ? ? ? for (int i = 0; i < cnFields.length; i++) { ? ? ? ? ? ? cell = row.createCell(i); ? ? ? ? ? ? cell.setCellValue(cnFields[i]); ? ? ? ? ? ? cell.setCellStyle(style); ? ? ? ? ? ? sheet.autoSizeColumn(i); ? ? ? ? ? ? //設置自適應寬高 ? ? ? ? ? ? maxWidth.put(i, cell.getStringCellValue().getBytes().length * 256 + 200); ? ? ? ? } ? ? ? ? // 填充內(nèi)容 ? ? ? ? for (int index = 0; index < list.size(); index++) { ? ? ? ? ? ? row = sheet.createRow(index + 1); ? ? ? ? ? ? // 獲取單個對象 ? ? ? ? ? ? T item = list.get(index); ? ? ? ? ? ? int j = 0; ? ? ? ? ? ? for (int i = 0; i < enFields.length; i++) { ? ? ? ? ? ? ? ? HSSFCell createCell = row.createCell(j); ? ? ? ? ? ? ? ? String objValue = getFieldValueByNameSequence(enFields[i], item); ? ? ? ? ? ? ? ? //獲取屬性對應的枚舉類 ? ? ? ? ? ? ? ? Class<Enum> converterClazz = converterMap.get(enFields[i]); ? ? ? ? ? ? ? ? objValue = convertFieldValue(objValue, converterClazz); ? ? ? ? ? ? ? ? String fieldValue = objValue == null ? "" : objValue; ? ? ? ? ? ? ? ? cell = row.createCell(i); ? ? ? ? ? ? ? ? createCell.setCellValue(fieldValue); ? ? ? ? ? ? ? ? ? int length = createCell.getStringCellValue().getBytes().length * 256 + 200; ? ? ? ? ? ? ? ? //這里把寬度最大限制到15000 ? ? ? ? ? ? ? ? if (length > 15000) { ? ? ? ? ? ? ? ? ? ? length = 15000; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? maxWidth.put(j, Math.max(length, maxWidth.get(j))); ? ? ? ? ? ? ? ? j++; ? ? ? ? ? ? ? ? createCell.setCellStyle(style); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? // 列寬自適應 ? ? ? ? for (int i = 0; i < cnFields.length; i++) { ? ? ? ? ? ? sheet.setColumnWidth(i, maxWidth.get(i)); ? ? ? ? } ? ? } ? ? ? /** ? ? ?* 轉換屬性值 ? ? ?* ? ? ?* @param objValue ? ? ? : ? ? ?* @param converterClazz : ? ? ?* @return java.lang.String ? ? ?**/ ? ? private static <T> String convertFieldValue(String objValue, Class<Enum> converterClazz) throws Exception { ? ? ? ? if (ObjectUtil.isNotNull(converterClazz)) { ? ? ? ? ? ? //獲取所有枚舉實例 ? ? ? ? ? ? Enum[] enumConstants = converterClazz.getEnumConstants(); ? ? ? ? ? ? //根據(jù)方法名獲取方法 ? ? ? ? ? ? Method valueMethod = converterClazz.getMethod("getValue"); ? ? ? ? ? ? if (ObjectUtil.isNull(valueMethod)) { ? ? ? ? ? ? ? ? throw new Exception(converterClazz.getSimpleName() + "類找不到getValue()方法"); ? ? ? ? ? ? } ? ? ? ? ? ? Method descMethod = converterClazz.getMethod("getDescription"); ? ? ? ? ? ? if (ObjectUtil.isNull(descMethod)) { ? ? ? ? ? ? ? ? throw new Exception(converterClazz.getSimpleName() + "類找不到getDescription()方法"); ? ? ? ? ? ? } ? ? ? ? ? ? for (Enum enumFiled : enumConstants) { ? ? ? ? ? ? ? ? //執(zhí)行枚舉方法獲得枚舉實例對應的值 ? ? ? ? ? ? ? ? Object value = valueMethod.invoke(enumFiled); ? ? ? ? ? ? ? ? Object description = descMethod.invoke(enumFiled); ? ? ? ? ? ? ? ? if (ObjectUtil.equal(objValue, value.toString())) { ? ? ? ? ? ? ? ? ? ? objValue = description.toString(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return objValue; ? ? } ? }
3.自定義標簽Export標簽
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExportEntityMap { ? ? String EnName() default "實體屬性名稱"; ? ? ? String CnName() default "excel標題名稱"; } @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExportProperty { ? ? ? Class<? extends Enum> Converter(); }
枚舉
public enum SexEnum { ? ? ? /** ? ? ?* 男 ? ? ?*/ ? ? MALE(1, "MALE", "男"), ? ? ? /** ? ? ?* 女 ? ? ?*/ ? ? FEMALE(2, "FEMALE", "女"); ? ? ? private int value; ? ? ? private String code; ? ? ? private String description; ? ? ? SexEnum(int value, String code, String description) { ? ? ? ? this.value = value; ? ? ? ? this.code = code; ? ? ? ? this.description = description; ? ? } ? ? ? public int getValue() { ? ? ? ? return value; ? ? } ? ? ? public String getDescription() { ? ? ? ? return description; ? ? } ? ? ? public String getCode() { ? ? ? ? return code; ? ? } }
4. 實體類引入@ExportEntityMap標簽
? ? /** ? ? ?* 性別 ? ? ?*/ ? ? @ExportEntityMap(CnName = "性別", EnName = "sex") ? ? //通過@ExportProperty來轉換枚舉字段 ? ? @ExportProperty(Converter = SexEnum.class) ? ? private Integer sex; ? ? /** ? ? ?* 年齡 ? ? ?*/ ? ? @ExportEntityMap(CnName = "年齡", EnName = "age") ? ? private Integer age;
5.在Controller層中調用ExportExcelUtils方法即可
? ? @ApiOperation("xxxx") ? ? @PostMapping(value = "/xxxx") ? ? public void getExport(HttpServletResponse response, @RequestBody ListRo ro) { ? ? ? ? //導出數(shù)據(jù) ? ? ? ? ExportExcelUtils.export("name", list, EntityClass.class, response); ? ? }
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring MVC接口防數(shù)據(jù)篡改和重復提交
這篇文章主要為大家詳細介紹了Spring MVC接口防數(shù)據(jù)篡改和重復提交,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08SpringBoot返回前端Long類型字段丟失精度問題及解決方案
Java服務端返回Long整型數(shù)據(jù)給前端,JS會自動轉換為Number類型,本文主要介紹了SpringBoot返回前端Long類型字段丟失精度問題及解決方案,感興趣的可以了解一下2024-03-03SpringBoot如何返回Json數(shù)據(jù)格式
這篇文章主要介紹了SpringBoot如何返回Json數(shù)據(jù)格式問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03