kcw-wx-java/youchain-system/src/main/java/com/youchain/utils/FastExcelUtil.java

294 lines
11 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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