2025-09-09 13:47:11 +08:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-20 10:34:59 +08:00
|
|
|
|
* 读取 Excel 指定目录指定行
|
2025-09-09 13:47:11 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @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) {
|
2025-09-20 10:34:59 +08:00
|
|
|
|
throw new BadRequestException("Excel 文件读取失败: " + e.getMessage());
|
2025-09-09 13:47:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否有数据
|
|
|
|
|
|
if (CollectionUtils.isEmpty(data)) {
|
|
|
|
|
|
throw new BadRequestException("数据为空");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-20 10:34:59 +08:00
|
|
|
|
// 获取并验证首行数据
|
2025-09-09 13:47:11 +08:00
|
|
|
|
Object firstRow = data.get(0);
|
2025-09-20 10:34:59 +08:00
|
|
|
|
if (!(firstRow instanceof Map)) {
|
2025-09-09 13:47:11 +08:00
|
|
|
|
throw new BadRequestException("读取Excel头部内容格式异常");
|
|
|
|
|
|
}
|
2025-09-20 10:34:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 将头部内容转换为字符串列表
|
|
|
|
|
|
Map<?, ?> rowMap = (Map<?, ?>) firstRow;
|
|
|
|
|
|
return rowMap.values().stream()
|
|
|
|
|
|
.map(value -> value == null ? "" : value.toString())
|
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
2025-09-09 13:47:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|