Excel的导入导出,基本上是每个项目必备的功能。但是导入导出的实现还是有一些不同,下面我就分三种情况来说明,通过代码来体现有哪些不同:

1.常规导入导出xlsx

1.1 引入jar

<!--导入导出excel-->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>2.1.7</version>
</dependency>

1.2 添加公共导入导出方法

这里需要添加几个处理导入导出必备类
EasyExcelUtils 公共类

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;

/**
 * @ClassName: EasyExcelUtils
 * @Description: excel 导入导出公共类
 * @Author: letisgo5
 * @Date: 2022/06/21 14:34
 */
public class EasyExcelUtils {

    /**
     * 导出
     * @param response
     * @param excelName   excel名称
     * @param sheetName   sheet名称
     * @param clazz       导出标题类(必须添加导出注解)
     * @param data        导出数据
     * @throws Exception
     */
    public static void exportData(HttpServletResponse response, String excelName, String sheetName, Class clazz, List data) throws Exception {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        excelName = URLEncoder.encode(excelName, "UTF-8").replaceAll("\\+", "%20");
        String filename = new String(excelName.getBytes("UTF-8"), "ISO-8859-1");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + filename + ExcelTypeEnum.XLSX.getValue());
        response.setHeader("filename", filename + ExcelTypeEnum.XLSX.getValue());
        EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).registerWriteHandler(new ExcelCellWriteHandler()).doWrite(data);
    }

    /**
     * 导入
     * @param excelFile             文件
     * @param clazz                 导出标题类
     * @param esayExcelListener     解析文件对象
     * @throws IOException
     */
    public static void importData(MultipartFile excelFile, Class clazz, EsayExcelListener esayExcelListener) throws IOException {
        EasyExcel.read(excelFile.getInputStream(), clazz, esayExcelListener).registerConverter(new ExcelStringNumberUtils()).sheet().headRowNumber(2).doRead(); //从第二行开始读取(第一行为标题)
    }

EsayExcel-导入/导出 监听类

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName: EsayExcelListener
 * @Description: EsayExcel-导入/导出 监听类
 * @Author: letisgo5
 * @Date: 2022/06/21 14:53
 */
public class EsayExcelListener<T> extends AnalysisEventListener<T> {

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

    /**
     * 通过 AnalysisContext 对象还获取当前 sheet,当前行等数据
     * @param data
     * @param analysisContext
     */
    @Override
    public void invoke(T data, AnalysisContext analysisContext) {
        dataList.add(data);
//        if (dataList.size() >= BATCH_COUNT) {
//        }
    }
    /**
     * 所有数据解析完成了 都会来调用
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //解析结束销毁不用的资源
        //dataList.clear();
    }

    public List<T> getDataList() {
        return dataList;
    }
    public void setDataList(List<T> dataList) {
        this.dataList = dataList;
    }

EsayExcel传入String类型数字转换工具类

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;
import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.util.NumberUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.util.NumberToTextConverter;
import java.math.BigDecimal;

/**
 * @ClassName: ExcelStringNumberUtils
 * @Description: EsayExcel传入String类型数字转换工具类
 * @Author: letisgo5
 * @Date: 2022/06/21 14:51
 */
public class ExcelStringNumberUtils implements Converter<String> {

    @Override
    public Class supportJavaTypeKey() {
        return String.class;
    }

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

    @Override
    public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
                                    GlobalConfiguration globalConfiguration) {
        // If there are "DateTimeFormat", read as date
        if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) {
            return com.alibaba.excel.util.DateUtils.format(
                    HSSFDateUtil.getJavaDate(cellData.getNumberValue().doubleValue(),
                            contentProperty.getDateTimeFormatProperty().getUse1904windowing(), null),
                    contentProperty.getDateTimeFormatProperty().getFormat());
        }
        // If there are "NumberFormat", read as number
        if (contentProperty != null && contentProperty.getNumberFormatProperty() != null) {
            return NumberUtils.format(cellData.getNumberValue().doubleValue(), contentProperty);
        }

        // Excel defines formatting
        if (cellData.getDataFormat() != null) {
            if (DateUtil.isADateFormat(cellData.getDataFormat(), cellData.getDataFormatString())) {

                if (cellData.getDataFormatString().contains(":")) {
                    return DateUtils.format(HSSFDateUtil.getJavaDate(cellData.getNumberValue().doubleValue(),
                            globalConfiguration.getUse1904windowing(), null));
                } else {
                    return DateUtils.format(HSSFDateUtil.getJavaDate(cellData.getNumberValue().doubleValue(),
                            globalConfiguration.getUse1904windowing(), null), "yyyy-MM-dd");
                }
            } else if (contentProperty == null) {
                try {
                    // 百分比
                    if (cellData.getDataFormatString() != null && cellData.getDataFormatString().contains("%")) {
                        return new BigDecimal(cellData.getNumberValue().toString()).multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString() + "%";
                    } else if (cellData.getDataFormatString() != null && cellData.getDataFormatString().equals("General")) {

                        //解决easyExcel 解析无 CLASS 对象时,Number to string  用String去接收数字,出现小数点等情况  方法一 会出现 数字位数失真的情况 ,即 excel 用公式计算数值后,只保留3位小数, 读取时 可能出现 直接去取保留前的N为小数的情况 建议使用方法二
//                     方法一   NumberFormat numberFormat = NumberFormat.getInstance();
//                        numberFormat.setMaximumFractionDigits(20);
//                        numberFormat.setGroupingUsed(false);
                        //  return numberFormat.format(cellData.getDoubleValue());
                        // 方法二
                        return NumberToTextConverter.toText(cellData.getNumberValue().doubleValue());
                    } else {
                        return NumberToTextConverter.toText(cellData.getNumberValue().doubleValue());
                        // return cellData.getDoubleValue().toString();
                    }
                } catch (Exception e) {
                    // 建议 统一使用以下方法,可以解决数值格式问题
                    return NumberToTextConverter.toText(cellData.getNumberValue().doubleValue());
                    //   return NumberUtils.format(cellData.getDoubleValue(), contentProperty);
                }
            } else {
                return NumberToTextConverter.toText(cellData.getNumberValue().doubleValue());
                //  return NumberUtils.format(cellData.getDoubleValue(), contentProperty);
            }
        }
        // Default conversion number
//        NumberFormat numberFormat = NumberFormat.getInstance();
//        numberFormat.setMaximumFractionDigits(20);
//        numberFormat.setGroupingUsed(false);
//        return numberFormat.format(cellData.getDoubleValue());
        // 方法二
        return NumberToTextConverter.toText(cellData.getNumberValue().doubleValue());
        // return NumberUtils.format(cellData.getDoubleValue(), contentProperty);
    }

    @Override
    public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
                                       GlobalConfiguration globalConfiguration) {
        return new CellData(Double.valueOf(value));
    }

ExcelCellWriteHandler 单元格处理器

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName: ExcelCellWriteHandler
 * @Description: 单元格处理器
 * @Author: letisgo5
 * @Date: 2022/06/21 14:48
 */
public class ExcelCellWriteHandler extends AbstractColumnWidthStyleStrategy {

    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<Integer, Map<Integer, Integer>>();

    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
                maxColumnWidthMap = new HashMap<Integer, Integer>();
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }

            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
                if (columnWidth > 255) {
                    columnWidth = 255;
                }

                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                }

            }
        }
    }

    private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }

1.3 代码调用

导出调用
			EasyExcelUtils.exportWeb(response, excelName, sheetName, ExportDataVo.class, list); 
    导入调用
            // ImportDataVo 表示需要导入的类,别忘记注解 @ExcelProperty(value = "序号", index = 0)
            EsayExcelListener esayExcelListener = new EsayExcelListener();
            EasyExcelUtils.importWeb(file, ImportDataVo.class, esayExcelListener);
            List<ImportDataVo> list = esayExcelListener.getDataList();

2.根据现有模板导入导出xlsx

根据导出模板导出
public void patchExport(String tempName, String fileName, List dataLst, HttpSession session, HttpServletResponse response) {
    BufferedOutputStream bos = null;
    try {
        //模板存储位置
        String realPath = session.getServletContext().getRealPath("WEB-INF/template/export/"+ tempName);
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        //导出的文件名
        String filename = URLEncoder.encode(fileName, "utf-8")+ ExcelTypeEnum.XLSX.getValue();
        response.setHeader("Content-disposition", "attachment; filename=" + new String(filename.getBytes("UTF-8"), "ISO-8859-1"));

        bos = new BufferedOutputStream(response.getOutputStream());
        ExcelWriter excelWriter = EasyExcel.write(bos).withTemplate(realPath).build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        //list map 是查询并需导出的数据,并且里面的字段和excel需要导出的字段对应
        if(CollectionUtils.isNotEmpty(dataLst)){
            excelWriter.fill(dataLst, writeSheet);
            // excelWriter.fill(dataMap, writeSheet);
        }
        excelWriter.finish();
        bos.flush();
    } catch (Exception e) {
        response.reset();
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        log.error("patchExport error:[{}]", ExceptionUtils.getStackTrace(e));
    } finally {
        if (bos != null){
            try {
                bos.close();
            } catch (IOException e) {
                log.error("patchExport BufferedOutputStream close error:[{}]", ExceptionUtils.getStackTrace(e));
            }
        }
    }
}
根据导入模板导入
      与常规导入无太大差别,主要是模板从第几行读取

3.导入导出csv

导入导出csv

3.1 引入jar

<!-- 导入导出csv文件 -->
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-csv</artifactId>
	<version>1.8</version>
</dependency>
<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.8.0</version>
</dependency>

3.2 实际应用

导出CSV

/**
  * 写CSV并转换为字节流
  * @param headers 表头
  * @param cellList 表数据
  * @return
  */
 public static byte[] writeCsvAfterToBytes(String[] headers,List<Object[]> cellList) {
     byte[] bytes = new byte[0];
     ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
     OutputStreamWriter outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream, Charset.forName("UTF-8"));
     BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
     CSVPrinter csvPrinter = null;
     try {
         //创建csvPrinter并设置表格头
         csvPrinter = new CSVPrinter(bufferedWriter, CSVFormat.DEFAULT.withHeader(headers));
         //写数据
         csvPrinter.printRecords(cellList);
         csvPrinter.flush();
         bytes = byteArrayOutputStream.toString("UTF-8").getBytes();
     } catch (IOException e) {
         log.error("writeCsv IOException:{}", e.getMessage(), e);
     } finally {
         try {
             if (csvPrinter != null) {
                 csvPrinter.close();
             }
             if (bufferedWriter != null) {
                 bufferedWriter.close();
             }
             if (outputStreamWriter != null) {
                 outputStreamWriter.close();
             }
             if (byteArrayOutputStream != null) {
                 byteArrayOutputStream.close();
             }
         } catch (IOException e) {
             log.error("iostream close IOException:{}", e.getMessage(), e);
         }
     }
     return bytes;
 }

 /**
  * 设置下载响应
  * @param fileName
  * @param bytes
  * @param response
  */
 public static void responseSetProperties(String fileName, byte[] bytes, HttpServletResponse response) {
     try {
         fileName = URLEncoder.encode(fileName, "UTF-8");
         response.setContentType("application/csv");
         response.setCharacterEncoding("UTF-8");
         response.setHeader("Pragma", "public");
         response.setHeader("Cache-Control", "max-age=30");
         response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
         OutputStream outputStream = response.getOutputStream();
         outputStream.write(bytes);
         outputStream.flush();
     } catch (IOException e) {
         log.error("responseSetProperties error:{}", e.getMessage(), e);
     }
 }

导入CSV

/**
 * 导入CSV文件
 * @param file 上传文件
 * @return
 */
public static List<String> importCsv(MultipartFile file) {
    List<String> dataList = new ArrayList<String>();
    BufferedReader br = null;
    try {
        byte [] byteArr = file.getBytes();
        InputStream inputStream = new ByteArrayInputStream(byteArr);
        InputStreamReader inputStreamReader =new InputStreamReader(inputStream,"GBK");
        br = new BufferedReader(inputStreamReader);
        String line = "";
        while ((line = br.readLine()) != null) {
            dataList.add(line);
        }
    } catch (Exception e) {
        log.error("importCsv  Close error:{}", e.getMessage(), e);
    } finally {
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                log.error("importCsv BufferedReader Close error:{}", e.getMessage(), e);
            }
        }
    }
    return dataList;
}