package com.youchain.utils; import cn.idev.excel.ExcelWriter; import cn.idev.excel.FastExcel; 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.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.File; 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 泛型 * @param 泛型 */ public static void batchExportExcel(HttpServletResponse response, String fileName, String sheetName, Class head, BiFunction> pageQueryFunction, Function convertFunction, ToLongFunction 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 batch = pageQueryFunction.apply(lastId, pageSize); if (batch.isEmpty()) { break; } List 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 数据类型 * @return 解析后的数据列表 */ public static List readExcelData(MultipartFile file, Class clazz, int sheetNo, int headRowNumber) { List 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; } /** * 读取Excel文件中指定位置的单元格值 * * @param file Excel文件 * @param reader 指定行和列 */ public static Map readExcelCellValues(MultipartFile file, FastExcelCellListener reader) { try { FastExcel.read(file.getInputStream(), reader) .sheet() .doReadSync(); } catch (IOException e) { throw new BadRequestException("数据格式存在问题,无法读取"); } return reader.getAllCellValues(); } /** * 读取 Excel 文件头部的所有内容(第一行) * * @param inputStream Excel 文件输入流 * @return 头部内容列表 */ public static List readHeadContent(InputStream inputStream, int sheetNo, int headRowNumber) { List 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 文件头部是否包含指定的列名 * * @param requiredColumns 必需的列名列表 * @return 验证结果,true 表示包含所有必需列 */ public static List readHeadContent(MultipartFile file, List requiredColumns, int sheetNo, int headRowNumber) { if (requiredColumns.isEmpty()) { return Collections.emptyList(); } try { //判断头部内容是否包含所有必填列 return readHeadContent(file.getInputStream(), sheetNo, headRowNumber); } catch (IOException e) { throw new BadRequestException("数据格式存在问题,无法读取"); } } private static String readHeadContent(File file, int headRowNumber) { return ""; } }