easyExcel实现excel文件上传和下载

一、easyExcel简介

  在工作中,经常需要把excel中的数据导入系统,亦或是把系统中符合筛选条件的数据通过excel的方式导出。
  Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后的存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出;03版excel的处理采用POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。

  引入jar包。由于easyExcel中已经引入poi-ooxml、poi等jar。如果开发的老项目中也有poi-ooxml、poi的引入,注意jar包版本问题。

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <!--目前最新的版本-->
    <version>1.1.2-beta5</version>
</dependency>

  学习easyExcel最好的方式,就是下载源码,参考项目中的测试用例。本篇教程为实践的总结,解决了实际开发中遇到的问题。两者结合学习事半功倍。
easyExcel GitHub链接


二、controller层解析excel文件

  easyExcel源码的测试用例中,提供了多种读取excel的方法。比如:

  1. 07版本excel读数据量少于1千行数据。
    excel中每行数据解析成Object,该Object包含excel中一整行的数据。
  2. 07版本excel读数据量少于1千行数据,自动转成 javamodel。
    excel中每行数据解析成java对象,excel每列对应java对象的一个成员变量。
  3. 07版本excel读数据量大于1千行。
  4. 07版本excel读数据量大于1千行数据自动转成javamodel。

  建议采用第4种方式,因为满足的场景更多,03版excel处于淘汰阶段,不予介绍;上述提到4种excel解析方法具体该如何使用,可以参考easyExcel源码中的测试用例。
  easyExcel源码的测试用例中,仅给出mavan项目resource路径下静态excel文件的解析的demo程序,实际项目中,文件都来自ajax发送的请求体。下面给出实际开发中controller层的代码。

@ResponseBody
@RequestMapping(value = "/importExcel", method = RequestMethod.POST,
        produces = "application/json;charset=utf-8")
public Result batchAddByExcel(
@RequestParam(value = "excel", required = false) MultipartFile excel,
@RequestParam(value = "name", required = false) String currentUser,
@RequestParam(value = "userId", required = false) Integer userId,
BatchAddPromotionVO request) {
    // 打印入参日志
    log.info("request = {},name = {}", request, currentUser);
    // 进行参数校验,检查入参的合理性
    Result result = checkParam(excel, request);
    if (!result.isSuccess()) {
        return result;
    }
    
    Map<Integer, List<Object>> sheetRowModelMap = null;
    try {
        // 解析excel文档。具体如何解析,省略。请参考easyExcel的源码测试用例
        sheetRowModelMap = parseExcelData(excel);
    } catch (Exception e) {
        log.error("parseExcelData error = ", e);
        return Result.wrapError(1, "解析excel失败" + e.getMessage());
    }
    // 遍历excel数据转化为业务需要的VO数据
    List<SavePromotionVO> list = parseExcelDataToVO(request, sheetRowModelMap);
    // 进行业务代码编写
    return Result.wrapSuccess(true);;
}

  注意点:在校验excel文件的合理性时,用下面的方式可能是有问题的。

private Result checkParam(MultipartFile excel, BatchAddPromotionVO request) {
        if (excel == null || request == null) {
            return Result.wrapError(1, "参数异常");
        }
        // excel文件的扩展名是可以是大写的 XLSX,所以这么判断不完整
        if (!excel.getOriginalFilename().contains(".xlsx")) {
            return Result.wrapError(1, "文件格式错误,请重新下载模板");
        }
        return Result.wrapSuccess(true);
    }

  上述代码中BatchAddPromotionVO,是一个复杂的java对象,定义如下:

package com.missfresh.pmp.admin.vo.productPromotion;

import com.alibaba.fastjson.JSONArray;
import com.missfresh.domain.Result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class BatchAddPromotionVO {
    private String operateGroup;
    private Integer promoteLevel;
    private Integer businessType;
    /**
     * 位置信息,用于接收一个大json字符串
     */
    private String positionString;
    /**
     * 上面的 positionString接收到参数后,需要编写代码将其 转成 positionList
     */
    private List<PositionVO> positionList;
}

  这个controller方法既需要上传excel文件,又需要上传一堆业务参数。代码编写完成后,使用postman调用接口,是一个问题,该如何调接口?如下图所示:
在这里插入图片描述


三、controller层导出excel文件

  首先需要了解什么是 MIME TYPE?链接:https://www.cnblogs.com/jsean/articles/1610265.html
  easyExcel中的测试用例中也给出了多种写静态excel文件的方法,可以供开发者学习。在实际的业务中,需要把符合筛选条件的数据通过excel的方式导出、提供下载excel的功能。

@ResponseBody
@RequestMapping(value = "/exportSelectedPromotion.do", 
method = RequestMethod.POST, produces = "application/json;charset=utf-8")
public void exportPromotionByExcel07(@RequestBody PromotionListQueryVO queryVO, 
								HttpServletResponse response) {
    log.info("queryVO = {}", queryVO);
    try {
        // 查符合筛选条件的数据
        List<PromotionDTO> list = getPromotionDTOList(queryVO);
        
        // 把符合条件的数据,转化成写excel需要的Model实体
        List<PromotionWriteModel> modelList = converseListToWriteModel(list);

        String time = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        // 指定导出的excel文件的名字
        String fileName = "promotion_export_" + time;
        // 指定工作簿的名字
        String sheetName = "sheet1";

        // easyexcel工具类实现Excel文件导出(下文给出ExcelUtil的代码)
        ExcelUtil.writeExcel(response, modelList, fileName, sheetName, new PromotionWriteModel());

    } catch (Exception e) {
        e.printStackTrace();
        log.error("exportPromotionByExcel07 error:", e);
    }
}

ExcelUtil代码如下:

package com.missfresh.pmp.admin.util;

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;

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

public class ExcelUtil {
    /**
     * 导出 Excel :一个 sheet,带表头.
     *
     * @param response  HttpServletResponse
     * @param list      数据 list,每个元素为一个 BaseRowModel
     * @param fileName  导出的文件名
     * @param sheetName 导入文件的 sheet 名
     * @param model     映射实体类,Excel 模型
     * @throws Exception 异常
     */
    public static void writeExcel(
        HttpServletResponse response, List<? extends BaseRowModel> list,
        String fileName, String sheetName, BaseRowModel model) throws Exception {
        ExcelWriter writer = 
        new ExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
        Sheet sheet = new Sheet(1, 0, model.getClass());
        sheet.setSheetName(sheetName);
        writer.write(list, sheet);
        writer.finish();
    }

    /**
     * 导出文件时为Writer生成OutputStream.
     *
     * @param fileName 文件名
     * @param response response
     * @return ""
     */
    private static OutputStream getOutputStream(String fileName, 
            HttpServletResponse response) throws Exception {
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf8");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "no-store");
            response.addHeader("Cache-Control", "max-age=0");
            return response.getOutputStream();
        } catch (IOException e) {
            throw new Exception("导出excel表格失败!", e);
        }
    }
}

Postman调接口测试:
在这里插入图片描述

四、注意事项

  填入excel中的数据,按键盘del键删不干净,easyExcel在解析时依然可以读到del键删过的单元格,bean里每个元素都解析成了null,过滤掉这些bean就行。临时解决办法时,别操作del键。选中要删除的 行 – 右键 —删除,这样能删干净。如下图:
在这里插入图片描述


end,转载请注明出处。

GNG
发布了118 篇原创文章 · 获赞 389 · 访问量 68万+

猜你喜欢

转载自blog.csdn.net/so_geili/article/details/91621575