基於註解的方式封裝一個通用的Excel下載輸出工具類

1. maven 依賴

首先引入Excel導出插件fastexcel文檔地址

	<dependency>
    <groupId>org.dhatim</groupId>
    <artifactId>fastexcel</artifactId>
    <version>0.10.11</version>
	</dependency>

2.定義註解

import java.lang.annotation.*;

/**
 * excel 導出 時標記的列名
 *
 * @author peter
 * create: 2020-01-07 15:06
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface ExcelColumn {
    /**
     * 列名
     */
    String value();

    /**
     * 列的編號,從1開始,默認標記第0列爲序號列
     */
    int columnIdx();
    /**
     * 對值需要特殊處理時
     */
    Class<? extends ExcelCellValueHandler> handler() default ExcelCellValueHandler.class;
}
/**
 * 處理 excel 單元格的值
 *
 * @author peter
 * create: 2020-01-08 09:55
 **/
public interface ExcelCellValueHandler {
    String handle(Object value);
}

3.處理註解及工具類

import org.dhatim.fastexcel.Workbook;
import org.dhatim.fastexcel.Worksheet;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.IncompleteAnnotationException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author peter
 * date: 2020-01-07 15:07
 **/
public final class ExcelUtils {
    private ExcelUtils() {
    }
    /**
     * @param response  響應流
     * @param excelName 表格名稱
     * @param data      數據列表
     * @param clazz     數據的Class
     * @param <T>       數據泛型
     * @return {@link Workbook}
     * @throws IOException            excel對象創建時
     * @throws IllegalAccessException 對象數據讀取錯誤時
     */
    public static <T> Workbook exportExcel(HttpServletResponse response, String excelName, Collection<T> data, Class<T> clazz) throws IOException, IllegalAccessException {
        try (OutputStream os = response.getOutputStream()) {
            setResponse(response, excelName);
            return exportExcel(os, excelName, data, clazz);
        }
    }


    public static <T> Workbook exportExcel(HttpServletResponse response, String excelName, SheetDataHandler handler, T data) throws IOException {
        try (OutputStream os = response.getOutputStream()) {
            setResponse(response, excelName);
            return exportExcel(os, excelName, handler, data);
        }
    }


    public static <T> Workbook exportExcel(OutputStream os, String excelName, SheetDataHandler handler, T data) throws IOException {
        try {
            Workbook wb = new Workbook(os, excelName, "1.0");
            Worksheet ws = wb.newWorksheet("Sheet 1");
            handler.handle(ws, data);
            wb.finish();
            return wb;
        } finally {
            os.close();
        }
    }

    public static <T> Workbook exportExcel(OutputStream os, String excelName, Collection<T> data, Class<T> clazz) throws IOException, IllegalAccessException {
        try {
            Workbook wb = new Workbook(os, excelName, "1.0");
            Worksheet ws = wb.newWorksheet("Sheet 1");
            Field[] declaredFields = getAllFields(clazz);
            List<ExcelColumn> collect = Arrays.stream(declaredFields).map(field -> field.getAnnotation(ExcelColumn.class))
                    .filter(Objects::nonNull)
                    .sorted(Comparator.comparingInt(ExcelColumn::columnIdx))
                    .collect(Collectors.toList());

            if (collect.isEmpty()) throw new IncompleteAnnotationException(ExcelColumn.class, clazz.getSimpleName());
            int rowIdx = 0;
            //表頭行
            for (int i = 0; i < collect.size(); i++) {
                if (i == 0) {
                    ws.value(rowIdx, i, "序號");
                }
                ExcelColumn excelColumn = collect.get(i);
                ws.value(rowIdx, excelColumn.columnIdx(), excelColumn.value());
            }
            //從第二行開始
            rowIdx++;
            for (T datum : data) {
                ws.value(rowIdx, 0, rowIdx);
                final int currentRow = rowIdx;
                for (Field field : declaredFields) {
                    ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
                    if (annotation == null) continue;
                    int columnIdx = annotation.columnIdx();
                    boolean flag = field.isAccessible();
                    if (!flag) {
                        field.setAccessible(true);
                    }
                    Object value = field.get(datum);

                    Class<? extends ExcelCellValueHandler> handler = annotation.handler();

                    if (!Objects.equals(handler.getSimpleName(), ExcelCellValueHandler.class.getSimpleName())) {
                       ExcelCellValueHandler valueHandler= null;
                        try {
                            valueHandler = handler.newInstance();
                        } catch (InstantiationException e) {
                            log.error("獲取單元格值處理{}實例失敗", handler.getSimpleName());
                            log.error(e.getMessage(), e);
                        }
                        if (valueHandler  != null){
                            ws.value(currentRow, columnIdx, valueHandler.handle(value));
                        }else{
                            ws.value(currentRow, columnIdx, value);
                        }
                    } else {
                        ws.value(currentRow, columnIdx, value);
                    }
                    field.setAccessible(flag);
                }
                //跳轉到下一行數據
                rowIdx++;
            }
            wb.finish();
            return wb;
        } finally {
            os.close();
        }
    }

    @FunctionalInterface
    public interface SheetDataHandler<T> {
        /**
         * 處理表格數據(將數據寫入表格)
         * ex:
         * ws.value(0, 0, "This is a string in A1");
         * ws.value(0, 1, 1123);
         * ws.value(0, 2, 1234);
         * ws.value(0, 3, 123456L);
         * ws.value(0, 4, 1.234);
         *
         * @param ws   表格sheet
         * @param data 實際數據
         */
        void handle(Worksheet ws, T data);
    }

    private static void setResponse(HttpServletResponse response, String excelName) {
        response.setHeader("Content-disposition", "attachment;filename="
                + new String(excelName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + ".xlsx");
        response.setCharacterEncoding("utf-8");
        // 由於導出格式是excel的文件,設置導出文件的響應頭部信息
        response.setContentType("application/vnd.ms-excel");
    }

    private static Field[] getAllFields(final Class<?> cls) {
        final List<Field> allFieldsList = getAllFieldsList(cls);
        return allFieldsList.toArray(new Field[0]);
    }

    private static List<Field> getAllFieldsList(final Class<?> cls) {
        Assert.isTrue(cls != null, "The class must not be null");
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            allFields.addAll(Arrays.asList(declaredFields));
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
    }
}

4.示例

@GetMapping(DOWNLOAD_BASE_URL+"{key}")
 public void downloadVipUserData(HttpServletResponse response, @PathVariable("key") String key) throws IOException, IllegalAccessException {
        List<VipUserItemVO> remove = CACHE_DOWNLOAD_DATA.remove(key);
        if (remove == null) {
            throw new SystemServiceException(ErrorMessageEnum.NO_DATA_EXIST_EXCEPTION,"未找到數據");
        }
        ExcelUtils.exportExcel(response,"VIP用戶信息表",remove,VipUserItemVO.class);
}
import lombok.Data;

import java.time.LocalDateTime;

/**
 * @author peter
 * date: 2019-12-09 09:51
 **/
@Data
public class VipUserItemVO {

    private Long id;
   
    @ExcelColumn(value = "註冊時間", columnIdx = 3, handler = DateFormatHandler.class)
    private String createTime;

    @ExcelColumn(value = "手機號", columnIdx = 2)
    private String phoneNumber;

    @ExcelColumn(value = "暱稱", columnIdx = 1)
    private String nickname;
}

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;

/**
 * @author peter
 * date: 2020-01-08 10:01
 **/
public class DateFormatHandler implements ExcelCellValueHandler {
    @Override
    public String handle(Object value) {
        if (Objects.isNull(value)) return "";
        if (value instanceof LocalDateTime) {
            return TimeUtils.formatDateTime2String((LocalDateTime) value);
        } else if (value instanceof LocalDate) {
            return TimeUtils.formatLocalDate((LocalDate) value);
        } else if (value instanceof Instant) {
            LocalDateTime time = TimeUtils.convert2LocalDateTime(((Instant) value));
            return TimeUtils.formatDateTime2String(time);
        } else if (value instanceof Date) {
            LocalDateTime time = TimeUtils.convert2LocalDateTime((Date) value);
            return TimeUtils.formatDateTime2String(time);
        }
        return value.toString();
    }
}

5.時間工具類

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
* @author peter
* date: 2019-09-12 08:56
**/
public final class TimeUtils {
   public static final String TIME_PATTERN = "yyyy/MM/dd HH:mm:ss";
   private static final String DATE_FORMAT = "yyyy-MM-dd";
   public static final String TIME_FORMAT = "HH:mm:ss";
   private static final String DATETIME_FORMAT = DATE_FORMAT + " " + TIME_FORMAT;
   private static final String DAY_START_TIME = "00:00:00";
   private static final String DAY_END_TIME = "23:59:59";
   private static final String YYYY_MM = "yyyy-MM";


   public static LocalDate[] latest30Days(LocalDate localDate) {
       int len = 30;
       LocalDate[] localDates = new LocalDate[len];

       for (int i = 0; i < len; i++) {

           localDates[i] = localDate.minusDays(i);
       }

       return localDates;
   }


   public static LocalDateTime atEndOfDay(Long timestamp) {
       LocalDate localDate = convert2LocalDate(timestamp);
       return atEndOfDay(localDate);
   }


   public static LocalDateTime atEndOfDay(LocalDate localDate) {
       String timeString = formatLocalDate(localDate) + " " + DAY_END_TIME;

       return parseDateTime(timeString);
   }

   /**
    * 獲取 date 日期 的上一週的開始結束日期,即使給定的日期是週一 也會 計算上一週的日期
    *
    * @param date 基準日期點
    * @return [0]:上一週的開始日期;[1] 上一週的結束日期
    */
   public static LocalDate[] getLastWeek(LocalDate date) {

       LocalDate[] weeks = new LocalDate[2];

       LocalDate localDate = date.minusWeeks(1);

       weeks[0] = localDate.with(DayOfWeek.MONDAY);
       weeks[1] = localDate.with(DayOfWeek.SUNDAY);

       return weeks;
   }

   /**
    * 獲取 date 日期所在的周 的開始日期和結束日期
    *
    * @param date 基準日期
    * @return [0]:周的開始日期;[1] 周的結束日期
    */
   public static LocalDate[] getThisWeek(LocalDate date) {

       LocalDate[] weeks = new LocalDate[2];

       weeks[0] = date.with(DayOfWeek.MONDAY);
       weeks[1] = date.with(DayOfWeek.SUNDAY);

       return weeks;
   }

   /**
    * 獲取 基準日期date 的上一個月的開始日期和結束日期
    *
    * @param date 基準日期點
    * @return [0]:上一個月的開始日期;[1] 上一個月的結束日期
    */
   public static LocalDate[] getLastMonth(LocalDate date) {

       LocalDate[] month = new LocalDate[2];
       LocalDate localDate = date.minusMonths(1);

       //startOfMonth
       month[0] = localDate.withDayOfMonth(1);

       //上一個月的天數

       int lengthOfMonth = localDate.lengthOfMonth();
       //endOfMonth
       month[1] = localDate.withDayOfMonth(lengthOfMonth);

       return month;
   }

   /**
    * 獲取 基準日期date 的【所在】月的開始日期和結束日期
    *
    * @param date 基準日期
    * @return [0]:月的開始日期;[1] 月的結束日期
    */
   public static LocalDate[] getThisMonth(LocalDate date) {

       LocalDate[] month = new LocalDate[2];

       //startOfMonth
       month[0] = date.withDayOfMonth(1);

       //上一個月的天數

       int lengthOfMonth = date.lengthOfMonth();
       //endOfMonth
       month[1] = date.withDayOfMonth(lengthOfMonth);

       return month;
   }

   /**
    * 獲取某一個時間得方法
    */
   public static LocalDate getYesterday() {

       LocalDate now = LocalDate.now(ZoneId.systemDefault());
       return now.minusDays(1);
   }

   public static String localDateNow() {
       LocalDate now = LocalDate.now(ZoneId.systemDefault());
       return formatLocalDate(now);
   }

   public static String localTimeNow() {
       LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault());
       DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(TIME_FORMAT);
       return now.format(dateTimeFormatter);
   }

   public static LocalDateTime todayStartTime() {
       return todayTime(DAY_START_TIME);
   }

   public static LocalDateTime todayEndTime() {
       return todayTime(DAY_END_TIME);
   }


   public static String getTime(LocalDateTime time) {
       return formatDateTime2String(time, TIME_FORMAT);
   }

   public static String getDate(LocalDateTime dateTime) {
       return formatDateTime2String(dateTime, DATE_FORMAT);
   }


   /**
    * 時間轉換的方法
    */
   public static Date convert2Date(LocalDate localDate) {

       Instant instant = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
       return Date.from(instant);
   }

   public static Date convert2Date(LocalDateTime dateTime) {
       Instant instant = convert2Instant(dateTime);
       return Date.from(instant);
   }

   public static LocalDate convert2LocalDate(Date date) {
       return convert2LocalDateTime(date).toLocalDate();
   }


   public static LocalDateTime convert2LocalDateTime(Date date) {
       Instant instant = date.toInstant();
       return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

   }

   public static LocalDateTime convert2LocalDateTime(Instant instant) {
       return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
   }

   public static LocalDateTime convert2LocalDateTime(Long timestamp) {
       Instant instant = Instant.ofEpochMilli(timestamp);
       return convert2LocalDateTime(instant);
   }

   public static LocalDate convert2LocalDate(Long timestamp) {
       LocalDateTime time = convert2LocalDateTime(timestamp);
       return time.toLocalDate();
   }

   public static Long convert2Timestamp(LocalDateTime time) {
       return convert2Instant(time).toEpochMilli();
   }

   public static Instant convert2Instant(LocalDateTime time) {
       return time.toInstant(getOffset());
   }


   /**
    * 解析時間
    */

   public static LocalDateTime parseDateTime(String dateTime) {
       return parseDateTime(dateTime, DATETIME_FORMAT);
   }

   public static LocalDateTime parseDateTime(String dateTime, String pattern) {
       DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(pattern);
       return LocalDateTime.from(timeFormatter.parse(dateTime));
   }

   public static LocalDate parseLocalDate(String date) {
       DateTimeFormatter pattern = DateTimeFormatter.ofPattern(DATE_FORMAT);
       return LocalDate.parse(date, pattern);
   }

   /**
    * 格式化時間 方法
    */

   public static String formatLocalDate(LocalDate now) {
       DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
       return now.format(dateTimeFormatter);
   }


   public static String formatLocalDate(LocalDateTime now) {
       DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
       return now.format(dateTimeFormatter);
   }


   public static String formatDateTime2String(LocalDateTime dateTime) {
       return formatDateTime2String(dateTime, DATETIME_FORMAT);
   }


   public static String formatDateTime2String(LocalDateTime dateTime, String pattern) {
       if (dateTime == null) return "";
       DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(pattern);
       return dateTime.format(timeFormatter);
   }


   public static String formatYearMonth2String(LocalDate date) {
       return formatDate2String(date, YYYY_MM);
   }

   public static String formatDate2String(LocalDate date, String pattern) {
       DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(pattern);
       return date.format(timeFormatter);
   }

   /**
    * 計算 method
    */

   public static int diff(LocalDate start, LocalDate end) {
       if (start.isAfter(end)) {
           throw new IllegalArgumentException("start date must be less than end date");
       }
       return (int) (end.toEpochDay() - start.toEpochDay());
   }


   public static Long[] checkStartTimeAndEndTimeParams(Long startTime, Long endTime) {
     if (startTime != null && startTime != 0) {

           long now = Instant.now().toEpochMilli();

           if (now < startTime) {
               throw new IllegalArgumentException("開始時間不能大於現在的時間");
           }

           if (endTime == null) {
               endTime = now;
           } else {
               if (endTime < startTime) {
                   throw new IllegalArgumentException("開始時間必須小於結束時間");
               }
           }
       }

       return new Long[]{startTime, endTime};
   }

   /**
    * 私有方法
    */

   private static LocalDateTime todayTime(String time) {

       LocalDate now = LocalDate.now(ZoneId.systemDefault());
       String dateTime = now + " " + time;
       DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(DATETIME_FORMAT);

       return LocalDateTime.from(timeFormatter.parse(dateTime));
   }

   private static ZoneOffset getOffset() {
       return ZoneId.systemDefault().getRules().getOffset(LocalDateTime.now());
   }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章