前端JavaScript通过ajax请求下载Blob类型文件(XMLHttpRequest)
1.前端post请求导出文件
exportFile = function(params, url) {
let xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8');
xhr.responseType = "blob"; // 返回类型blob
xhr.send(JSON.stringify(params)); // 发送ajax请求
xhr.onreadystatechange = function () {
// 监听属性xhr.readyState每次变化时会触发
if (xhr.readyState === XMLHttpRequest.DONE) {
// 响应已返回
var code = decodeURI(xhr.getResponseHeader("code"));
console.log(code);
if (code.toString() === "0") {
$.messager.alert('提示', "操作成功!");
// 截取文件名,这里是后端返回了文件名+后缀,如果没有可以自己拼接
var fileName = decodeURI(xhr.getResponseHeader("content-disposition").split(";")[1].split("=")[1]);
console.log(fileName);
// 将`blob`对象转化成一个可访问的`url`
let dataUrl = window.URL.createObjectURL(new Blob([xhr.response]));
let link = document.createElement("a");
link.style.display = "none";
link.href = dataUrl;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
var msg = decodeURI(xhr.getResponseHeader("msg"));
$.messager.alert('提示', msg);
}
}
}
}
2.后台文件下载接口
2.1.Controller层
/**
* 导出商品明细
* @param req
* @return
*/
@RequestMapping(value = "/exportGoodsRecord.json", method = RequestMethod.POST)
public void exportGoodsRecord(@RequestBody GoodsReq req, HttpServletResponse response, HttpSession session) throws UnsupportedEncodingException {
try {
req.setUserId((String) session.getAttribute(Constant.USER_SESSION_KEY));
goodsService.exportGoodsRecord(req, response);
} catch (Exception e) {
log.error("导出商品明细异常", e);
response.setHeader("code", String.valueOf(HttpStatus.SC_INTERNAL_SERVER_ERROR));
response.setHeader("msg", URLEncoder.encode(e instanceof BusinessException ? ((BusinessException) e).getMsg() : "未知异常", StandardCharsets.UTF_8.toString()));
}
}
2.2.Service层
@Override
public void exportGoodsRecord(GoodsReq req, HttpServletResponse response) throws IOException {
goodsLogic.checkExistsOrElseThrows(req.getUserId(), req.getGoodsId());
GoodsEntity entity = goodsDao.selectByPrimaryKey(req.getGoodsId());
String fileName = String.format("%s_%s.%s", entity.getGoodsId(), System.currentTimeMillis(), "xlsx");
String dirPath = String.format("%s/%s", ConfigUtil.getConf("fps.filepath"), LocalDate.now().format(WeDateUtils.FMT_DATE_SHORT));
File dir = new File(dirPath);
if (!dir.exists()) {
dir.mkdirs();
}
File excelFile = new File(String.format("%s/%s", dirPath, fileName));
goodsLogic.generateDetailFile(excelFile, entity.getGoodsId());
FileUtil.writeResponse(excelFile, response);
}
2.3.Logic层
@Override
public void checkExistsOrElseThrows(String userId, String goodsId) {
GoodsEntity entity = dao.selectByPrimaryKey(goodsId);
if (entity == null) {
throw new BusinessException("商品ID不存在");
}
if (StringUtils.isBlank(userId)) {
throw new BusinessException("用户未登录");
}
}
@Override
public void generateDetailFile(File excelFile, String goodsId) throws IOException {
// 根据操作类型+卡密类型获取CardInfoTypeEnum
GoodsEntity goodsEntity = dao.selectByPrimaryKey(goodsId);
CardInfoTypeEnum cardInfoTypeEnum = CardInfoTypeEnum.getUniqueElseThrows(FileOperateType.EXPORT.getCode(),
StringUtils.defaultIfEmpty(goodsEntity.getSBizType(), "JD_CARD"), TemplateVersionEnum.VERSION1.getCode());
String sheetName = "卡券商品明细";
int batchSize = 1000;
int count = goodsRecordDao.selectCountByGoodsIdAndKind(goodsEntity.getGoodsId(), StringUtils.defaultIfEmpty(goodsEntity.getSBizType(), "JD_CARD"));
Function<Integer, List<GoodsRecordCellBo>> queryFunction = i -> {
List<GoodsRecordEntity> list = goodsRecordDao.selectListByGoodsIdAndKind(goodsEntity.getGoodsId(), StringUtils.defaultIfEmpty(goodsEntity.getSBizType(), "JD_CARD"), i * batchSize, batchSize);
List<GoodsRecordCellBo> returnList = Lists.newArrayList();
for (GoodsRecordEntity entity : list) {
GoodsRecordCellBo cell = new GoodsRecordCellBo();
BeanUtils.copyProperties(entity, cell);
cell.setStartTime(DateUtils.localTimeFormat(entity.getStartTime(), WeDateUtils.DATETIME_LONG));
cell.setEndTime(DateUtils.localTimeFormat(entity.getEndTime(), WeDateUtils.DATETIME_LONG));
cell.setState(String.format("%s-%s", entity.getState(), GoodsRecordStateEnum.get(entity.getState()).getRemark()));
returnList.add(cell);
}
return returnList;
};
// 获取卡密类型对应的源文件模板
Map<Integer, CellEnum> idxToCellMap = cardInfoTypeEnum.getRowCellEnumSupplier().get();
// 在源文件模板的末尾添加状态,订单id,用户id,用户类型单元格
idxToCellMap.put(idxToCellMap.keySet().stream().max(Integer::compareTo).orElseThrow(() -> new IllegalArgumentException("导出获取标题行异常")) + 1, CellEnum.STATE);
idxToCellMap.put(idxToCellMap.keySet().stream().max(Integer::compareTo).orElseThrow(() -> new IllegalArgumentException("导出获取标题行异常")) + 1, CellEnum.EMS_ORDER_ID);
idxToCellMap.put(idxToCellMap.keySet().stream().max(Integer::compareTo).orElseThrow(() -> new IllegalArgumentException("导出获取标题行异常")) + 1, CellEnum.USER_ID);
idxToCellMap.put(idxToCellMap.keySet().stream().max(Integer::compareTo).orElseThrow(() -> new IllegalArgumentException("导出获取标题行异常")) + 1, CellEnum.USER_TYPE);
// 获取导出模板的标题行名称、标题行字段
String[] headName = new String[idxToCellMap.size()];
String[] headField = new String[idxToCellMap.size()];
idxToCellMap.values().stream().map(CellEnum::getHeaderName).collect(Collectors.toList()).toArray(headName);
idxToCellMap.values().stream().map(CellEnum::getHeaderField).collect(Collectors.toList()).toArray(headField);
ExcelUtil.writeExcel(excelFile, sheetName, headName, headField, count, batchSize, queryFunction);
}
2.4.FileUtil工具类
public class FileUtil {
public static File cache(MultipartFile multipartFile, String cacheFileName) throws IOException {
String dirPath = String.format("%s/%s", ConfigUtil.getConf("fps.filepath"), LocalDate.now().format(WeDateUtils.FMT_DATE_SHORT));
File dir = new File(dirPath);
if (!dir.exists()) {
dir.mkdirs();
}
File cacheFile = new File(String.format("%s/%s", dirPath, cacheFileName));
multipartFile.transferTo(cacheFile);
return cacheFile;
}
public static String getExtensionElseThrows(MultipartFile file) {
String fileName = file.getOriginalFilename();
if (StringUtils.isBlank(fileName)) {
throw new RuntimeException("文件名为空");
}
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
public static void writeResponse(File file, HttpServletResponse response) {
OutputStream out = null;
try {
response.reset();
response.setContentType("text/plain;charset=UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
long fileLength = file.length();
String length = String.valueOf(fileLength);
response.setHeader("Content_Length", length);
response.setHeader("code", "0");
out = response.getOutputStream();
out.write(FileUtils.readFileToByteArray(file));
out.flush();
} catch (IOException e) {
OpLogUtil.logOpStepException("文件下载,操作结果返回", "异常", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
OpLogUtil.logOpStepException("关闭流", "异常", e);
}
}
}
}
}
2.5.导入导出模板枚举类
**
* 卡密类型枚举<PK:operateType+cardType+version>
* operateType: 0导入、1导出
* cardType: JD_CARD京东卡、XMLY喜马拉雅、SNW水泥网、QXB启信宝
* version: 1.0卡券导入历史模板、2.0卡券导入模板V2
* rowCellEnumSupplier:卡密类型对应文件模板的行定义(列号->单元格类型)
*/
public enum CardInfoTypeEnum {
IMPORT_JD_CARD("0", "京东卡1.0模板导入", "JD_CARD", "1.0", () -> {
Map<Integer, CellEnum> jdCardHeaderPredicate = new TreeMap<>();
// 卡号
jdCardHeaderPredicate.put(0, CellEnum.CARD_NO);
// 卡密
jdCardHeaderPredicate.put(1, CellEnum.CARD_PWD);
// 面额
jdCardHeaderPredicate.put(2, CellEnum.FACE_VALUE);
// 有效起始时间
jdCardHeaderPredicate.put(3, CellEnum.START_TIME);
// 有效结束时间
jdCardHeaderPredicate.put(4, CellEnum.END_TIME);
return jdCardHeaderPredicate;
}),
IMPORT_JD_CARD_V2("0", "京东卡2.0模板导入", "JD_CARD", "2.0", () -> {
Map<Integer, CellEnum> jdCardV2HeaderPredicate = new TreeMap<>();
// 卡号
jdCardV2HeaderPredicate.put(0, CellEnum.CARD_NO);
// 卡密
jdCardV2HeaderPredicate.put(1, CellEnum.CARD_PWD);
// 有效起始时间
jdCardV2HeaderPredicate.put(2, CellEnum.START_TIME);
// 有效结束时间
jdCardV2HeaderPredicate.put(3, CellEnum.END_TIME);
return jdCardV2HeaderPredicate;
}),
IMPORT_XMLY_V2("0", "喜马拉雅2.0模板导入", "XMLY", "2.0", () -> {
Map<Integer, CellEnum> xmlyCardV2HeaderPredicate = new TreeMap<>();
// 卡号
xmlyCardV2HeaderPredicate.put(0, CellEnum.CARD_NO);
// 卡密
xmlyCardV2HeaderPredicate.put(1, CellEnum.CARD_PWD);
// 有效起始时间
xmlyCardV2HeaderPredicate.put(2, CellEnum.START_TIME);
// 有效结束时间
xmlyCardV2HeaderPredicate.put(3, CellEnum.END_TIME);
return xmlyCardV2HeaderPredicate;
}),
IMPORT_SNW_V2("0", "水泥网2.0模板导入", "SNW", "2.0", () -> {
Map<Integer, CellEnum> snwCardV2HeaderPredicate = new TreeMap<>();
// 卡号
snwCardV2HeaderPredicate.put(0, CellEnum.CARD_NO);
// 卡密
snwCardV2HeaderPredicate.put(1, CellEnum.CARD_PWD);
// 有效起始时间
snwCardV2HeaderPredicate.put(2, CellEnum.START_TIME);
// 有效结束时间
snwCardV2HeaderPredicate.put(3, CellEnum.END_TIME);
return snwCardV2HeaderPredicate;
}),
IMPORT_QXB_V2("0", "启信宝2.0模板导入", "QXB", "2.0", () -> {
Map<Integer, CellEnum> qxbCardV2HeaderPredicate = new TreeMap<>();
// 卡号
qxbCardV2HeaderPredicate.put(0, CellEnum.CARD_NO);
// 卡密
qxbCardV2HeaderPredicate.put(1, CellEnum.CARD_PWD);
// 有效起始时间
qxbCardV2HeaderPredicate.put(2, CellEnum.START_TIME);
// 有效结束时间
qxbCardV2HeaderPredicate.put(3, CellEnum.END_TIME);
return qxbCardV2HeaderPredicate;
}),
EXPORT_JD_CARD("1", "京东卡明细导出", "JD_CARD", "1.0", () -> {
Map<Integer, CellEnum> exJdCardHeaderPredicate = new TreeMap<>();
// 卡号
exJdCardHeaderPredicate.put(0, CellEnum.CARD_NO);
// 有效起始时间
exJdCardHeaderPredicate.put(1, CellEnum.START_TIME);
// 有效结束时间
exJdCardHeaderPredicate.put(2, CellEnum.END_TIME);
return exJdCardHeaderPredicate;
}),
EXPORT_XMLY("1", "喜马拉雅明细导出", "XMLY", "1.0", () -> {
Map<Integer, CellEnum> exXmlyCardHeaderPredicate = new TreeMap<>();
// 卡号
exXmlyCardHeaderPredicate.put(0, CellEnum.CARD_NO);
// 有效起始时间
exXmlyCardHeaderPredicate.put(1, CellEnum.START_TIME);
// 有效结束时间
exXmlyCardHeaderPredicate.put(2, CellEnum.END_TIME);
return exXmlyCardHeaderPredicate;
}),
EXPORT_SNW("1", "水泥网明细导出", "SNW", "1.0", () -> {
Map<Integer, CellEnum> exSnwCardHeaderPredicate = new TreeMap<>();
// 卡号
exSnwCardHeaderPredicate.put(0, CellEnum.CARD_NO);
// 有效起始时间
exSnwCardHeaderPredicate.put(1, CellEnum.START_TIME);
// 有效结束时间
exSnwCardHeaderPredicate.put(2, CellEnum.END_TIME);
return exSnwCardHeaderPredicate;
}),
EXPORT_QXB("1", "启信宝明细导出", "QXB", "1.0", () -> {
Map<Integer, CellEnum> exQxbCardHeaderPredicate = new TreeMap<>();
// 卡号
exQxbCardHeaderPredicate.put(0, CellEnum.CARD_NO);
// 有效起始时间
exQxbCardHeaderPredicate.put(1, CellEnum.START_TIME);
// 有效结束时间
exQxbCardHeaderPredicate.put(2, CellEnum.END_TIME);
return exQxbCardHeaderPredicate;
});
private String operateType;
private String cardTypeDesc;
private String cardType;
private String version;
private Supplier<Map<Integer, CellEnum>> rowCellEnumSupplier;
public static CardInfoTypeEnum getUniqueElseThrows(String operateType, String cardType, String version) {
return Arrays.stream(values())
.filter(e -> e.getOperateType().equals(operateType) && e.getCardType().equals(cardType) && e.getVersion().equals(version))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(String.format("卡券产品%s未找到模板类型%s", cardType, version)));
}
public static String getCardInfoDescDefaultBlank(String operateType, String cardType, String version) {
Optional<CardInfoTypeEnum> optional = Arrays.stream(values())
.filter(e -> e.getOperateType().equals(operateType) && e.getCardType().equals(cardType) && e.getVersion().equals(version))
.findFirst();
if (optional.isPresent()) {
return optional.get().getCardTypeDesc();
}
return StringUtils.EMPTY;
}
CardInfoTypeEnum(String cardTypeDesc, String cardType, String version, Supplier<Map<Integer, CellEnum>> rowCellEnumSupplier) {
this.cardTypeDesc = cardTypeDesc;
this.cardType = cardType;
this.version = version;
this.rowCellEnumSupplier = rowCellEnumSupplier;
}
CardInfoTypeEnum(String operateType, String cardTypeDesc, String cardType, String version, Supplier<Map<Integer, CellEnum>> rowCellEnumSupplier) {
this.operateType = operateType;
this.cardTypeDesc = cardTypeDesc;
this.cardType = cardType;
this.version = version;
this.rowCellEnumSupplier = rowCellEnumSupplier;
}
public String getOperateType() {
return operateType;
}
public String getCardTypeDesc() {
return cardTypeDesc;
}
public String getCardType() {
return cardType;
}
public String getVersion() {
return version;
}
public Supplier<Map<Integer, CellEnum>> getRowCellEnumSupplier() {
return rowCellEnumSupplier;
}
}
2.6.单元格枚举类
/**
* 单元格枚举
* headerName:单元格所属标题列名
* headerField:单元格所属列映射POJO字段
* cellPredicate:单元格内容检查规则
* copyPropsConsumer:单元格转DTO实体
* Create by v_wbqsliu on 2021/8/2
*/
public enum CellEnum {
CARD_NO("卡号", "cardNo", ExcelUtil.cellStrNotBlank(), (cell, object) -> {
CardInfoDto dto = (CardInfoDto) object;
dto.setCardNo(ExcelUtil.parseStr(cell, null));
}),
CARD_PWD("卡密", "cardPwd", ExcelUtil.cellStrNotBlank(), (cell, object) -> {
CardInfoDto dto = (CardInfoDto) object;
dto.setCardPwd(ExcelUtil.parseStr(cell, null));
}),
FACE_VALUE("面额", "faceValue", ExcelUtil.cellPositiveDouble(), (cell, object) -> {
CardInfoDto dto = (CardInfoDto) object;
dto.setFaceValue(ExcelUtil.parseStr(cell, null));
}),
START_TIME("有效起始时间", "startTime", ExcelUtil.cellStrLegalDate(), (cell, object) -> {
CardInfoDto dto = (CardInfoDto) object;
dto.setStartTime(ExcelUtil.parseStr(cell, null));
}),
END_TIME("有效结束时间", "endTime", ExcelUtil.cellStrLegalDate(), (cell, object) -> {
CardInfoDto dto = (CardInfoDto) object;
dto.setEndTime(ExcelUtil.parseStr(cell, null));
}),
STATE("状态", "state", e -> true, (cell, object) -> {
}),
EMS_ORDER_ID("订单ID", "emsOrderId", e -> true, (cell, object) -> {
}),
USER_ID("用户ID", "userId", e -> true, (cell, object) -> {
}),
USER_TYPE("用户类型", "userType", e -> true, (cell, object) -> {
});
private String headerName;
private String headerField;
private Predicate<Cell> cellPredicate;
private BiConsumer<Cell, Object> copyPropsConsumer;
CellEnum(String headerName, String headerField, Predicate<Cell> cellPredicate, BiConsumer<Cell, Object> copyPropsConsumer) {
this.headerName = headerName;
this.headerField = headerField;
this.cellPredicate = cellPredicate;
this.copyPropsConsumer = copyPropsConsumer;
}
public String getHeaderName() {
return headerName;
}
public String getHeaderField() {
return headerField;
}
public Predicate<Cell> getCellPredicate() {
return cellPredicate;
}
public BiConsumer<Cell, Object> getCopyPropsConsumer() {
return copyPropsConsumer;
}
}
附:ExcelUtil工具类
1.集成了Excel文件的读取和写入、内容解析和检查相关操作
public class ExcelUtil {
private static final String XLS = "xls";
private static final String XLSX = "xlsx";
public static final String PATTERN_ZERO_DIGIT = "0";
public static final int MAX_ROWS_LENGTH = 60000;
public static Set<String> workbookWrongIdxs(File localFile, Map<Integer, CellEnum> cellPredicateMap) throws IOException {
return ExcelUtil.traverse(localFile, (Function<Workbook, Set<String>>) workbook -> wrongSheetRows(workbook, row -> row != null && row.getRowNum() == 0, row -> hasError(row, cellPredicateMap)));
}
public static boolean workbookSeekTitle(File localFile, String title) throws IOException {
return ExcelUtil.traverse(localFile, (Function<Workbook, Boolean>) workbook -> hasCertainTitle(workbook, cell -> StringUtils.equals(title, parseStr(cell, null))));
}
public static Integer workbookTotalRows(File localFile) throws IOException {
return ExcelUtil.traverse(localFile, (Function<Workbook, Integer>) workbook -> totalRows(workbook, row -> row != null && row.getRowNum() == 0));
}
public static <T> T traverse(File excelFile, Function<Workbook, T> function)
throws IOException {
Workbook workbook = null;
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(excelFile);
workbook = getWorkbook(inputStream, excelFile);
return function.apply(workbook);
} finally {
try {
if (null != workbook) {
workbook.close();
}
if (null != inputStream) {
inputStream.close();
}
} catch (Exception e) {
OpLogUtil.logOpStepException("关闭流", "异常", e);
}
}
}
public static boolean traverse(File excelFile, Consumer<Workbook> consumer) throws IOException {
Workbook workbook = null;
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(excelFile);
workbook = getWorkbook(excelFile);
consumer.accept(workbook);
workbook.write(outputStream);
return true;
} finally {
try {
if (null != workbook) {
workbook.close();
}
if (null != outputStream) {
outputStream.close();
}
} catch (Exception e) {
OpLogUtil.logOpStepException("关闭流", "异常", e);
}
}
}
public static <T> boolean writeExcel(File excelFile, String sheetName, String[] headName, String[] headKey, int count,
int batchSize, Function<Integer, List<T>> queryFunction) throws IOException {
return ExcelUtil.traverse(excelFile, workbook -> {
// 生成表头
generateExcelTitle(workbook, sheetName, headName, headKey, count);
// 生成数据
int times = count / batchSize + 1;// 需要写入次数
for (int i = 0; i < times; i++) {
// 从sheetName页第batchSize*i+1追加写入数据
int startRow = batchSize * i + 1;
List<T> dataList = queryFunction.apply(i);
generateExcelData(workbook, sheetName, headKey, dataList, startRow);
}
});
}
public static void generateExcelTitle(Workbook workbook, String sheetName, String[] headName, String[] headKey, int count) {
if (count <= 0) {
throw new BusinessException("数据为空,无需生成");
}
if (headName.length == 0 || headKey.length == 0) {
throw new BusinessException("表头为空,无需生成");
}
if (headName.length != headKey.length) {
throw new BusinessException("表头数与要求生成数不一致");
}
writeSheetRows(workbook, sheetName, Collections.singletonList(Arrays.asList(headName)), 0);
}
public static void generateExcelData(Workbook workbook, String sheetName, String[] headKey, List dataList, int startRow) {
List<List<Object>> dataObjList = new ArrayList<>();
try {
Iterator iterator = dataList.listIterator();
while (iterator.hasNext()) {
List<Object> data = new ArrayList<>();
Object obj = iterator.next();
Field[] fields = obj.getClass().getDeclaredFields();
for (int j = 0; j < headKey.length; j++) {
for (int i = 0; i < fields.length; i++) {
if (fields[i].getName().equals(headKey[j])) {
fields[i].setAccessible(true);
if (fields[i].get(obj) == null) {
data.add("");
break;
}
data.add(fields[i].get(obj).toString());
break;
}
}
}
dataObjList.add(data);
}
} catch (Exception e) {
OpLogUtil.logOpStepException("Excel数据转换", "异常", e);
}
writeSheetRows(workbook, sheetName, dataObjList, startRow);
}
private static void writeRow(List<Object> rowObj, Row row) {
for (int i = 0; i < rowObj.size(); i++) {
row.createCell((short) i).setCellValue(rowObj.get(i).toString());
}
}
/**
* 由于xls表格页有最大长度的限制(65535),因此每6w条数据分成一页
*
* 说明:startRow(开始行号)必须大于等于currRows(当前sheet页已有数据行数),否则,
* 条件i + currRows >= MAX_ROWS_LENGTH 一直满足,递归方法进入死循环,导致 StackOverflowError
*
* @param workbook 工作簿
* @param sheetName sheet名称
* @param dataObjList 待处理数据
* @param startRow 开始行号
*/
private static void writeSheetRows(Workbook workbook, String sheetName, List<List<Object>> dataObjList, int startRow) {
String newSheetName = startRow < MAX_ROWS_LENGTH ? sheetName : sheetName + startRow / MAX_ROWS_LENGTH;
Sheet sheet = workbook.getSheet(newSheetName);
if (sheet == null) {
sheet = workbook.createSheet(newSheetName);
sheet.setDefaultColumnWidth(20);
sheet.setDefaultRowHeight((short) (2 * 256));
}
int currRows = sheet.getPhysicalNumberOfRows();
for (int i = 0; i < dataObjList.size(); i++) {
if (i + currRows >= MAX_ROWS_LENGTH) {
writeSheetRows(workbook, sheetName,
new ArrayList(dataObjList.subList(i, dataObjList.size())), i + startRow);
break;
}
List<Object> rowObj = dataObjList.get(i);
Row row = sheet.createRow(i + currRows);
ExcelUtil.writeRow(rowObj, row);
}
}
public static <T> List<T> parseWorkbookData(Workbook workbook, Predicate<Row> shouldSkip, Function<Row, T> rowTransform) {
List<T> list = new ArrayList<>();
for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
// 获取sheet
Sheet sheet = workbook.getSheetAt(sheetNum);
if (sheet == null) {
continue;
}
// 遍历行
int firstRow = sheet.getFirstRowNum();
int endRow = sheet.getPhysicalNumberOfRows();
for (int rowNum = firstRow; rowNum < endRow; rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null || shouldSkip.test(row)) {
continue;
}
list.add(rowTransform.apply(row));
}
}
return list;
}
public static Predicate<Cell> cellStrNotBlank() {
return cell -> {
String str = ExcelUtil.cellToString(cell, ExcelUtil.PATTERN_ZERO_DIGIT);
return StringUtils.isNotBlank(str);
};
}
public static Predicate<Cell> cellPositiveDouble() {
return cell -> {
String str = ExcelUtil.cellToString(cell, null);
return StringUtils.isNotBlank(str) && Double.parseDouble(str) > 0;
};
}
public static Predicate<Cell> cellStrLegalDate() {
return cell -> {
String str = ExcelUtil.cellToString(cell, null);
String pattern = "^(\\d{4}-\\d{2}-\\d{2})(\\s\\d{2}:\\d{2}:\\d{2}){0,1}$";
return StringUtils.isBlank(str) || Pattern.compile(pattern).matcher(str).matches();
};
}
public static String parseStr(Cell cell) {
return ExcelUtil.cellToString(cell, ExcelUtil.PATTERN_ZERO_DIGIT);
}
public static String parseStr(Cell cell, String decimalFormatPattern) {
return ExcelUtil.cellToString(cell, decimalFormatPattern);
}
private static String cellToString(Cell cell, String decimalFormatPattern) {
if (cell == null) {
return null;
}
String returnValue = null;
switch (cell.getCellType()) {
case NUMERIC:
Double doubleValue = cell.getNumericCellValue();
if (StringUtils.isBlank(decimalFormatPattern)) {
return String.valueOf(doubleValue);
}
DecimalFormat df = new DecimalFormat(decimalFormatPattern);
returnValue = df.format(doubleValue);
break;
case STRING:
returnValue = cell.getStringCellValue();
break;
case BOOLEAN:
boolean booleanValue = cell.getBooleanCellValue();
returnValue = Boolean.toString(booleanValue);
break;
case FORMULA:
returnValue = cell.getCellFormula();
break;
default:
break;
}
return returnValue;
}
private static Workbook getWorkbook(InputStream inputStream, File excelFile) throws IOException {
String extension = excelFile.getAbsolutePath().substring(excelFile.getAbsolutePath().lastIndexOf(".") + 1);
switch (extension) {
case XLS:
return new HSSFWorkbook(inputStream);
case XLSX:
return new XSSFWorkbook(inputStream);
default:
throw new IllegalArgumentException(String.format("%s|%s", "EXCEL文件扩展名有误", extension));
}
}
private static Workbook getWorkbook(File excelFile) {
String extension = excelFile.getAbsolutePath().substring(excelFile.getAbsolutePath().lastIndexOf(".") + 1);
switch (extension) {
case XLS:
return new HSSFWorkbook();
case XLSX:
return new SXSSFWorkbook();//写入文件时使用流式版XFFS,防止大文件导出时内存溢出
default:
throw new IllegalArgumentException(String.format("%s|%s", "EXCEL文件扩展名有误", extension));
}
}
private static boolean hasCertainTitle(Workbook workbook, Predicate<Cell> hasCertain) {
for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
// 获取sheet
Sheet sheet = workbook.getSheetAt(sheetNum);
if (sheet == null) {
continue;
}
// 获取表头
Row row = sheet.getRow(0);
if (row != null) {
Cell cell = null;
int cellIdx = 0;
Iterator<Cell> iterator = row.cellIterator();
while (iterator.hasNext()) {
cell = iterator.next();
if (hasCertain.test(cell)) {
return true;
}
cellIdx++;
}
}
}
return false;
}
private static Set<String> wrongSheetRows(Workbook workbook, Predicate<Row> shouldSkip, Predicate<Row> hasError) {
Set<String> errorSheetRow = new HashSet<>();
for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
// 获取sheet
Sheet sheet = workbook.getSheetAt(sheetNum);
if (sheet == null) {
continue;
}
// 遍历行
int firstRow = sheet.getFirstRowNum();
int endRow = sheet.getPhysicalNumberOfRows();
for (int rowNum = firstRow; rowNum < endRow; rowNum++) {
Row row = sheet.getRow(rowNum);
if (shouldSkip.test(row)) {
continue;
}
if (hasError.test(row)) {
errorSheetRow.add(String.format("sheet%s_row%s:%s", sheetNum, rowNum, ExcelUtil.rowContent(row)));
}
}
}
return errorSheetRow;
}
private static Set<String> wrongSheetRows(Workbook workbook, Predicate<Row> shouldSkip, Predicate<Row> hasError) {
Set<String> errorSheetRow = new HashSet<>();
for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
// 获取sheet
Sheet sheet = workbook.getSheetAt(sheetNum);
if (sheet == null) {
continue;
}
// 遍历行
int firstRow = sheet.getFirstRowNum();
int endRow = sheet.getPhysicalNumberOfRows();
for (int rowNum = firstRow; rowNum < endRow; rowNum++) {
Row row = sheet.getRow(rowNum);
if (shouldSkip.test(row)) {
continue;
}
if (hasError.test(row)) {
errorSheetRow.add(String.format("sheet%s_row%s:%s", sheetNum, rowNum, ExcelUtil.rowContent(row)));
}
}
}
return errorSheetRow;
}
private static String rowContent(Row row) {
StringBuilder sb = new StringBuilder();
row.forEach(cell -> sb.append(cellToString(cell, null)).append("|"));
return sb.toString();
}
public static boolean hasError(Row row, Map<Integer/*cellIdx*/, CellEnum> cellPredicateMap) {
Cell cell = null;
try {
int cellIdx = 0;
Iterator<Cell> iterator = row.cellIterator();
while (iterator.hasNext()) {
cell = iterator.next();
Optional<CellEnum> optional = Optional.ofNullable(cellPredicateMap.get(cellIdx));
if (optional.isPresent() && !optional.get().getCellPredicate().test(cell)) {
return true;
}
cellIdx++;
}
} catch (Exception e) {
OpLogUtil.logOpStepException("Excel行Cell检查", "异常", e, cell);
return true;
}
return false;
}
}
2.ExcelUtil工具类使用说明
Excel工具类的相关使用说明,请参考链接:Java生成并下载Excel文件-工具类
参考文献链接:
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/getResponseHeader
本文来自博客园,作者:冰枫丶,转载请注明原文链接:https://www.cnblogs.com/lqsblog/p/15108059.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· DeepSeek “源神”启动!「GitHub 热点速览」
· 上周热点回顾(2.17-2.23)