EasyExcel快速读写Excel数据

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EasyExcel快速读写Excel数据相关的知识,希望对你有一定的参考价值。

参考技术A

ps: 其实本人并没有对比过POI,只是网络上资料都有这么一说,再对比了GitHub的star数,最后直接选用了EasyExcel

本文介绍一下,如何使用实体映射关系直接读取Excel数据

假设需要读取的数据如下:

首先定义实体类 User ,需要继承 BaseRowModel

实现Excel数据的读取和写入

四EasyExcel实现Excel读写,封装工具类

在项目中,我们经常用到EasyExcel框架实现:对Excel文件的读写操作。为了方便后续其他项目中的使用,将对Excel文件的读写操作,封装成工具类。

一、EasyExcel实现Excel读写,封装工具类

1.1、后端代码

  • ExcelUtil工具类,完成读写操作
package com.deewin.aftermarket.admin.utils;

import cn.hutool.core.convert.Convert;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.deewin.aftermarket.admin.excel.listener.ExcelListener;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel读写工具类
 * @Author bitaotao
 * @Description Excel读写工具类
 * @Date 2021-11-23
 */
public class ExcelUtil 

    private static final String SUFFIX_XLSX = ".xlsx";
    private static final String SUFFIX_XLS=".xls";

    /**
     * 读取Excel(多个sheet可以用同一个实体类解析)
     * @param excelInputStream
     * @param fileName
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> readExcel(InputStream excelInputStream, String fileName,Class<T> clazz) 
        ExcelListener excelListener = new ExcelListener();
        ExcelReader excelReader = getReader(excelInputStream, fileName,clazz, excelListener);
        if (excelReader == null) 
            return new ArrayList<>();
        
        List<ReadSheet> readSheetList = excelReader.excelExecutor().sheetList();
        for (ReadSheet readSheet : readSheetList) 
            excelReader.read(readSheet);
        
        excelReader.finish();
        return Convert.toList(clazz, excelListener.getDataList());
    

    /**
     * 导出Excel(一个sheet)
     *
     * @param response  HttpServletResponse
     * @param list      数据list
     * @param fileName  导出的文件名
     * @param sheetName 导入文件的sheet名
     * @param clazz     实体类
     */
    public static <T> void writeExcel(HttpServletResponse response, List<T> list, String fileName, String sheetName, Class<T> clazz) 

        OutputStream outputStream = getOutputStream(response, fileName);
        ExcelWriter excelWriter = EasyExcel.write(outputStream, clazz).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
        excelWriter.write(list, writeSheet);
        excelWriter.finish();
    


    /**
     * 导出时生成OutputStream
     */
    private static OutputStream getOutputStream(HttpServletResponse response, String fileName) 
        // 文件名
        String fileFullName = fileName.concat(SUFFIX_XLSX);
        try 
            // 防止中文乱码
            fileFullName = URLEncoder.encode(fileFullName, "UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileFullName);
            return response.getOutputStream();
         catch (IOException e) 
            e.printStackTrace();
        
        return null;
    

    /**
     * 返回ExcelReader
     * @param inputStream 输入流
     * @param fileName 文件
     * @param clazz 实体类
     * @param excelListener
     */
    private static <T> ExcelReader getReader(InputStream inputStream, String fileName, Class<T> clazz, ExcelListener excelListener) 
        try 
            if (fileName == null
                    || (!fileName.toLowerCase().endsWith(SUFFIX_XLS) && !fileName.toLowerCase().endsWith(SUFFIX_XLSX))) 
                return null;
            
            ExcelReader excelReader = EasyExcel.read(inputStream, clazz, excelListener).build();
            inputStream.close();
            return excelReader;
         catch (Exception e) 
            e.printStackTrace();
        
        return null;
    


  • 监听器,ExcelListener.java
package com.deewin.aftermarket.admin.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.extern.slf4j.Slf4j;

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

/**
 * Excel监听类
 * @Author bitaotao
 * @Description 监听类
 * @Date 2021-11-23
 */
@Slf4j
public class ExcelListener extends AnalysisEventListener 
    /**
     * 可以通过实例获取该值
     */
    private List<Object> dataList = new ArrayList<>();

    @Override
    public void invoke(Object object, AnalysisContext context) 
        //数据存储到list,供批量处理,或后续自己业务逻辑处理。
        dataList.add(object);
        handleBusinessLogic();
        /*
        //如数据过大,可以进行定量分批处理
        if(dataList.size()>=200)
            handleBusinessLogic();
            dataList.clear();
        
        */
    

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) 
        //非必要语句,查看导入的数据
        log.info("导入的数据条数为: " + dataList.size());
    

    /**
     * 根据业务自行实现该方法,例如将解析好的dataList存储到数据库中
     */
    private void handleBusinessLogic() 
        // TODO
    

    public List<Object> getDataList() 
        return dataList;
    

    public void setDataList(List<Object> dataList) 
        this.dataList = dataList;
    


  • 转换器
    当读写的模板数据中有LocalDateTime类型的数据时,需要使用该转换器
package com.deewin.aftermarket.admin.excel.convert;

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 java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * LocalDateTime 转换器
 * @Author bitaotao
 * @Date 2021-11-23
 */
public class LocalDateTimeConverter implements Converter<LocalDateTime> 

	@Override
	public Class<LocalDateTime> supportJavaTypeKey() 
		return LocalDateTime.class;
	

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

	@Override
	public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
										   GlobalConfiguration globalConfiguration) 
		return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
	

	@Override
	public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
	                                           GlobalConfiguration globalConfiguration) 
		return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
	



  • Excel读写模版实体
package com.deewin.aftermarket.admin.excel.model;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.deewin.aftermarket.admin.excel.convert.LocalDateTimeConverter;
import lombok.*;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * <p>
 * 采销订单
 * Excel导出模型
 * </p>
 *
 * @author bitaotao
 * @since 2021-11-24
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class PurchaseSalesOrderExcelModel 
    @TableId(value = "id", type = IdType.AUTO)
    @ExcelProperty(value = "序号")
    private Long id;

    @ExcelProperty(value = "采购单号")
    @ColumnWidth(20)
    private String orderNo;

    @ExcelProperty(value = "申请时间",converter = LocalDateTimeConverter.class)
    @ColumnWidth(20)
    private LocalDateTime createTime;

    @ExcelProperty(value = "采购方")
    @ColumnWidth(40)
    private String createDeptName;

    @ExcelProperty(value = "产品品类")
    @ColumnWidth(20)
    private String productKindName;

    @ExcelProperty(value = "品牌")
    @ColumnWidth(20)
    private String brandName;

    @ExcelProperty(value = "产品总数量")
    @ColumnWidth(20)
    private Integer productTotalNumber;

    @ExcelProperty(value = "采购总金额")
    @ColumnWidth(20)
    private BigDecimal orderTotalAmount;

    @ExcelProperty(value = "付款方式")
    @ColumnWidth(20)
    private String paymentMethodName;

    @ExcelProperty(value = "合同号")
    @ColumnWidth(20)
    private String contractNo;


  • 控制层代码
    控制层方法exportPurchaseOrder,接受的参数是前端通过封装form表单发起的POST请求。
   /**
     * 采销订单导出
     * @param productKindId 产品品类
     * @param productModel 产品品类
     * @param paymentMethod 收/付款方式
     * @param contractNo 合同号
     * @param orderNo 采购单号
     * @param brandName 采购单号
     * @param response
     */
    @RequestMapping("export")
    @ResponseBody
    public void exportPurchaseOrder(Long productKindId, String productModel, String paymentMethod, String contractNo,
                                    String orderNo, String brandName, HttpServletResponse response) 
        purchaseSalesOrderService.exportPurchaseOrder(productKindId, productModel, paymentMethod, contractNo, orderNo, brandName, response);
    

1.2、前端代码

用Ajax 以post 请求进行文件下载,通过封装form表单,提交post请求。
https://blog.csdn.net/qq_33378853/article/details/86507094

function exportPurchaseOrder() 
	var productKindId = $("#productKindId").val();  //产品品类
	var productModel = $("#productModel").val();  //产品型号
	var paymentMethod = $("#paymentMethod").val();  //收/付款方式
	var contractNo = $("#contractNo").val();//合同号
	var orderNo = $("#orderNo").val();//采购单号
	var brandName = $("#brandName").val();//采购单号
	var form = $('<form method="POST" action="/purchaseSalesOrder/export">');
	form.append($('<input type="hidden" name="productKindId" value="' + productKindId + '">'));
	form.append($('<input type="hidden" name="productModel" value="' + productModel + '">'));
	form.append($('<input type="hidden" name="paymentMethod" value="' + paymentMethod + '">'));
	form.append($('<input type="hidden" name="contractNo" value="' + contractNo + '">'));
	form.append($('<input type="hidden" name="orderNo" value="' + orderNo + '">'));
	form.append($('<input type="hidden" name="brandName" value="' + brandName + '">'));
	$('body').append(form);
	form.submit();

以上是关于EasyExcel快速读写Excel数据的主要内容,如果未能解决你的问题,请参考以下文章

EasyExcel操作excel表格进行读写操作

EasyExcel快速上手

四EasyExcel实现Excel读写,封装工具类

四EasyExcel实现Excel读写,封装工具类

使用EasyExcel进行读写操作

poi和easyExcel基于Java操作Excel学习笔记