no message
							parent
							
								
									62efd8ff9a
								
							
						
					
					
						commit
						140cfa1b5b
					
				| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
package com.youchain.utils;
 | 
			
		||||
 | 
			
		||||
import cn.idev.excel.context.AnalysisContext;
 | 
			
		||||
import cn.idev.excel.read.listener.ReadListener;
 | 
			
		||||
import com.youchain.exception.BadRequestException;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
 | 
			
		||||
public class FastExcelCellListener implements ReadListener<Map<Integer, String>> {
 | 
			
		||||
    private final Map<CellPosition, String> cellValues = new ConcurrentHashMap<>();
 | 
			
		||||
    private final Set<CellPosition> targetCells = new HashSet<>();
 | 
			
		||||
    private volatile boolean allCellsRead = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 添加目标单元格
 | 
			
		||||
     *
 | 
			
		||||
     * @param row 行索引(从1开始,Excel格式)
 | 
			
		||||
     * @param col 列索引(从1开始,Excel格式)
 | 
			
		||||
     * @return 当前监听器实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public FastExcelCellListener addTargetCell(int row, int col) {
 | 
			
		||||
        if (row < 1 || col < 1) {
 | 
			
		||||
            throw new BadRequestException("行列索引必须从1开始");
 | 
			
		||||
        }
 | 
			
		||||
        targetCells.add(new CellPosition(row, col));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
 | 
			
		||||
        // 如果所有目标单元格已读取完毕,直接返回
 | 
			
		||||
        if (allCellsRead) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // EasyExcel 的行索引从0开始,转换为Excel格式(从1开始)
 | 
			
		||||
        int currentRowIndex = context.readRowHolder().getRowIndex() + 1;
 | 
			
		||||
 | 
			
		||||
        // 检查当前行是否包含目标单元格
 | 
			
		||||
        for (CellPosition targetCell : targetCells) {
 | 
			
		||||
            if (currentRowIndex == targetCell.row) {
 | 
			
		||||
                // 列索引需要减1转换为0-based
 | 
			
		||||
                String cellValue = rowData.get(targetCell.col - 1);
 | 
			
		||||
                if (cellValue != null) {
 | 
			
		||||
                    cellValues.put(targetCell, cellValue);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 检查是否所有目标单元格都已读取
 | 
			
		||||
        if (cellValues.size() == targetCells.size()) {
 | 
			
		||||
            allCellsRead = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
 | 
			
		||||
        // 可根据需要添加逻辑
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定单元格的值
 | 
			
		||||
     *
 | 
			
		||||
     * @param row 行索引(从1开始,Excel格式)
 | 
			
		||||
     * @param col 列索引(从1开始,Excel格式)
 | 
			
		||||
     * @return 单元格值,如果不存在返回null
 | 
			
		||||
     */
 | 
			
		||||
    public String getCellValue(int row, int col) {
 | 
			
		||||
        return cellValues.get(new CellPosition(row, col));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取所有单元格值(字符串格式的key)
 | 
			
		||||
     *
 | 
			
		||||
     * @return 所有单元格值的Map,key为"row-col"格式
 | 
			
		||||
     */
 | 
			
		||||
    public Map<String, String> getAllCellValues() {
 | 
			
		||||
        Map<String, String> result = new HashMap<>();
 | 
			
		||||
        cellValues.forEach((pos, value) ->
 | 
			
		||||
                result.put(pos.row + "-" + pos.col, value));
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 单元格位置封装类
 | 
			
		||||
     */
 | 
			
		||||
    public static class CellPosition {
 | 
			
		||||
        private final int row;
 | 
			
		||||
        private final int col;
 | 
			
		||||
 | 
			
		||||
        public CellPosition(int row, int col) {
 | 
			
		||||
            this.row = row;
 | 
			
		||||
            this.col = col;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean equals(Object o) {
 | 
			
		||||
            if (this == o) return true;
 | 
			
		||||
            if (o == null || getClass() != o.getClass()) return false;
 | 
			
		||||
            CellPosition that = (CellPosition) o;
 | 
			
		||||
            return row == that.row && col == that.col;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int hashCode() {
 | 
			
		||||
            return Objects.hash(row, col);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public String toString() {
 | 
			
		||||
            return "(" + row + "," + col + ")";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,167 @@
 | 
			
		|||
package com.youchain.utils;
 | 
			
		||||
 | 
			
		||||
import cn.idev.excel.FastExcel;
 | 
			
		||||
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.IOException;
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
import java.net.URLEncoder;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import static cn.hutool.core.util.CharsetUtil.UTF_8;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class FastExcelUtils {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置下载文件消息头
 | 
			
		||||
     *
 | 
			
		||||
     * @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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 从 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 读取Excel文件中指定位置的单元格值
 | 
			
		||||
     *
 | 
			
		||||
     * @param file   Excel文件
 | 
			
		||||
     * @param reader 指定行和列
 | 
			
		||||
     */
 | 
			
		||||
    public static Map<String, String> readExcelCellValues(MultipartFile file, FastExcelCellListener reader) {
 | 
			
		||||
        try {
 | 
			
		||||
            FastExcel.read(file.getInputStream(), reader)
 | 
			
		||||
                    .sheet()
 | 
			
		||||
                    .doReadSync();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            throw new BadRequestException("Excel文件读取失败,请检查文件格式是否正确");
 | 
			
		||||
        }
 | 
			
		||||
        return reader.getAllCellValues();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 读取 Excel 文件头部的所有内容(第一行)
 | 
			
		||||
     *
 | 
			
		||||
     * @param file Excel 文件
 | 
			
		||||
     * @return 回返指定目录指定行返回的内容
 | 
			
		||||
     */
 | 
			
		||||
    public static List<String> readHeadContent(MultipartFile file, int sheetNo, int headRowNumber) {
 | 
			
		||||
        List<Object> data;
 | 
			
		||||
        try {
 | 
			
		||||
            data = FastExcel.read(file.getInputStream())
 | 
			
		||||
                    .sheet(sheetNo)
 | 
			
		||||
                    .headRowNumber(headRowNumber)
 | 
			
		||||
                    .doReadSync();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            throw new BadRequestException("数据格式存在问题,无法读取");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 检查是否有数据
 | 
			
		||||
        if (CollectionUtils.isEmpty(data)) {
 | 
			
		||||
            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头部内容格式异常");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue