easyexcel案例之类型转换,合并单元格,批注,下拉框,导入校验

news2024/11/17 4:28:03

文章目录

  • easyexcel案例之类型转换,合并单元格,批注,下拉框,导入校验
    • 一、依赖
    • 二、导出
      • 1.类型转换导出
      • 2.自定义文件标题
      • 3.合并单元格导出
        • 注解方式
        • 通过 registerWriteHandler 方法注册进去
        • 自定义合并规则进行合并
      • 4.批注和下拉框导出
        • 批注信息类
        • 自定义批注处理器
        • 自定义下拉框处理器
        • 工具类
        • 测试
    • 三、导入
      • 1、修改导入模板实体类
      • 2、添加expiringmap依赖
      • 3、自定义监听器处理导入数据
      • 4、读取文件和错误信息下载

easyexcel案例之类型转换,合并单元格,批注,下拉框,导入校验

一、依赖

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.10</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.3</version>
        </dependency>

二、导出

1.类型转换导出

工具类

package com.hl.easyexcel.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.hl.easyexcel.util.ExcelListener;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.web.multipart.MultipartFile;
 
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
 
@Slf4j
public class ExcelUtil {
    /**
     * 导出数据为excel文件
     *
     * @param filename       文件名称
     * @param dataResult     集合内的bean对象类型要与clazz参数一致
     * @param clazz          集合内的bean对象类型要与clazz参数一致
     * @param response       HttpServlet响应对象
     */
    public static void export(String filename, String sheetName,List<?> dataResult, Class<?> clazz, HttpServletResponse response) {
        response.setStatus(200);
        OutputStream outputStream = null;
        try {
            if (StringUtils.isBlank(filename)) {
                throw new RuntimeException("'filename' 不能为空");
            }
            String fileName = filename.concat(".xlsx");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            outputStream = response.getOutputStream();


            EasyExcel.write(response.getOutputStream(), clazz)
                    .registerWriteHandler(getStyleStrategy())
                    // 导出文件名
                    .autoCloseStream(Boolean.TRUE).sheet(sheetName)
                    .doWrite(dataResult);
 
        } catch (Exception e) {
            log.error("导出excel数据异常:", e);
            throw new RuntimeException(e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    log.error("导出数据关闭流异常", e);
                }
            }
        }
    }

 
    /**
     *  设置表格内容居中显示策略
     */
    public static HorizontalCellStyleStrategy getStyleStrategy(){
        // 这里需要设置不关闭流
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //设置背景颜色
         headWriteCellStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
        //设置头字体
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short)15);
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        //设置头居中
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        //内容策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        WriteFont headWriteFont2 = new WriteFont();
        headWriteFont2.setFontHeightInPoints((short)13);
        //设置 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 垂直居中
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }
}

导出实体类

package com.hl.easyexcel.domain;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.hl.easyexcel.controller.CustomSexConverterHandler;
import lombok.Data;
import lombok.experimental.Accessors;
 
import java.io.Serializable;
import java.util.Date;

/**
 * 资质信息导出实体
 */
@Data
@Accessors(chain = true) //Lombok注解,链式赋值使用
public class ExportExcelVo implements Serializable {
    private static final long serialVersionUID = 1L;
 
    @ColumnWidth(25)
    @ExcelProperty(value = {"企业信息", "企业名称"}, index = 0)
    private String name;
 
    @ColumnWidth(25)
    @ExcelProperty(value = {"企业信息", "社会统一信用代码"}, index = 1)
    private String creditCode;
 
    @ColumnWidth(15)
    @ExcelProperty(value = {"企业信息","公司法人"}, index = 2)
    private String legalPerson;

    @ColumnWidth(15)
    @ExcelProperty(value = {"企业信息","性别"}, index = 3,converter = CustomSexConverterHandler.class)
    private Integer gender;
 
    @ExcelProperty(value = {"企业信息","区域"}, index = 4)
    private String province;

    @ColumnWidth(25)
    @DateTimeFormat("yyyy年MM月dd日 HH时mm分ss秒")
    @ExcelProperty(value = {"企业信息","录入时间"}, index = 5)
    private Date createTime;
 
    @ColumnWidth(15)
    @ExcelProperty(value = {"企业信息","公司股东"}, index = 6)
    private String stockholder;

    @ColumnWidth(25)
    @ExcelProperty(value = {"企业信息","企业联系方式"}, index = 7)
    private String contact;

    @NumberFormat("#.##%")
    @ColumnWidth(15)
    @ExcelProperty(value = {"企业信息","增长收益"},index = 8)
    private Double doubleData;
 
}

自定义性别类型转换器

package com.hl.easyexcel.controller;

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

/**
 * @ClassName CustomSexConverterHandler
 * @Description 自定义性别类型转换器
 * @Author hl
 * @Date 2022/12/28 10:39
 * @Version 1.0
 */
public class CustomSexConverterHandler implements Converter<Integer> {
    @Override
    public Class supportJavaTypeKey() {
        return Integer.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    /**
     * 导入
     */
    @Override
    public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
       return "男".equals(cellData.getStringValue()) ? 1 : 0;
    }

    /**
     * 导出
     */
    @Override
    public CellData convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        if (integer.equals(1)){
            return new CellData("男");
        }else {
            return new CellData("女");
        }
    }
}

controller中的请求方法

 /**
     * excel数据导出
     * @param size  导出条数, 也可以是用户需要导出数据的条件
     */
    @RequestMapping(value = "/export", method = RequestMethod.GET)
    public HttpResponseTemp<?> export(Long size, HttpServletResponse response){
        // 模拟根据条件在数据库查询数据
        ArrayList<ExportExcelVo> excelVos = new ArrayList<>();
        for (int i = 1; i <= size; i++) {
            ExportExcelVo excelVo = new ExportExcelVo();
            excelVo.setContact(String.valueOf(10000000000L + i));
            excelVo.setName("公司名称" + i);
            excelVo.setCreditCode("社会性用代码" + i);
            excelVo.setProvince("地区" + i);
            excelVo.setLegalPerson("法人" + i);
            excelVo.setStockholder("投资人" + i);
            excelVo.setCreateTime(new Date());
            if (i % 2 == 0){
                excelVo.setGender(1);
            }else {
                excelVo.setGender(0);
            }
            excelVo.setDoubleData(0.11);
            excelVos.add(excelVo);
        }
 
        String fileName = "数据导出" + System.currentTimeMillis();
 
        try {
            ExcelUtil.export(fileName,"数据", excelVos, ExportExcelVo.class, response);
        } catch (Exception e) {
            return ResultStat.SERVER_INTERNAL_ERROR.wrap("数据导出失败!" + e.getMessage());
        }
        return ResultStat.OK.wrap("数据导出成功");
    }

在这里插入图片描述

2.自定义文件标题

自定义文件表头名称处理器,这里需要合并多少列需要看情况

package com.hl.easyexcel.controller;

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

/**
 * @ClassName CustomerSheetWriteHandler
 * @Description 自定义文件表头名称
 * @Author hl
 * @Date 2022/12/28 13:58
 * @Version 1.0
 */
public class CustomSheetTitleHandler implements SheetWriteHandler {

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

        Workbook workbook = writeWorkbookHolder.getWorkbook();
        Sheet sheet = workbook.getSheetAt(0);
        //设置标题
        Row row2 = sheet.createRow(0);
        row2.setHeight((short) 800);
        Cell cell1 = row2.createCell(0);
        cell1.setCellValue("企业信息统计报表");
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setLocked(true);
        Font font = workbook.createFont();
        font.setBold(true);
        font.setFontHeight((short) 400);
        cellStyle.setFont(font);
        cell1.setCellStyle(cellStyle);
        //这里看有多少列就合并多少列
        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 8));

    }
}

请求接口和实体类

 @RequestMapping(value = "/export3", method = RequestMethod.GET)
    public HttpResponseTemp<?> export3(Long size, HttpServletResponse response){
        // 模拟根据条件在数据库查询数据
        ArrayList<ExportExcelVo2> excelVos = new ArrayList<>();
        for (int i = 1; i <= size; i++) {
            ExportExcelVo2 excelVo = new ExportExcelVo2();
            excelVo.setContact(String.valueOf(10000000000L + i));
            excelVo.setName("公司名称" + i);
            excelVo.setCreditCode("社会性用代码" + i);
            excelVo.setProvince("地区" + i);
            excelVo.setLegalPerson("法人" + i);
            excelVo.setStockholder("投资人" + i);
            excelVo.setCreateTime(new Date());
            if (i % 2 == 0){
                excelVo.setGender(1);
            }else {
                excelVo.setGender(0);
            }
            excelVo.setDoubleData(0.11);
            excelVos.add(excelVo);
        }

        String fileName = "数据导出" + System.currentTimeMillis();

        try {
            ExcelUtil.exportWithTitle(fileName,"数据", excelVos, ExportExcelVo2.class, response);
        } catch (Exception e) {
            return ResultStat.SERVER_INTERNAL_ERROR.wrap("数据导出失败!" + e.getMessage());
        }
        return ResultStat.OK.wrap("数据导出成功");
    }

@Data
@Accessors(chain = true) //Lombok注解,链式赋值使用
public class ExportExcelVo2 implements Serializable {
    private static final long serialVersionUID = 1L;

    @ColumnWidth(25)
    @ExcelProperty(value = {"企业名称"}, index = 0)
    private String name;

    @ColumnWidth(25)
    @ExcelProperty(value = {"社会统一信用代码"}, index = 1)
    private String creditCode;

    @ColumnWidth(15)
    @ExcelProperty(value = {"公司法人"}, index = 2)
    private String legalPerson;

    @ColumnWidth(15)
    @ExcelProperty(value = {"性别"}, index = 3,converter = CustomSexConverterHandler.class)
    private Integer gender;

    @ExcelProperty(value = {"区域"}, index = 4)
    private String province;

    @ColumnWidth(25)
    @DateTimeFormat("yyyy年MM月dd日 HH时mm分ss秒")
    @ExcelProperty(value = {"录入时间"}, index = 5)
    private Date createTime;

    @ColumnWidth(15)
    @ExcelProperty(value = {"公司股东"}, index = 6)
    private String stockholder;

    @ColumnWidth(25)
    @ExcelProperty(value = {"企业联系方式"}, index = 7)
    private String contact;

    @NumberFormat("#.##%")
    @ColumnWidth(15)
    @ExcelProperty(value = {"增长收益"},index = 8)
    private Double doubleData;
}

工具类

public static void exportWithTitle(String filename, String sheetName, ArrayList<ExportExcelVo2> dataResult, Class<ExportExcelVo2> clazz, HttpServletResponse response) {
        response.setStatus(200);
        OutputStream outputStream = null;
        try {
            if (StringUtils.isBlank(filename)) {
                throw new RuntimeException("'filename' 不能为空");
            }
            String fileName = filename.concat(".xlsx");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            outputStream = response.getOutputStream();

            EasyExcel.write(response.getOutputStream(), clazz)
                    .registerWriteHandler(new CustomSheetTitleHandler())//自定义表头文件名称
                    .registerWriteHandler(getStyleStrategy())
                    .relativeHeadRowIndex(1)//标题占了一行
                    // 导出文件名
                    .autoCloseStream(Boolean.TRUE).sheet(sheetName)
                    .doWrite(dataResult);
        } catch (Exception e) {
            log.error("导出excel数据异常:", e);
            throw new RuntimeException(e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    log.error("导出数据关闭流异常", e);
                }
            }
        }
    }

在这里插入图片描述

3.合并单元格导出

版本 2.2.6+

注解方式

注解这里只能使用一种,不可以合并多个,要合并多个使用下面那种方式

@Data
@Accessors(chain = true) //Lombok注解,链式赋值使用
// 将第3-7行的8列合并成一个单元格
@OnceAbsoluteMerge(firstRowIndex = 2, lastRowIndex = 6, firstColumnIndex = 8, lastColumnIndex = 8)
// 将第6-7行的2-3列合并成一个单元格
// @OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)
public class ExportExcelVo implements Serializable {}

在这里插入图片描述

在这里插入图片描述

通过 registerWriteHandler 方法注册进去

 EasyExcel.write(response.getOutputStream(), clazz)
                    .registerWriteHandler(getStyleStrategy())
                    //将第3-7行的8列合并成一个单元格
                    .registerWriteHandler(new OnceAbsoluteMergeStrategy(2,6,8,8))
                    //将第6-7行的2-3列合并成一个单元格
                    .registerWriteHandler(new OnceAbsoluteMergeStrategy(5,6,1,2))
                    // 导出文件名
                    .autoCloseStream(Boolean.TRUE).sheet(sheetName)
                    .doWrite(dataResult);

在这里插入图片描述

自定义合并规则进行合并

自定义合并策略

package com.hl.easyexcel.controller;

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

public class ExcelFillCellMergeStrategy implements CellWriteHandler {
    private int[] mergeColumnIndex;
    private int mergeRowIndex;

    public ExcelFillCellMergeStrategy() {
    }

    public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }

    public int[] getMergeColumnIndex() {
        return mergeColumnIndex;
    }

    public void setMergeColumnIndex(int[] mergeColumnIndex) {
        this.mergeColumnIndex = mergeColumnIndex;
    }

    public int getMergeRowIndex() {
        return mergeRowIndex;
    }

    public void setMergeRowIndex(int mergeRowIndex) {
        this.mergeRowIndex = mergeRowIndex;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
       //当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();

        if (curRowIndex > mergeRowIndex) {
            for (int columnIndex : mergeColumnIndex) {
                if (curColIndex == columnIndex) {
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }

    /**
     * 当前单元格向上合并
     * @param cell             当前单元格
     * @param curRowIndex      当前行
     * @param curColIndex      当前列
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        //获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并
        Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();

        // 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
        //
        if (curData.equals(preData)) {
            Sheet sheet = writeSheetHolder.getSheet();
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // 若上一个单元格未被合并,则新增合并单元
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }
}

工具类

 public static void exportMerge(String filename, String sheetName,List<?> dataResult, Class<?> clazz, HttpServletResponse response,ExcelFillCellMergeStrategy excelFillCellMergeStrategy) {
        response.setStatus(200);
        OutputStream outputStream = null;
        try {
            if (StringUtils.isBlank(filename)) {
                throw new RuntimeException("'filename' 不能为空");
            }
            String fileName = filename.concat(".xlsx");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            outputStream = response.getOutputStream();

            EasyExcel.write(response.getOutputStream(), clazz)
                    .registerWriteHandler(getStyleStrategy())
                    .registerWriteHandler(excelFillCellMergeStrategy)//合并策略
                    // 导出文件名
                    .autoCloseStream(Boolean.TRUE).sheet(sheetName)
                    .doWrite(dataResult);
        } catch (Exception e) {
            log.error("导出excel数据异常:", e);
            throw new RuntimeException(e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    log.error("导出数据关闭流异常", e);
                }
            }
        }
    }

测试

@RequestMapping(value = "/export2", method = RequestMethod.GET)
    public HttpResponseTemp<?> export2(Long size, HttpServletResponse response){
        // 模拟根据条件在数据库查询数据
        ArrayList<ExportExcelVo> excelVos = new ArrayList<>();
        for (int i = 1; i <= size; i++) {
            ExportExcelVo excelVo = new ExportExcelVo();
            excelVo.setContact(String.valueOf(10000000000L + i));
            excelVo.setName("公司名称" + i);
            excelVo.setCreditCode("社会性用代码" + i);
            excelVo.setProvince("地区" + i);
            excelVo.setLegalPerson("法人" + i);
            excelVo.setStockholder("投资人" + i);
            excelVo.setCreateTime(new Date());
            if (i == 1 || i ==2){
                excelVo.setGender(1);
                excelVo.setDoubleData(0.11);
            }else {
                excelVo.setGender(0);
                excelVo.setDoubleData(0.32);
            }
            excelVos.add(excelVo);
        }

        String fileName = "数据导出" + System.currentTimeMillis();

        try {
            //需要合并的列
            int[] mergeColumnIndex = {3,8};
            //从第几行后开始合并
            int mergeRowIndex = 2;
            //自定义合并策略
            ExcelFillCellMergeStrategy excelFillCellMergeStrategy = new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumnIndex);
            ExcelUtil.exportMerge(fileName,"数据", excelVos, ExportExcelVo.class, response,excelFillCellMergeStrategy);
        } catch (Exception e) {
            return ResultStat.SERVER_INTERNAL_ERROR.wrap("数据导出失败!" + e.getMessage());
        }
        return ResultStat.OK.wrap("数据导出成功");
    }

在这里插入图片描述

4.批注和下拉框导出

批注信息类

package com.hl.easyexcel.controller.anno;
 
import lombok.Data;
 
/**
 * 批注信息类
 *
 * @author xudongmaster
 */
@Data
public class CommentModel {
    /**
     * sheet页名称
     */
    private String sheetName;
    /**
     * 列索引
     */
    private int colIndex;
    /**
     * 行索引
     */
    private int rowIndex;
    /**
     * 行索引
     */
    private String commentContent;
 
    /**
     * 生成批注信息
     *
     * @param sheetName      sheet页名称
     * @param rowIndex       行号
     * @param columnIndex    列号
     * @param commentContent 批注内容
     * @return
     */
    public static CommentModel createCommentModel(String sheetName, int rowIndex, int columnIndex, String commentContent) {
        CommentModel commentModel = new CommentModel();
        //sheet页名称
        commentModel.setSheetName(sheetName);
        //行号
        commentModel.setRowIndex(rowIndex);
        //列号
        commentModel.setColIndex(columnIndex);
        //批注内容
        commentModel.setCommentContent(commentContent);
        return commentModel;
    }
}

自定义批注处理器

package com.hl.easyexcel.controller.anno;
 
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.write.handler.AbstractRowWriteHandler;
import com.alibaba.excel.write.metadata.holder.*;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;

import java.util.*;
import java.util.stream.Collectors;
 
/**
 * 自定义批注处理器
 *
 * @author xudongmaster
 */
public class CommentWriteHandler extends AbstractRowWriteHandler {
 
 
    /**
     * sheet页名称列表
     */
    private List<String> sheetNameList;
    List<CommentModel> commentList = new ArrayList<>();
 
    /**
     * 自定义批注适配器构造方法
     *
     * @param commentList 批注信息
     * @param extension   文件后缀(xlsx、xls)
     */
    public CommentWriteHandler(List<CommentModel> commentList, String extension) {
        if (CollUtil.isEmpty(commentList)) {
            return;
        }
        //文件不为指定的格式时,默认为Xlsx
        if (!StrUtil.equals(extension, "xlsx") && !StrUtil.equals(extension, "xls")) {
            extension = "xlsx";
        }
        this.commentList = commentList.stream().filter(x ->
                StrUtil.isNotBlank(x.getSheetName()) && x.getColIndex() >=0 && x.getRowIndex() >= 0 && StrUtil.isNotBlank(x.getCommentContent())
        ).collect(Collectors.toList());
        sheetNameList = this.commentList.stream().map(CommentModel::getSheetName).distinct().collect(Collectors.toList());
        this.extension = extension;
    }
 
    /**
     * 文档后缀名
     */
    private String extension;
 
    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
                                Integer relativeRowIndex, Boolean isHead) {
        Sheet sheet = writeSheetHolder.getSheet();
        //不需要添加批注,或者当前sheet页不需要添加批注
        if (CollUtil.isEmpty(commentList) || !sheetNameList.contains(sheet.getSheetName())) {
            return;
        }
        //获取当前行的批注信息
        List<CommentModel> rowCommentList = commentList.stream().filter(x ->
                StrUtil.equals(x.getSheetName(), sheet.getSheetName())
                        && Objects.equals(relativeRowIndex, x.getRowIndex())).collect(Collectors.toList());
        //当前行没有批注信息
        if (CollUtil.isEmpty(rowCommentList)) {
            return;
        }
        List<Integer> colIndexList = rowCommentList.stream().map(CommentModel::getColIndex).distinct().collect(Collectors.toList());
        for (Integer colIndex : colIndexList) {
            //同一单元格的批注信息
            List<CommentModel> cellCommentList = rowCommentList.stream().filter(x ->
                    Objects.equals(colIndex, x.getColIndex())).collect(Collectors.toList());
            if (CollUtil.isEmpty(cellCommentList)) {
                continue;
            }
            //批注内容拼成一条
            String commentContent = cellCommentList.stream().map(CommentModel::getCommentContent).collect(Collectors.joining());
            Cell cell = row.getCell(colIndex);
            addComment(cell, commentContent, extension);
        }
        //删除批注信息
        commentList.remove(rowCommentList);
        //重新获取要添加的sheet页姓名
        sheetNameList = commentList.stream().map(CommentModel::getSheetName).distinct().collect(Collectors.toList());
    }

    /**
     * 给Cell添加批注
     *
     * @param cell 单元格
     * @param value 批注内容
     * @param extension 扩展名
     */
    public static void addComment(Cell cell, String value, String extension) {
        Sheet sheet = cell.getSheet();
        cell.removeCellComment();
        if ("xls".equals(extension)) {
            ClientAnchor anchor = new HSSFClientAnchor();
            // 关键修改
            anchor.setDx1(0);
            anchor.setDx2(0);
            anchor.setDy1(0);
            anchor.setDy2(0);
            anchor.setCol1(cell.getColumnIndex());
            anchor.setRow1(cell.getRowIndex());
            anchor.setCol2(cell.getColumnIndex() + 3);
            anchor.setRow2(cell.getRowIndex() + 4);
            // 结束
            Drawing<?> drawing = sheet.createDrawingPatriarch();
            Comment comment = drawing.createCellComment(anchor);
            // 输入批注信息
            comment.setString(new HSSFRichTextString(value));
            // 将批注添加到单元格对象中
            cell.setCellComment(comment);
        } else if ("xlsx".equals(extension)) {
            ClientAnchor anchor = new XSSFClientAnchor();
            // 关键修改
            anchor.setDx1(0);
            anchor.setDx2(0);
            anchor.setDy1(0);
            anchor.setDy2(0);
            anchor.setCol1(cell.getColumnIndex());
            anchor.setRow1(cell.getRowIndex());
            anchor.setCol2(cell.getColumnIndex() + 3);
            anchor.setRow2(cell.getRowIndex() + 4);
            // 结束
            Drawing<?> drawing = sheet.createDrawingPatriarch();
            Comment comment = drawing.createCellComment(anchor);
            // 输入批注信息
            comment.setString(new XSSFRichTextString(value));
            // 将批注添加到单元格对象中
            cell.setCellComment(comment);
        }
    }
}

自定义下拉框处理器

package com.hl.easyexcel.controller;

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
 
import java.util.Map;
import java.util.TreeMap;
 
/**
 * 这个类的作用主要是给列增加下拉框
 * 主要是为了方便用户填写数据
 */
public class CustomSheetSelectHandler implements SheetWriteHandler {
 
    /**
     * 存放下拉内容的集合
     * key为列的下标, value为下拉内容数组
     */
    private final Map<Integer, String[]> map = new TreeMap<>();
 
    /**
     * 工作簿下标,从0开始
     */
    private int index = 0;
 
    /**
     * 给多少行添加下拉框,这里默认给2000行
     */
    private final int batchSize = 2000;
 
 
 
    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
 
    }
 
    /**
     * 宝藏在此:如果下拉框内容总的长度超过255,会导致Cell有下拉框,但是下拉内容显示不了,
     * 这时我们可以新建一个sheet,将其隐藏,然后将里面的内容引用到我们的下拉框列就可以。
     * 值得细品
     */
    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // excel下标从0开始,这里第二列的下拉选择内容
        map.put(2, new String[]{"张三", "李四"});
        // excel下标从0开始,这里第三列的下拉选择内容
        map.put(3, new String[]{"男", "女"});
 
        DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();
        map.forEach((k, v) -> {
            // 创建sheet,突破下拉框255的限制
            // 获取一个workbook
            Workbook workbook = writeWorkbookHolder.getWorkbook();
            // 定义sheet的名称
            String sheetName = "sheet" + k;
            // 1.创建一个隐藏的sheet 名称为 proviceSheet
            Sheet proviceSheet = workbook.createSheet(sheetName);
            // 从第二个工作簿开始隐藏
            this.index++;
            // 设置隐藏
            workbook.setSheetHidden(this.index, true);
            // 2.循环赋值(为了防止下拉框的行数与隐藏域的行数相对应,将隐藏域加到结束行之后)
            for (int i = 0, length = v.length; i < length; i++) {
                // i:表示你开始的行数 0表示你开始的列数
                proviceSheet.createRow(i).createCell(0).setCellValue(v[i]);
            }
            Name category1Name = workbook.createName();
            category1Name.setNameName(sheetName);
            // 4 $A$1:$A$N代表 以A列1行开始获取N行下拉数据
            category1Name.setRefersToFormula(sheetName + "!$A$1:$A$" + (v.length));
            // 5 将刚才设置的sheet引用到你的下拉列表中,1表示从行的序号1开始(开始行,通常行的序号为0的行是表头),50表示行的序号50(结束行),表示从行的序号1到50,k表示开始列序号和结束列序号
            CellRangeAddressList addressList = new CellRangeAddressList(1, batchSize, k, k);
            DataValidationConstraint constraint8 = helper.createFormulaListConstraint(sheetName);
            DataValidation dataValidation3 = helper.createValidation(constraint8, addressList);
 
            // 阻止输入非下拉选项的值
            dataValidation3.setErrorStyle(DataValidation.ErrorStyle.STOP);
            dataValidation3.setShowErrorBox(true);
            dataValidation3.setSuppressDropDownArrow(true);
            dataValidation3.createErrorBox("提示", "此值与单元格定义格式不一致");
            // validation.createPromptBox("填写说明:","填写内容只能为下拉数据集中的单位,其他单位将会导致无法入仓");
            writeSheetHolder.getSheet().addValidationData(dataValidation3);
        });
    }
}

工具类

public static void exportTemplate(String filename, String sheetName,List<?> dataResult, Class<?> clazz, HttpServletResponse response) {
        response.setStatus(200);
        OutputStream outputStream = null;
        try {
            if (StringUtils.isBlank(filename)) {
                throw new RuntimeException("'filename' 不能为空");
            }
            String fileName = filename.concat(".xlsx");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            outputStream = response.getOutputStream();

            List<CommentModel> commentList = new ArrayList<>();
            commentList.add(CommentModel.createCommentModel(sheetName, 1, 0, "第一条批注。"));
            commentList.add(CommentModel.createCommentModel(sheetName, 1, 1, "第二条批注。"));


            EasyExcel.write(response.getOutputStream(), clazz)
                    .registerWriteHandler(getStyleStrategy())
                    .registerWriteHandler(new CommentWriteHandler(commentList,"xlsx"))
                    .registerWriteHandler(new CustomSheetSelectHandler())
                    // 导出文件名
                    .autoCloseStream(Boolean.TRUE).sheet(sheetName)
                    .doWrite(dataResult);
        } catch (Exception e) {
            log.error("导出excel数据异常:", e);
            throw new RuntimeException(e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    log.error("导出数据关闭流异常", e);
                }
            }
        }
    }

测试

    @RequestMapping(value = "/template", method = RequestMethod.GET)
    public HttpResponseTemp<?> template(HttpServletResponse response){
        String fileName = "导入模板下载" + System.currentTimeMillis();
        try {
            ExcelUtil.exportTemplate(fileName,"模板", null, ImportExcelVo.class, response);
        } catch (Exception e) {
            return ResultStat.SERVER_INTERNAL_ERROR.wrap("模板下载失败" + e.getMessage());
        }
        return ResultStat.OK.wrap("模板下载成功!");
    }

@Data
public class ImportExcelVo implements Serializable {
    private static final long serialVersionUID = 1L;

    @ColumnWidth(25)
    @ExcelProperty(value = {"企业信息", "企业名称"}, index = 0)
    private String name;

    @ColumnWidth(25)
    @ExcelProperty(value = {"企业信息", "社会统一信用代码"}, index = 1)
    private String creditCode;

    @ColumnWidth(15)
    @ExcelProperty(value = {"企业信息","公司法人"}, index = 2)
    private String legalPerson;

    @ColumnWidth(15)
    @ExcelProperty(value = {"企业信息","性别"}, index = 3,converter = CustomSexConverterHandler.class)
    private Integer gender;

    @ExcelProperty(value = {"企业信息","区域"}, index = 4)
    private String province;

    @ColumnWidth(25)
    @DateTimeFormat("yyyy年MM月dd日 HH时mm分ss秒")
    @ExcelProperty(value = {"企业信息","录入时间"}, index = 5)
    private Date createTime;

    @ColumnWidth(15)
    @ExcelProperty(value = {"企业信息","公司股东"}, index = 6)
    private String stockholder;

    @ColumnWidth(25)
    @ExcelProperty(value = {"企业信息","企业联系方式"}, index = 7)
    private String contact;
}

在这里插入图片描述

三、导入

1、修改导入模板实体类

添加字段rowNum记录是哪一行数据,和重写equals和hashCode方法判断数据是否重复

@Data
public class ImportExcelVo implements Serializable {
    private static final long serialVersionUID = 1L;

    @ExcelIgnore
    private Integer rowNum;

    @ColumnWidth(25)
    @ExcelProperty(value = {"企业名称"}, index = 0)
    private String name;

    @ColumnWidth(25)
    @ExcelProperty(value = {"社会统一信用代码"}, index = 1)
    private String creditCode;

    @ColumnWidth(15)
    @ExcelProperty(value = {"公司法人"}, index = 2)
    private String legalPerson;

    @ColumnWidth(15)
    @ExcelProperty(value = {"性别"}, index = 3,converter = CustomSexConverterHandler.class)
    private Integer gender;

    @ExcelProperty(value = {"区域"}, index = 4)
    private String province;

    @ColumnWidth(25)
    @DateTimeFormat("yyyy年MM月dd日 HH时mm分ss秒")
    @ExcelProperty(value = {"录入时间"}, index = 5)
    private Date createTime;

    @ColumnWidth(15)
    @ExcelProperty(value = {"公司股东"}, index = 6)
    private String stockholder;

    @ColumnWidth(25)
    @ExcelProperty(value = {"企业联系方式"}, index = 7)
    private String contact;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ImportExcelVo that = (ImportExcelVo) o;
        return Objects.equals(name, that.name) && Objects.equals(creditCode, that.creditCode) && Objects.equals(legalPerson, that.legalPerson) && Objects.equals(gender, that.gender) && Objects.equals(province, that.province) && Objects.equals(createTime, that.createTime) && Objects.equals(stockholder, that.stockholder) && Objects.equals(contact, that.contact);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, creditCode, legalPerson, gender, province, createTime, stockholder, contact);
    }
}

2、添加expiringmap依赖

添加expiringmap依赖,用来解决,导入错误信息,放入一个有时间限制的map中,然后进行下载错误信息

        <dependency>
            <groupId>net.jodah</groupId>
            <artifactId>expiringmap</artifactId>
            <version>0.5.10</version>
        </dependency>

expiringmap的使用

    private static final ExpiringMap<String,List<ImportErrorInfo>> expiryFileMap  = ExpiringMap.builder()
            .maxSize(100)
            .expiration(180, TimeUnit.SECONDS)
            .expirationPolicy(ExpirationPolicy.ACCESSED)
            .variableExpiration()
            .build();

3、自定义监听器处理导入数据

package com.hl.easyexcel.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.hl.easyexcel.domain.ImportErrorInfo;
import com.hl.easyexcel.domain.ImportExcelVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public final class ExcelListener extends AnalysisEventListener<ImportExcelVo> {

    /**
     * 此线程变量用来存储错误提示,只是提示表头是否符合模板要求
     */
    public static final ThreadLocal<Exception> RESP = new ThreadLocal<>();

    /**
     * 处理重复数据
     */
    private static final ThreadLocal<List<ImportExcelVo>> threadLocal = new ThreadLocal<>();
    /**
     * 批处理阈值1000
     */
    private static final int BATCH_COUNT = 1000;

    /**
     * 自定义用于暂时存储data
     * 可以通过实例获取该值
     */
    private final List<ImportExcelVo> dataList = new ArrayList<>();

    /**
     * 返回提示语
     */
    private final List<ImportErrorInfo> errorInfoList = new ArrayList<>();

    /**
     * 第几行有错误,这里根据表头来进行判断
     */
    private int row = 1;

    /**
     * 重写invokeHeadMap方法,校验表头
     *headMap的key是表头下标,value是内容
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        // headMap.containsKey(下标),先判空
        // headMap.get(0).equals() 判内容是否相符
        log.info(JSONUtil.toJsonStr(headMap));
        if ((!headMap.containsKey(0) || !headMap.get(0).trim().equals("企业名称"))
                || (!headMap.containsKey(1) || !headMap.get(1).trim().equals("社会统一信用代码"))
                || (!headMap.containsKey(2) || !headMap.get(2).trim().equals("公司法人"))
                || (!headMap.containsKey(3) || !headMap.get(3).trim().equals("性别"))
                || (!headMap.containsKey(4) || !headMap.get(4).trim().equals("区域"))
                || (!headMap.containsKey(5) || !headMap.get(5).trim().equals("录入时间"))
                || (!headMap.containsKey(6) || !headMap.get(6).trim().equals("公司股东"))
                || (!headMap.containsKey(7) || !headMap.get(7).trim().equals("企业联系方式")) ){
            RESP.set(new RuntimeException("表头错误"));
        }

    }

    /**
     * 具体校验逻辑
     */
    @Override
    public void invoke(ImportExcelVo importExcelVo, AnalysisContext analysisContext) {
        log.info(importExcelVo.toString());
        //给每条数据设置行数
        importExcelVo.setRowNum(++row);
        //检查重复数据
        checkDuplicateData(importExcelVo,errorInfoList);
        //校验参数
        Integer gender = importExcelVo.getGender();
        if (gender != 0){
            saveError(importExcelVo.getRowNum(),"性别不能为男",errorInfoList);
        }

        //存入数据
        dataList.add(importExcelVo);

    }

    /**
     * excel读取完都会调用此方法
     */
    @SneakyThrows
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (ObjectUtil.isNotNull(RESP) && ObjectUtil.isNotNull(RESP.get())){
            try {
                throw RESP.get();
            } finally {
                //最后清空线程变量的值
                RESP.remove();
            }
        }

        //解析结束销毁不用的资源
        //注意不要调用datas.clear(),否则getDatas为null

        // 获取总行数(含表头)
        Integer totalRowNum = context.readSheetHolder().getApproximateTotalRowNumber();
        totalRowNum = totalRowNum-1;
        if(totalRowNum.compareTo(0) == 0){
            throw new RuntimeException("数据为空");
        }
        if(totalRowNum > BATCH_COUNT){
            throw new RuntimeException("excel中的数据条数大于1000条");
        }
    }

    private void checkDuplicateData(ImportExcelVo inputEntity, List<ImportErrorInfo> errorInfoList) {
        List<ImportExcelVo> threadLocalVal = threadLocal.get();
        if (CollUtil.isEmpty(threadLocalVal)) {
            threadLocalVal = new ArrayList<>();
        }

        threadLocalVal.forEach(e -> {
            if (e.equals(inputEntity)) {
                ImportErrorInfo errorInfo = new ImportErrorInfo();
                errorInfo.setLine(inputEntity.getRowNum());
                errorInfo.setMsg("数据与第" + e.getRowNum() + "行重复");
                errorInfoList.add(errorInfo);
            }
        });
        // 添加本行数据对象到ThreadLocal中
        threadLocalVal.add(inputEntity);
        threadLocal.set(threadLocalVal);
    }

    /**
     * 保存提示信息到集合
     * @param rowIndex 行数
     * @param desc 提示信息
     * @param errorInfoList 存入集合
     */
    private void saveError(Integer rowIndex, String desc, List<ImportErrorInfo> errorInfoList) {
        ImportErrorInfo errorInfo = new ImportErrorInfo();
        errorInfo.setLine(rowIndex);
        errorInfo.setMsg(desc);
        errorInfoList.add(errorInfo);
    }


    /**
     * 返回数据
     * @return 返回读取的数据集合
     **/
    public List<ImportExcelVo> getDataList() {
        return dataList;
    }

    /**
     * 返回数据
     * @return 返回提示集合
     **/
    public List<ImportErrorInfo> getErrorInfoList() {
        return errorInfoList;
    }

    /**
     * 返回重复数据
     * @return 返回提示集合
     */
    public ThreadLocal<List<ImportExcelVo>> getThreadLocal() {
        return threadLocal;
    }
}

4、读取文件和错误信息下载

private static final ExpiringMap<String,List<ImportErrorInfo>> expiryFileMap  = ExpiringMap.builder()
            .maxSize(100)
            .expiration(180, TimeUnit.SECONDS)
            .expirationPolicy(ExpirationPolicy.ACCESSED)
            .variableExpiration()
            .build();

    /**
     * 导入
     */
    @PostMapping("/import")
    @ResponseBody
    public HttpResponseTemp<?> importManualEntry(@RequestParam(value = "file") MultipartFile file){
        ExcelListener listener = new ExcelListener();
        try {
            //headRowNumber(2)从第3行开始读取,使用getDataList()方法取出数据
            EasyExcel.read(file.getInputStream(), ImportExcelVo.class,listener).headRowNumber(1).sheet(0).doRead();
        }catch (Exception e){
            throw new RuntimeException(e);
        }finally {
            // 清除threadLocal 防止内存泄漏
            ThreadLocal<List<ImportExcelVo>> threadLocal = listener.getThreadLocal();
            if (threadLocal != null) {
                threadLocal.remove();
            }
        }
        //正确信息
        List<ImportExcelVo> excelList = listener.getDataList();
        //基础校验有错误,则直接返回错误信息
        List<ImportErrorInfo> errorInfoList = listener.getErrorInfoList();
        if(ObjectUtil.isNotEmpty(errorInfoList)){
            errorInfoList.sort(Comparator.comparing(ImportErrorInfo::getLine));
            String uuid = IdUtil.randomUUID();
            expiryFileMap.put(uuid,errorInfoList);
            return ResultStat.SERVER_INTERNAL_ERROR.wrap("数据导入失败:" + uuid);
        }else {
            System.out.println(excelList);
            return ResultStat.OK.wrap("数据导入完成");
        }
    }

    @GetMapping("/downloadErrorFile/{fileId}")
    public void downloadErrorFile(@PathVariable String fileId,HttpServletResponse response){
        //从存储错误信息的map中把数据取出来,然后再下载导出错误文件模板
        List<ImportErrorInfo> dataList = expiryFileMap.get(fileId);
        if (CollUtil.isNotEmpty(dataList)) {
            String name = "导入错误信息";
            ExcelUtil.export(name,"数据", dataList, ImportErrorInfo.class, response);
        } else {
            throw ApiException.wrapMessage(ResultStat.SERVER_INTERNAL_ERROR,"文件不存在");
        }
    }

导入文件样式

在这里插入图片描述

下载的错误信息

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/125773.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vulnhub DC系列 DC-2

总结:cewl和wpscan的使用&#xff0c;rbash逃逸&#xff0c;suid提权 下载地址 DC-2.zip (Size: 847 MB)Download: http://www.five86.com/downloads/DC-2.zipDownload (Mirror): https://download.vulnhub.com/dc/DC-2.zip使用方法:解压后&#xff0c;使用vm直接打开ova文件 漏…

MergeTree原理之二级索引

二级索引 除了一级索引之外&#xff0c;MergeTree同样支持二级索引&#xff0c;二级索引又称跳数索引&#xff0c;由数据的聚合信息构建而成。根据索引类型的不同&#xff0c;其聚合信息的内容也不同&#xff0c;当然跳数索引的作用和一级索引是一样的&#xff0c;也是为了查询…

【SpringCloud】什么是微服务?什么是SpringCloud?

【SpringCloud】什么是微服务&#xff1f;什么是SpringCloud&#xff1f; 一、什么是微服务&#xff1f; 1. 微服务架构的演变历程 单体架构 单体架构优缺点 2. 分布式架构 分布式架构优缺点 存在问题 3. 微服务 微服务的架构特征 微服务的优缺点 二、SpringClo…

头部3D建模新应用:护目镜类产品定制,省时高效好选择

自从越来越多人开始了运动健身&#xff0c;不少运动爱好者已经从小白用户升级为高级运动玩家。随着大家对运动装备的要求也越来越高&#xff0c;不少爱好者开始选购一些轻量化的私人订制装备。例如&#xff0c;高度符合用户头部外型的游泳眼镜、骑行护目镜等等。 游泳眼镜是为了…

用ACLS去控制访问文件

ACLs可以针对多个用户以及群组&#xff0c;其他人做出权限控制。文件系统需要挂载被ACL支持。XFS文件系统支持ACL。EXt4在7版本中默认激活ACL.但在更早的版本需要使用acl选项去挂载申请。 上图第十个字符.代表已经有了acl.表示已经设置ALC。文件的owner可以对文件设置ACL. get…

【结构型】组合模式(Composite)

目录组合模式(Composite)适用场景组合模式实例代码&#xff08;Java&#xff09;组合模式(Composite) 将对象组合成树型结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。 适用场景 想表示对象的部分-整体层次结构。希望用户忽略…

Java判断null的几种方式

组内code review时&#xff0c;有同学提到字符串判断空值的写法&#xff0c;如下两种&#xff0c;&#xff08;1&#xff09;null在后&#xff0c;Test public void testDemo1() {String str null;if (str null) {System.out.println("null在后");return;} }&#…

计算机的人机交互

1、 计算机的人机交互发展历史 计算机在刚开始出现的时候&#xff0c;因为占地广、造价高、耗电高的原因&#xff0c;一般都是给军队、政府使用的&#xff0c;并不是给个人使用的。随着计算机的发展&#xff0c;体积越来越小&#xff0c;出现了微型机&#xff0c;才使得计算机…

C# 数据库 ADO.NET概述

一 数据库 1 数据库&#xff08;Database&#xff09; 2 数据库管理系统&#xff08;DBMS&#xff09; 如Oracle,MS SQL Server 3 数据库系统的优点 共享性、独立性、完整性、冗余数据少。 4 管理功能 数据定义/操纵/完整/完全/并发 二 常用的数据库管理系统 1 微软的…

剑指offer----C语言版----第二天

目录 1. 二维数组中的查找 1.1 题目描述 1.1 思路一 1.2 思路二 1.3 思路三&#xff08;最优解&#xff09; 1. 二维数组中的查找 原题链接&#xff1a;剑指 Offer 04. 二维数组中的查找 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/er-wei-shu-…

WinServer 2012 域控组策略 用户发布软件部署

本例演示安装 Notepad 这款软件 因为域中发布软件只支持 msi 格式&#xff0c;所以要把 exe 转成 msi 格式&#xff0c;可以用这个软件 https://www.advancedinstaller.com/ 1、转换格式 &#xff08;1&#xff09;选择 MSI from EXE &#xff08;2&#xff09;定义项目名…

PLC实现十字路口交通灯的控制课程设计毕业设计

微信公众号&#xff1a;创享日记 对话框发送&#xff1a;plc十字路口 获取完整源码源程序文件 要求&#xff1a; 1、信号灯受启动及停止按钮的控制&#xff0c;当按下启动按钮时&#xff0c;信号灯系统开始工作&#xff0c;并周而复始地循环工作&#xff0c;当按下停止按钮时&…

【再学Tensorflow2】TensorFlow2的核心概念

TensorFlow2的核心概念Tensorflow中的张量常量张量变量张量Tensorflow中的计算图计算图介绍静态计算图动态计算图Autograph张量Tensor、图Graph、操作Operation、会话Session模型Model与层LayerTensorflow中的自动微分机制利用梯度磁带求导数利用梯度磁带和优化器求最小值参考资…

ArcGIS基础实验操作100例--实验13 数字化面图形的技巧

本实验专栏来自于汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 基础编辑篇--实验13 数字化面图形的技巧 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08…

Android 虚拟机 模拟器 设置梯子代理 for Mac

最近需要Android13的环境&#xff0c;想着刷机或者弄个模拟器。 本着效率优先的原则&#xff0c;现在本地机器上搞个Android模拟器。 打开AndroidStudio&#xff0c;在tool菜单 选择avd&#xff0c;安卓虚拟设备 根据本地电脑选择x86架构&#xff0c;MAC os 10.12&#xff0c…

虹科案例 | 监测各种材料液位居然如此轻松?

应用背景 1 天送达标准给公司带来了压力&#xff0c;要求他们比以往任何时候都更快、更高效地处理物品。对于依赖散装材料的行业尤其如此。例如&#xff1a; 农业 建造 塑胶成型 食品加工 许多这些行业通过将材料存放在箱子、罐或筒仓中来处理材料。为了正确地计…

麦克风采样率设置导致视频会议中声音采集异常问题分享

目录 1、问题描述 2、麦克风音频采集频率引发的声音采集异常 3、修改麦克风采集频率的详细说明 4、最后 这几年&#xff0c;大家已经习惯于使用视频会议软件&#xff08;比如免费的腾讯会议软件&#xff09;进行线上沟通与交流&#xff0c;通过摄像头将头像采集发送到会议中…

ARM专用指令

目录 一、状态寄存器传送指令:访问&#xff08;读写&#xff09;CPSR寄存器 ​编辑 二、软中断指令:触发软中断 三、协处理器指令:操控协处理器的指令 四、伪指令: 一、状态寄存器传送指令:访问&#xff08;读写&#xff09;CPSR寄存器 CPSR寄…

多线程的创建和使用(4种)

1.JDK5.0之前的创建方式 方式一&#xff1a;继承于Thread类 1.创建一个继承于Thread类的子类 2.重写Thread类的run() --> 此线程执行的作声明在run()中 3.创建Thread类的子类的对象 4.通过此对象调用start() 方式二&#xff1a;实现Runnable接口 1.创建一个实现了Runnable…

第三十五章 数论——卡特兰数

第三十五章 数论——卡特兰数一、什么是卡特兰数1、推导2、公式二、卡特兰数的应用1、问题&#xff1a;2、分析3、代码一、什么是卡特兰数 1、推导 我们看下面这个坐标系&#xff1a; 我们从(0,0)(0,0)(0,0)点到(6,6)(6,6)(6,6)点的路线有很多&#xff0c;并且根据我们高中排…