Alibaba EasyExcel 异步导出excel

简单描述

使用EasyExcel操作Excel文件可以避免很多POI api的繁琐操作,可以较大程度的简化导出Excel代码,当然EasyExcel也是对POI的封装,如果遇到较为复杂的场景可以使用原生的poi进行操作。

异步导出思路

异步导出是将导出拆分为生成excel文件和下载excel文件两步操作,实现方式很多可根据场景进行选择,如果是单应用可以直接将生成的excel发到应用内存中比如map,下载请求直接从map中拿取生成好的对象;如果是多应用可以将生成的excel保存到一个公共目录中,下载请求直接读取生成好的excel。

生成excel

	private static Map<String, ExcelWriter> workbookMap = new ConcurrentHashMap();
    private static Map<String, String> workbookName = new ConcurrentHashMap();

    @RequestMapping("/export/async")
    @ResponseBody
    public JSONObject AsyncExport(UserVo user) {
        JSONObject returnJson = new JSONObject();
        String fileName = FILE_NAME_PREFIX + "用户.xlsx";
        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(UploadFileCst.EXPORTFILE + fileName, UserVo.class).build();
			List<UserUserVo> dataList = userService.getUserList(user);
  			WriteSheet writeSheet = EasyExcel.writerSheet("user").build();
            excelWriter.write(dataList, writeSheet);                        
            workbookMap.put(key, excelWriter);
            workbookName.put(key, fileName);
            returnJson.put("Status", "OK");
            returnJson.put("key", key);
        } catch (Exception ex) {
            returnJson.put("Status", "ERROR");
            returnJson.put("Message", ex.getMessage());
            log.error(ex.getMessage(), ex);
        }
        return returnJson;
    }

ps:

  1. 生成excel的请求返回的是json数据且数据中必须包含生成excel的结果状态,前端会根据该请求返回的结果决定是发送下载请求还是抛出下载失败。
  2. Sheet最大行数为1048575行,超出会抛出异常
java.lang.IllegalArgumentException: Invalid row number (1048576) outside allowable range (0..1048575)

下载excel请求

 	@RequestMapping("/export/async/download")
    public void downloadSync(String name, HttpServletResponse response) {
        ExcelWriter excelWriter = null;
        WriteContext writeContext = null;
        WriteSheetHolder writeSheetHolder = null;
        WriteWorkbookHolder writeWorkbookHolder = null;
        Workbook workbook = null;
        String fileName = null;
        try {
            if (StringUtils.isBlank(name)) {
                throw new RuntimeException("文件key不能为空");
            }
            if (!workbookName.containsKey(name) || !workbookMap.containsKey(name)) {
                throw new RuntimeException("查找文件失败");
            }
            fileName = workbookName.get(name);
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            String fileNameCode = URLEncoder.encode(fileName, "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileNameCode);

            excelWriter = workbookMap.get(name);

            writeContext = excelWriter.writeContext();
            writeSheetHolder = writeContext.writeSheetHolder();
            writeWorkbookHolder = writeSheetHolder.getParentWriteWorkbookHolder();
            workbook = writeWorkbookHolder.getCachedWorkbook();

            workbook.write(response.getOutputStream());

        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            if (writeContext != null) {
                writeContext.finish(true);
            }
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
            if (excelWriter != null) {
                excelWriter.finish();
            }
            if (workbookMap.containsKey(name)) {
                workbookMap.remove(name);
            }
            if (workbookName.containsKey(name)) {
                workbookName.remove(name);
            }
            //清除文件
            if (StringUtils.isNotBlank(fileName)) {
                File file = new File(UploadFileCst.EXPORTFILE + fileName);
                if (file != null) {
                    file.delete();
                }
            }
        }
    }

ps:下载请求需要注意的是删除资源,关闭流等操作。

前端代码

function exprot(type) {
        var params = $("#file_form").serialize();
        $("#download_plane").window('close');
        var url = "${url}/export/async?" + params;
        //提示弹出框
        $("#download_model").window('open');
        $.ajax({
            url:url,
            type:"GET",
            dataType:"json",
            async:true,
            success:function (data) {
                if (data.Status == 'OK') {
                    $("#download_model").window('close');
                    window.location.href = "${url}/export/async/download?name=" + data.key;
                } else {
                    $('#download_message').empty().text("文件导出错误,请稍后重试!")
                }
            }
        });
    }

ps:注意设置async:true

知识点

EasyExcel 使用cglib动态代理将目标对象进行代码,通过注解定义的规则对数据进行校验封装

 WriteHolder currentWriteHolder = this.writeContext.currentWriteHolder();
        BeanMap beanMap = BeanMap.create(oneRowData);
        Set<String> beanMapHandledSet = new HashSet();
        int cellIndex = false;
        Map ignoreMap;
        Cell cell;
        int cellIndex;
        if (HeadKindEnum.CLASS.equals(this.writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) {
            ignoreMap = this.writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap();
            Map<Integer, ExcelContentProperty> contentPropertyMap = this.writeContext.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap();
            Iterator var11 = contentPropertyMap.entrySet().iterator();

            while(var11.hasNext()) {
                Entry<Integer, ExcelContentProperty> entry = (Entry)var11.next();
                cellIndex = (Integer)entry.getKey();
                ExcelContentProperty excelContentProperty = (ExcelContentProperty)entry.getValue();
                String name = excelContentProperty.getField().getName();
                if (beanMap.containsKey(name)) {
                    Head head = (Head)ignoreMap.get(cellIndex);
                    WriteHandlerUtils.beforeCellCreate(this.writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE);
                    cell = WorkBookUtil.createCell(row, cellIndex);
                    WriteHandlerUtils.afterCellCreate(this.writeContext, cell, head, relativeRowIndex, Boolean.FALSE);
                    Object value = beanMap.get(name);
                    CellData cellData = this.converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, value, excelContentProperty, head, relativeRowIndex);
                    WriteHandlerUtils.afterCellDispose(this.writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE);
                    beanMapHandledSet.add(name);
                }
            }
        }

总结

EasyExcel是对poi的封装如果场景比较特殊,可以直接获取ExcelWriter对象底层的Workbook进行操作,具体操作可以查看下载excel导出代码示例,只是要注意的是需要关闭三次流,如果前两次流没有关闭或关闭失败会导致excelWriter.finish()抛出流关闭异常。
ps:如有建议或疑问可在评论区沟通

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
阿里巴巴的EasyExcel是一个基于Java的开源项目,用于简化Excel的读写操作。你可以通过在项目的pom.xml文件中添加以下依赖来使用EasyExcel进行Excel导出: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.1</version> </dependency> ``` EasyExcel的特点是在尽可能节约内存的情况下支持读写大型Excel文件。相比于其他Java领域的Excel处理框架,如Apache POI和jxl,EasyExcel在解析Excel时不会一次性加载全部数据到内存中,而是逐行读取并解析数据,从而减少内存占用。此外,EasyExcel采用了观察者模式,通过AnalysisEventListener来处理每一行的解析结果。 你可以参考EasyExcel的官方文档,了解更多关于使用EasyExcel进行Excel导出的详细信息。\[2\] #### 引用[.reference_title] - *1* *2* [alibabaeasyexcel导入导出Excel处理](https://blog.csdn.net/xiyang_1990/article/details/130832480)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [1.EasyExcel读写Excel的介绍](https://blog.csdn.net/weixin_46080928/article/details/120101184)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值