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());
}
}