Controller:
java
@PostMapping(value = "/export",
produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE, MediaType.APPLICATION_JSON_VALUE})
public void RApiExport(HttpServletResponse response, @RequestBody SearchListReqDto reqDto) throws Exception {
rapiSearchConfigService.exportData(response, reqDto);
}
service
java
public void exportData(HttpServletResponse response, SearchListReqDto reqDto) throws IOException, IllegalAccessException {
//excel对应多sheet实体类
ExportRApiDataTemplate exportRApiDataTemplate = new ExportRApiDataTemplate();
String statisticalStart = reqDto.getStatisticalStart();
String statisticalEnd = reqDto.getStatisticalEnd();
List<String> keywords = getKeywords(reqDto);
List<SearchUserInfo> searchUserInfoList = new ArrayList<>();
List<RapiSearchUser> searchUserList = rapiSearchUserDao.selectByCreateTime(statisticalStart, statisticalEnd, null, null, keywords);
searchUserList.forEach(r -> {
SearchUserInfo searchUserInfo = BeanConvertUtils.convertTo(r, SearchUserInfo::new);
searchUserInfo.setCreateTime(DateUtil.toString(r.getCreateTime(), DateUtil.yyyy_MM_ddHHmmss_format));
searchUserInfo.setUserHomePage(getUserShareUrl(r.getSearchUserInfo()));
searchUserInfoList.add(searchUserInfo);
});
exportRApiDataTemplate.setSearchUserInfoList(searchUserInfoList);
String nowDate = DateUtil.toString(new Date(), DateUtil.yyMMddHHmmss_format);
//调用工具类导出
EasyPoiUtils.exportMultiSheetWorkbook(nowDate + "RApi数据统计", exportRApiDataTemplate, response);
}
excel导出模版对应实体类(注:子类不序列化会抛错)
java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class ExportRApiDataTemplate {
@SheetName(name = "人员信息")
private List<SearchUserInfo> searchUserInfoList;
/* @SheetName(name = "帖子信息")
private List<SearchPostsInfo> searchPostsInfoList;
@SheetName(name = "标签信息")
private List<SearchHashTagInfo> searchHashTagInfoList;
*/
}
java
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class SearchUserInfo implements Serializable {
@Excel(name = "用户id")
private String uid;
@Excel(name = "用户名称",width = 30)
private String name;
@Excel(name = "已关注数量")
private String followingCount;
@Excel(name = "点赞数")
private String totalFavorited;
@Excel(name = "粉丝数量")
private String followerCount;
@Excel(name = "insId")
private String insId;
@Excel(name = "邮箱")
private String email;
@Excel(name = "用户主页链接")
private String userHomePage;
@Excel(name = "统计时间")
private String createTime;
@Excel(name = "数据抓取关键字",width = 50)
private String dataSource;
}
EasyPoiUtils 工具类
java
package com.seezoon.admin.modules.sys.utils;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.exception.excel.ExcelExportException;
import cn.afterturn.easypoi.exception.excel.ExcelImportException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.seezoon.admin.modules.sys.dto.excel.kol.SheetName;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @Classname EasyPoiUtils
* @Description
* @Date 2023/8/31 11:59
* @Created by jyl
*/
public class EasyPoiUtils {
static final Logger logger = LoggerFactory.getLogger(EasyPoiUtils.class);
private static final String XLS = "xls";
private static final String XLSX = "xlsx";
private static final String SPLIT = ".";
public static void newExportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response){
newExport(list, fileName, response);
}
private static void newDownLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) {
try {
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
workbook.write(response.getOutputStream());
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
private static void newExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) {
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
if (workbook != null);
newDownLoadExcel(fileName, response, workbook);
}
public static <T> List<T> newImportExcelMore(MultipartFile file, Class<T> pojoClass, ImportParams params){
if (file == null){
return null;
}
List<T> list = null;
try {
list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);
}catch (NoSuchElementException e){
throw new RuntimeException("excel文件不能为空");
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return list;
}
public static Workbook newGetWorkbook(MultipartFile file) {
Workbook workbook=null;
try {
// 获取Excel后缀名
String fileName = file.getOriginalFilename();
if (StringUtils.isEmpty(fileName) || fileName.lastIndexOf(SPLIT) < 0) {
logger.warn("解析Excel失败,因为获取到的Excel文件名非法!");
return null;
}
String fileType = fileName.substring(fileName.lastIndexOf(SPLIT) + 1, fileName.length());
// 获取Excel工作簿
if (fileType.equalsIgnoreCase(XLS)) {
workbook = new HSSFWorkbook(file.getInputStream());
} else if (fileType.equalsIgnoreCase(XLSX)) {
workbook = new XSSFWorkbook(file.getInputStream());
}
} catch (IOException e) {
e.printStackTrace();
}
return workbook;
}
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) {
ExportParams exportParams = new ExportParams(title, sheetName);
exportParams.setCreateHeadRows(isCreateHeader);
defaultExport(list, pojoClass, fileName, response, exportParams);
}
/**
* 导出excel
*
* @param list 数据list
* @param title 标题
* @param sheetName sheet名称
* @param pojoClass 实体class
* @param fileName 文件名
* @param response 响应
*/
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=GBK");
try {
response.setHeader("content-disposition",
"attachment;filename=" + java.net.URLEncoder.encode(fileName, "GBK")
+ ";filename*=GBK''" + java.net.URLEncoder.encode(fileName, "GBK"));
} catch (Exception e) {
// ...
}
defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName));
}
/**
* 导出excel
*
* @param list 多个Map key title 对应表格Title key entity 对应表格对应实体 key data
* @param fileName 标题
* @param response 响应
*/
public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) {
defaultExport(list, fileName, response);
}
/**
* 导出多个excel
*
* @param workbooks 多个excel文件 通过ExcelExportUtil.exportExcel往workbooks内放入excel
* @param fileNames 文件名 每个excel文件的名称顺序必须一致且名称请务必保证不重复
* @param fileName 压缩包文件名
* @param response 标题
*/
public static void exportExcels(List<Workbook> workbooks, List<String> fileNames, String fileName, HttpServletResponse response) {
try {
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".zip");
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
ZipOutputStream zipOut = new ZipOutputStream(toClient);
for (int i = 0; i < workbooks.size(); i++) {
ZipEntry entry = new ZipEntry(fileNames.get(i) + ".xls");
zipOut.putNextEntry(entry);
workbooks.get(i).write(zipOut);
}
zipOut.flush();
zipOut.close();
} catch (IOException e) {
throw new ExcelExportException(e.getMessage());
}
}
/**
* 导出excel
*
* @param list 数据list
* @param pojoClass 实体class
* @param fileName 文件名
* @param response 响应
*/
private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) {
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
downLoadExcel(fileName, response, workbook);
}
private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) {
try {
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
workbook.write(response.getOutputStream());
workbook.close();
} catch (IOException e) {
throw new ExcelImportException(e.getMessage());
}
}
private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) {
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
downLoadExcel(fileName, response, workbook);
}
public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
if (StringUtils.isBlank(filePath)) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
List<T> list = null;
try {
list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
} catch (NoSuchElementException e) {
throw new ExcelImportException("模板不能为空");
} catch (Exception e) {
e.printStackTrace();
throw new ExcelImportException(e.getMessage());
}
return list;
}
public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
if (file == null) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);//标题占几行,从哪行开始读取
params.setHeadRows(headerRows);//header占几行
params.setSheetNum(1);
List<T> list = null;
try {
list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);
} catch (NoSuchElementException e) {
throw new ExcelImportException("excel文件不能为空");
} catch (Exception e) {
throw new ExcelImportException(e.getMessage());
}
return list;
}
/**
* 多Sheet导出,不需要批注
* @param fileName 文件名
* @param exportDataSet 导出的类对象
* @param response
* @throws IllegalAccessException
* @throws IOException
*/
public static <T> void exportMultiSheetWorkbook(String fileName,T exportDataSet, HttpServletResponse response) throws IOException, IllegalAccessException {
exportMultiSheetWorkbook(fileName,exportDataSet,response,null);
}
/**
* 多Sheet导出,需要批注
* @param fileName 文件名
* @param exportDataSet 导出的类对象
* @param response
* @throws IllegalAccessException
* @throws IOException
*/
public static <T> void exportMultiSheetWorkbook(String fileName,T exportDataSet, HttpServletResponse response,String remake) throws IllegalAccessException, IOException {
// 多个sheet配置参数
final List<Map<String, Object>> sheetsList = Lists.newArrayList();
Class<?> aClass = exportDataSet.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
SheetName annotation = field.getAnnotation(SheetName.class);
final String sheetName = annotation.name();
Map<String, Object> exportMap = Maps.newHashMap();
final ExportParams exportParams = new ExportParams(null, sheetName);
// 以下3个参数为API中写死的参数名 分别是sheet配置/导出类(注解定义)/数据集
exportMap.put("title", exportParams);
// 获取list<?>中?的泛型
if (field.getType().isAssignableFrom(List.class)){
Type fc = field.getGenericType(); //如果是List类型,得到其Generic的类型
if(fc instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType) fc;
//得到泛型里的class类型对象。
Class genericClazz = (Class)pt.getActualTypeArguments()[0];
exportMap.put("entity", genericClazz);
}
exportMap.put("data", field.get(exportDataSet));
}else {
exportMap.put("entity", field.getType());
exportMap.put("data", new ArrayList<>(Collections.singletonList(field.get(exportDataSet))));
}
// 加入多sheet配置列表
sheetsList.add(exportMap);
}
// 导出文件
// 核心方法:导出含多个sheet的excel文件 【注意,该方法第二个参数必须与上述的ExportParams对象指定的导出类型一致,默认ExcelType.HSSF格式,否则执行此方法时会报错!!!】
final Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
// 添加批注格式 :0#姓名不能为空__1#学生性别 1:男 2:女__2#出生日期:yyyy-MM-dd__3#图片不能为空
if(StringUtils.isNotBlank(remake)){
buildComment(workbook,0,remake);
}
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName + ".xls", "UTF-8"));
workbook.write(response.getOutputStream());
// 写完数据关闭流
workbook.close();
}
public static void buildComment(Workbook workbook, int titleRowsIndex, String commentStr) {
Sheet sheet = workbook.getSheetAt(0);
//创建一个图画工具
Drawing<?> drawing = sheet.createDrawingPatriarch();
Row row = sheet.getRow(titleRowsIndex);
if (StringUtils.isNotBlank(commentStr)) {
//解析批注,并传换成map
Map<Integer, String> commentMap = getCommentMap(commentStr);
for (Map.Entry<Integer, String> entry : commentMap.entrySet()) {
Cell cell = row.getCell(entry.getKey());
//创建批注
Comment comment = drawing.createCellComment(newClientAnchor(workbook));
//输入批注信息
comment.setString(newRichTextString(workbook, entry.getValue()));
//将批注添加到单元格对象中
cell.setCellComment(comment);
if (entry.getValue().contains("必填项")){
//设置单元格背景颜色
CellStyle cellStyle = workbook.createCellStyle();
//通过workbook获得文字处理类
Font font = workbook.createFont();
font.setColor(Font.COLOR_RED);
//水平居中
cellStyle.setAlignment(HorizontalAlignment.CENTER);
//垂直居中
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
cellStyle.setFont(font);
cell.setCellStyle(cellStyle);
}
}
}
}
/**
* 批注信息,默认解析:批注#列索引,比如用户名不允许重复#0。可覆盖此方法,解析自定义的批注格式
*
* @param commentStr 当前行的所有批注信息
* @return key:列索引,value:对应列的所有批注信息
*/
protected static Map<Integer, String> getCommentMap(String commentStr) {
//每行的所有单元格的批注都在commentStr里,并用"__"分隔
String[] split = commentStr.split("__");
Map<Integer, String> commentMap = new HashMap<>();
for (String msg : split) {
String[] cellMsg = msg.split("#");
//如果当前列没有批注,会将该列的索引作为key存到map里;已有批注,以","分隔继续拼接
int cellIndex = Integer.parseInt(cellMsg[0]);
if (commentMap.get(cellIndex) == null) {
commentMap.put(cellIndex, cellMsg[1]);
} else {
commentMap.replace(cellIndex, commentMap.get(cellIndex) + "," + cellMsg[1]);
}
}
return commentMap;
}
private static ClientAnchor newClientAnchor(Workbook workbook) {
//xls
if (workbook instanceof HSSFWorkbook) {
return new HSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6);
}
//xlsx
else {
return new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6);
}
}
private static RichTextString newRichTextString(Workbook workbook, String msg) {
//xls
if (workbook instanceof HSSFWorkbook) {
return new HSSFRichTextString(msg);
}
//xlsx
else {
return new XSSFRichTextString(msg);
}
}
}