package com.*.common.core.utils.poi; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.metadata.WriteSheet; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.util.List; import java.util.Stack; import java.util.stream.Collectors; /** * Ⓕ大数据量excel 分表切片导出 * * @param <T> 操作类 10w+ */ public class ExcelBigDataUtil<T> implements AutoCloseable { private ExcelWriter excelWriter; // 当前操作sheet private WriteSheet sheets; // 当前sheet index 0开始 private Integer index = 0; // sheet分表名 private String[] sheetNames = null; // 单次最大写入数量 private final static Integer MAX_IN_SIZE = Integer.valueOf(5000); // 操作对象类型 private Class<T> target; // 文件完成状态 public Boolean isCompeted = false; // 缓存文件夹 private final static String bigExcelPath = "D:\\charts"; // 磁盘缓存路径 private String path; // 调用加载方式 true(进程内存 + 磁盘) private Boolean isCash; public ExcelBigDataUtil(String[] sheetNames, Class<T> target) { this.sheetNames = sheetNames; this.target = target; } /** * 初始配置(构造器) * * @param response 响应体 * @param target 操作类(含注解) * @param fileName 磁盘文件名(缓存通过此) * @param isCash false 拒绝采用之前统计文件 * @return 构建对象 * @throws IOException 磁盘读取失败 */ public static <T> ExcelBigDataUtil<T> config(HttpServletResponse response, Class<T> target, String fileName, Boolean isCash) throws IOException { return config(response,target,null,fileName,null,isCash); } /** * 初始配置(构造器) * * @param response 响应体 * @param target 操作类(含注解) * @param excelType 文件类型 * @param fileName 磁盘文件名(缓存通过此) * @param sheetNames 分表名(默认’分表+index') * @param isCash false 拒绝采用之前的统计文件 * @return 构建对象 * @throws IOException 磁盘读取失败 */ public static <T> ExcelBigDataUtil<T> config(HttpServletResponse response, Class<T> target, ExcelTypeEnum excelType, String fileName, String[] sheetNames, Boolean isCash) throws IOException { ExcelBigDataUtil util; if(isCash) { Assert.notNull(ExcelBigDataUtil.bigExcelPath, "路径属性未注入"); File dir = new File(bigExcelPath); if (!dir.canWrite()) throw new RuntimeException("文件路径无权限访问"); if (dir.isDirectory()) dir.mkdirs(); util = new ExcelBigDataUtil<>(sheetNames, target); util.isCash = true; // 是否从磁盘读取到文件 String path = ExcelBigDataUtil.bigExcelPath + "\\" + fileName + ".xlsx"; util.path = path; util.readByDisk(response,false); // 新生成文档操作对象 if(!util.isCompeted){ if(excelType == null) excelType = ExcelTypeEnum.XLSX; // 写入磁盘 util.excelWriter = EasyExcel.write(path, target).excelType(excelType).build(); } // 删除空文件 File file = new File(path); if (file.length() == 0) file.delete(); } else { // 直接加载进响应体 util = new ExcelBigDataUtil<>(sheetNames, target); util.isCash = false; // 新生成文档操作对象 if(excelType == null) excelType = ExcelTypeEnum.XLSX; util.excelWriter = EasyExcel.write(response.getOutputStream()).excelType(excelType).build(); } response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); return util; } /** * 数据分次提交 * * @param response 响应体 * @param data 切片数据 * @param isNext 是否换表 * @param isFinish 是否结束 * @throws IOException 磁盘读取失败 */ public void submit(HttpServletResponse response, List<T> data, Boolean isNext, Boolean isFinish) throws IOException { Assert.notNull(excelWriter,"使用需配置"); Assert.notNull(data,"空数据源"); Assert.notNull(data.size() > MAX_IN_SIZE ? null : true,"单次写入受限"); data = data.stream().filter(var -> !ObjectUtils.isEmpty(var)).collect(Collectors.toList()); String sheetName = sheetNames != null ? sheetNames[index] : ("分表" + (index + 1)); // 分表操作 WriteSheet point; // 接上表 point = next(null); // 换表 if(isNext || sheets == null){ ++index; point = EasyExcel.writerSheet(index,sheetName).head(target).build(); next(point); } // 写入数据 excelWriter.write(data, point); data = null; // 最终处理 if(isFinish) { this.readByDisk(response,true); } } /** * 操作分表栈 * * @param sheet 入栈元素 * @return 出栈元素 */ private WriteSheet next(WriteSheet sheet){ // 换表 if(sheet != null) sheets = sheet; // 不换表 if(sheets == null && sheet == null) return null; return sheets; } /** * 读取磁盘文件加载进响应体 * * @param response * @throws IOException */ private void readByDisk(HttpServletResponse response,Boolean isFinish) throws IOException{ if(isFinish != null && isFinish) excelWriter.finish(); if(!isCash || isCompeted) return; if(path == null) return; // 磁盘中存在文件 File file = new File(path); if(!file.isFile() || file.length() == 0) return; FileInputStream fileInputStream = new FileInputStream(file); response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); response.setContentLength((int) file.length()); // 缓冲区读取 BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); byte[] data = new byte[bufferedInputStream.available()]; bufferedInputStream.read(data); // 写入响应体 OutputStream outputStream = response.getOutputStream(); outputStream.write(data); // 关闭新建流 bufferedInputStream.close(); outputStream.flush(); outputStream.close(); this.isCompeted = true; } @Override public void close() { if(sheets != null) sheets = null; if(excelWriter != null) excelWriter.finish(); } }
百万数据切片导出工具类
于 2023-11-09 17:08:21 首次发布