294 lines
11 KiB
Java
294 lines
11 KiB
Java
package com.youchain.utils;
|
||
|
||
import cn.idev.excel.ExcelReader;
|
||
import cn.idev.excel.ExcelWriter;
|
||
import cn.idev.excel.FastExcel;
|
||
import cn.idev.excel.read.metadata.ReadSheet;
|
||
import cn.idev.excel.write.metadata.WriteSheet;
|
||
import cn.idev.excel.write.metadata.style.WriteCellStyle;
|
||
import cn.idev.excel.write.metadata.style.WriteFont;
|
||
import cn.idev.excel.write.style.HorizontalCellStyleStrategy;
|
||
|
||
import com.youchain.exception.BadRequestException;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.apache.commons.collections4.CollectionUtils;
|
||
import org.apache.commons.lang3.StringUtils;
|
||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||
import org.apache.poi.ss.usermodel.Workbook;
|
||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||
import org.springframework.http.HttpHeaders;
|
||
import org.springframework.http.MediaType;
|
||
import org.springframework.http.MediaTypeFactory;
|
||
import org.springframework.web.multipart.MultipartFile;
|
||
|
||
import javax.servlet.http.HttpServletResponse;
|
||
import java.io.IOException;
|
||
import java.io.InputStream;
|
||
import java.io.UnsupportedEncodingException;
|
||
import java.net.URLEncoder;
|
||
import java.util.*;
|
||
import java.util.function.BiFunction;
|
||
import java.util.function.Function;
|
||
import java.util.function.ToLongFunction;
|
||
import java.util.stream.Collectors;
|
||
|
||
import static cn.hutool.core.util.CharsetUtil.UTF_8;
|
||
|
||
|
||
/**
|
||
* excel 工具类
|
||
*/
|
||
@Slf4j
|
||
public class FastExcelUtil {
|
||
|
||
/**
|
||
* 设置下载文件消息头
|
||
*
|
||
* @param response 响应
|
||
* @param fileName 文件名
|
||
* @param fileSize 文件大小
|
||
*/
|
||
public static void setDownloadFileHeader(HttpServletResponse response, String fileName, Long fileSize) {
|
||
response.setCharacterEncoding(UTF_8);
|
||
if (fileSize != null) {
|
||
response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileSize));
|
||
}
|
||
|
||
if (StringUtils.isNotEmpty(fileName)) {
|
||
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaTypeFactory.getMediaType(fileName).orElse(MediaType.APPLICATION_OCTET_STREAM) + ";charset=utf-8");
|
||
try {
|
||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(fileName, UTF_8).replaceAll("\\+", "%20"));
|
||
} catch (UnsupportedEncodingException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取样式
|
||
*
|
||
* @return horizontalCellStyleStrategy
|
||
*/
|
||
public static HorizontalCellStyleStrategy getHorizontalCellStyleStrategy() {
|
||
|
||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||
headWriteCellStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
|
||
|
||
WriteFont headWriteFont = new WriteFont();
|
||
headWriteFont.setFontName("宋体");
|
||
headWriteFont.setColor(IndexedColors.WHITE.getIndex());
|
||
headWriteCellStyle.setWriteFont(headWriteFont);
|
||
|
||
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
|
||
contentWriteCellStyle.setFillForegroundColor(IndexedColors.BLACK.getIndex());
|
||
|
||
return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
|
||
}
|
||
|
||
/**
|
||
* 通用单sheet导出
|
||
*/
|
||
public static void exportExcel(HttpServletResponse response, String fileName, String sheetName, Class head, Collection<?> data) throws IOException {
|
||
// 设置下载消息头
|
||
setDownloadFileHeader(response, fileName, null);
|
||
//样式
|
||
HorizontalCellStyleStrategy horizontalCellStyleStrategy = getHorizontalCellStyleStrategy();
|
||
// 下载
|
||
FastExcel.write(response.getOutputStream(), head)
|
||
.autoCloseStream(Boolean.FALSE)
|
||
.sheet(sheetName)
|
||
.registerWriteHandler(horizontalCellStyleStrategy)
|
||
.doWrite(data);
|
||
}
|
||
|
||
/**
|
||
* 大数据导出
|
||
*
|
||
* @param response 响应
|
||
* @param fileName 文件名
|
||
* @param sheetName sheet名称
|
||
* @param head 头
|
||
* @param pageQueryFunction 查询方法
|
||
* @param convertFunction 转换方法
|
||
* @param idExtractor id提取方法
|
||
* @param pageSize 每页大小
|
||
* @param <T> 泛型
|
||
* @param <E> 泛型
|
||
*/
|
||
public static <T, E> void batchExportExcel(HttpServletResponse response, String fileName, String sheetName, Class head, BiFunction<Long, Integer, List<T>> pageQueryFunction, Function<T, E> convertFunction, ToLongFunction<T> idExtractor, int pageSize) {
|
||
long startTime = System.currentTimeMillis();
|
||
// 设置下载消息头
|
||
setDownloadFileHeader(response, fileName, null);
|
||
HorizontalCellStyleStrategy horizontalCellStyleStrategy = getHorizontalCellStyleStrategy();
|
||
try (ExcelWriter excelWriter = FastExcel.write(response.getOutputStream(), head).registerWriteHandler(horizontalCellStyleStrategy).build()) {
|
||
WriteSheet writeSheet = FastExcel.writerSheet(sheetName).build();
|
||
long lastId = 0;//最后一个id
|
||
while (true) {
|
||
List<T> batch = pageQueryFunction.apply(lastId, pageSize);
|
||
if (batch.isEmpty()) {
|
||
break;
|
||
}
|
||
|
||
List<E> excelData = batch.stream()
|
||
.map(convertFunction)
|
||
.collect(Collectors.toList());
|
||
excelWriter.write(excelData, writeSheet);
|
||
|
||
lastId = batch.stream()
|
||
.mapToLong(idExtractor)
|
||
.max()
|
||
.orElse(lastId);
|
||
}
|
||
excelWriter.finish();
|
||
log.info("导出总耗时: {}ms", System.currentTimeMillis() - startTime);
|
||
} catch (Exception e) {
|
||
throw new BadRequestException("导出失败");
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 从 Excel 文件中读取指定类型的数据
|
||
*
|
||
* @param file 上传的 Excel 文件
|
||
* @param clazz 数据映射的类(如 Template1.class)
|
||
* @param <T> 数据类型
|
||
* @return 解析后的数据列表
|
||
*/
|
||
public static <T> List<T> readExcelData(MultipartFile file, Class<T> clazz, int sheetNo, int headRowNumber) {
|
||
List<T> dataList;
|
||
try {
|
||
dataList = FastExcel.read(file.getInputStream()).head(clazz)
|
||
.sheet(sheetNo)
|
||
.headRowNumber(headRowNumber)
|
||
.doReadSync();
|
||
} catch (IOException e) {
|
||
throw new BadRequestException("数据格式存在问题,无法读取");
|
||
}
|
||
if (CollectionUtils.isEmpty(dataList)) {
|
||
throw new BadRequestException("数据为空");
|
||
}
|
||
return dataList;
|
||
}
|
||
|
||
|
||
public static List<Map<Integer, Object>> readExcelData2(MultipartFile file, int headRowNumber) {
|
||
try {
|
||
return FastExcel.read(file.getInputStream())
|
||
.sheet()
|
||
.headRowNumber(headRowNumber)
|
||
.doReadSync();
|
||
} catch (IOException e) {
|
||
throw new BadRequestException("数据格式存在问题,无法读取");
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 读取Excel文件中指定位置的单元格值
|
||
*
|
||
* @param file Excel文件
|
||
* @param reader 指定行和列
|
||
*/
|
||
public static Map<String, String> readExcelCellValues(MultipartFile file, FastExcelCellListener reader, int sheetNo) {
|
||
try {
|
||
FastExcel.read(file.getInputStream(), reader)
|
||
.sheet(sheetNo)
|
||
.doReadSync();
|
||
} catch (IOException e) {
|
||
throw new BadRequestException("数据格式存在问题,无法读取");
|
||
}
|
||
|
||
return reader.getAllCellValues();
|
||
}
|
||
|
||
/**
|
||
* 读取 Excel 文件头部的所有内容(第一行)
|
||
*
|
||
* @param inputStream Excel 文件输入流
|
||
* @return 头部内容列表
|
||
*/
|
||
public static List<String> readHeadContent(InputStream inputStream, int sheetNo, int headRowNumber) {
|
||
List<Object> data = FastExcel.read(inputStream)
|
||
.sheet(sheetNo)
|
||
.headRowNumber(headRowNumber)
|
||
.doReadSync();
|
||
// 检查是否有数据
|
||
if (data.isEmpty()) {
|
||
throw new BadRequestException("导入模板不正确,请确认模板信息");
|
||
}
|
||
Object firstRow = data.get(0);
|
||
// 将头部内容转换为字符串列表
|
||
if (firstRow instanceof Map) {
|
||
Map<?, ?> rowMap = (Map<?, ?>) firstRow;
|
||
return rowMap.values().stream()
|
||
.map(value -> value == null ? "" : value.toString())
|
||
.collect(Collectors.toList());
|
||
} else {
|
||
throw new BadRequestException("读取Excel头部内容格式异常");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证 Excel 文件头部是否包含指定的列名
|
||
*
|
||
* @return 验证结果,true 表示包含所有必需列
|
||
*/
|
||
public static List<String> readHeadContent(MultipartFile file, int sheetNo, int headRowNumber) {
|
||
try {
|
||
//判断头部内容是否包含所有必填列
|
||
log.info(file.getInputStream()+"");
|
||
return readHeadContent(file.getInputStream(), sheetNo, headRowNumber);
|
||
} catch (IOException e) {
|
||
throw new BadRequestException("数据格式存在问题,无法读取");
|
||
}
|
||
}
|
||
|
||
public static List<ReadSheet> readSheets(MultipartFile file) {
|
||
try (ExcelReader excelReader = FastExcel.read(file.getInputStream()).build()) {
|
||
// 获取所有Sheet
|
||
List<ReadSheet> allSheets = excelReader.excelExecutor().sheetList();
|
||
|
||
// 使用 POI 检测隐的sheet
|
||
Set<String> hiddenSheetNames = getHiddenSheetNames(file);
|
||
|
||
//过滤隐藏的sheet
|
||
return allSheets.stream()
|
||
.filter(sheet -> !hiddenSheetNames.contains(sheet.getSheetName()))
|
||
.collect(Collectors.toList());
|
||
} catch (IOException e) {
|
||
log.error("读取Excel Sheet信息失败", e);
|
||
throw new BadRequestException("读取Excel Sheet信息失败");
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* 获取隐藏的Sheet名称
|
||
*
|
||
* @param file 文件
|
||
*/
|
||
private static Set<String> getHiddenSheetNames(MultipartFile file) {
|
||
Set<String> hiddenSheets = new HashSet<>();
|
||
|
||
try (InputStream inputStream = file.getInputStream();
|
||
Workbook workbook = WorkbookFactory.create(inputStream)) {
|
||
|
||
int numberOfSheets = workbook.getNumberOfSheets();
|
||
for (int i = 0; i < numberOfSheets; i++) {
|
||
if (workbook.isSheetHidden(i) || workbook.isSheetVeryHidden(i)) {
|
||
String sheetName = workbook.getSheetName(i);
|
||
hiddenSheets.add(sheetName);
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
throw new BadRequestException("获取隐藏的Sheet名称失败");
|
||
}
|
||
|
||
return hiddenSheets;
|
||
}
|
||
|
||
|
||
}
|