EasyExcel使用笔记

官方Github地址:https://github.com/alibaba/easyexcel

官方使用说明:Alibaba Easy Excel - 简单、省内存的Java解析Excel工具 | 首页

使用步骤:

  1. 在页面上新增导入按钮和文件选择框,代码参考:
    <a id="btnImport" class="easyui-linkbutton" data-options="iconCls:'icon-save'" onclick="selectFile()" style="float: right;">导入</a>
    
    <input id="filebox" name="filebox" type="file" onchange="uploadFile()" style="float:right;display:none;" />

  2. JavaScript调用后台方法,以JQuery为例:

  3.    function selectFile(){
            $("#filebox").click();
        }
    
        function uploadFile() {
            if(isLowerIE10()){
                $.messager.alert('错误', "当前浏览器版本太低,请使用IE10及以上或者其他浏览器",'error');
            } else {
                var file = $("#filebox").val();    
                 if (file.endWith(".xlsx")) {
                     var options = {
                         type : 'post',
                         url : "upload.do",
                         dataType : 'json',
                         complete : function(result) {
                             $('#dg').datagrid("loaded");
                             var result = result.responseJSON;
                             $.messager.alert('操作提示', result.msg,'info',function(){
                                 $('#dg').datagrid('reload');
                             });
                         }
                     }
                     $('#dg').datagrid("loading");
                     $("#searchForm").ajaxSubmit(options);
                 } else {
                     $.messager.alert('错误', "请选择Excel2007以上版本文件,扩展名:xlsx",'error');
                 }
             }
         }

  4. 后台Controller接受文件流并传给业务层做进一步处理,代码参考
        @RequestMapping("upload.do")
        @ResponseBody
        public Result upload(HttpServletRequest req, @RequestParam(value = "filebox") MultipartFile file) {
            Result result = Result.getInstanceError();
            try {
                result = prodlineBugService.importByExcel(file);
            } catch (Exception e) {
                log.error("产线不良Excel导入异常:", e);
                result.setMsg(e.getMessage());
            }
            return result;
        }

  5. 业务层service代码参考。接收到流之后转换成业务对象。
    public Result importByExcel(MultipartFile file) throws IOException {
            EasyExcel.read(file.getInputStream(), ProdlineBugVO.class, new ProdlineBugListener(this)).sheet().autoTrim(true).doRead();
            return Result.getInstanceSuccess();
    }

  6. 业务对象VO用注解标识一下列名。
    @ExcelIgnoreUnannotated
    public class ProdlineBugVO {
    
        /**
         * ID
         */
        @ExcelProperty(value = "ID")
        private Long id;
        /**
         * 期间(yyyy-mm)
         */
        private String yearMonth;
        /**
         * 供应商编码
         */
        @ExcelProperty(value = "供应商编码")
        private String supplierCode;
        /**
         * 物料编码
         */
        @ExcelProperty(value = "物料编码")
        private String itemNumber;
        /**
         * 包含Y 不包含N
         */
        @ExcelProperty(value = "不良数")
        private BigDecimal bugNum;
        /**
         * 总数
         */
        private BigDecimal totalNum;
        /**
         * 包含Y 不包含N
         */
        @ExcelProperty(value = "工单号")
        private String icmoCode;
        /**
         * 描述
         */
        @ExcelProperty(value = "描述")
        private String description;
        /**
         * 季度考核ID
         */
        private Long quarterExamineId;
        /**
         * 发生时间
         */
        @ExcelProperty(value = "发生日期")
        private Date actualTime;
    
        private String itemName;
        private String supplierName;
        /** excel行号 */
        private Integer rowIndex;
    
        /**
         * 设置 ID
         */
        public Long getId() {
            return id;
        }
    
        /**
         * 获取 ID
         */
        public void setId(Long id) {
            this.id = id;
        }
    
        /**
         * 设置 期间(yyyy-mm)
         */
        public String getYearMonth() {
            return yearMonth;
        }
    
        /**
         * 获取 期间(yyyy-mm)
         */
        public void setYearMonth(String yearMonth) {
            this.yearMonth = yearMonth;
        }
    
        /**
         * 设置 供应商编码
         */
        public String getSupplierCode() {
            return supplierCode;
        }
    
        /**
         * 获取 供应商编码
         */
        public void setSupplierCode(String supplierCode) {
            this.supplierCode = supplierCode;
        }
    
        /**
         * 设置 物料编码
         */
        public String getItemNumber() {
            return itemNumber;
        }
    
        /**
         * 获取 物料编码
         */
        public void setItemNumber(String itemNumber) {
            this.itemNumber = itemNumber;
        }
    
        public BigDecimal getBugNum() {
            return bugNum;
        }
    
        public void setBugNum(BigDecimal bugNum) {
            this.bugNum = bugNum;
        }
    
        public BigDecimal getTotalNum() {
            return totalNum;
        }
    
        public void setTotalNum(BigDecimal totalNum) {
            this.totalNum = totalNum;
        }
    
        /**
         * 设置 包含Y 不包含N
         */
        public String getIcmoCode() {
            return icmoCode;
        }
    
        /**
         * 获取 包含Y 不包含N
         */
        public void setIcmoCode(String icmoCode) {
            this.icmoCode = icmoCode;
        }
    
        /**
         * 设置 描述
         */
        public String getDescription() {
            return description;
        }
    
        /**
         * 获取 描述
         */
        public void setDescription(String description) {
            this.description = description;
        }
    
        /**
         * 设置 季度考核ID
         */
        public Long getQuarterExamineId() {
            return quarterExamineId;
        }
    
        /**
         * 获取 季度考核ID
         */
        public void setQuarterExamineId(Long quarterExamineId) {
            this.quarterExamineId = quarterExamineId;
        }
    
        /**
         * 设置 发生时间
         */
        public Date getActualTime() {
            return actualTime;
        }
    
        /**
         * 获取 发生时间
         */
        public void setActualTime(Date actualTime) {
            this.actualTime = actualTime;
        }
    
        /**
         * 创建人(邮箱)
         */
        private String createBy;
        /**
         * 创建时间
         */
        private Date createTime;
        /**
         * 更新人(邮箱)
         */
        private String updateBy;
        /**
         * 更新时间
         */
        private Date updateTime;
    
        public String getCreateBy() {
            return createBy;
        }
    
        public void setCreateBy(String createBy) {
            this.createBy = createBy;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        public String getUpdateBy() {
            return updateBy;
        }
    
        public void setUpdateBy(String updateBy) {
            this.updateBy = updateBy;
        }
    
        public Date getUpdateTime() {
            return updateTime;
        }
    
        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }
    
        public String getItemName() {
            return itemName;
        }
    
        public void setItemName(String itemName) {
            this.itemName = itemName;
        }
    
        public String getSupplierName() {
            return supplierName;
        }
    
        public void setSupplierName(String supplierName) {
            this.supplierName = supplierName;
        }
    
        public Integer getRowIndex() {
            return rowIndex;
        }
    
        public void setRowIndex(Integer rowIndex) {
            this.rowIndex = rowIndex;
        }
    }

  7. 定义好Listener来调用保存方法。
    public class ProdlineBugListener extends AnalysisEventListener<ProdlineBugVO> {
        private Logger log = LoggerFactory.getLogger(this.getClass());
    
        /** 一次处理的数量,防止一次处理的过多造成oom */
        private static final int BATCH_COUNT = 5000;
    
        List<ProdlineBugVO> list = new ArrayList<ProdlineBugVO>();
    
        private ProdlineBugService prodlineBugService;
    
        public ProdlineBugListener() {
        }
    
        public ProdlineBugListener(ProdlineBugService prodlineBugService) {
            this.prodlineBugService = prodlineBugService;
        }
    
        @Override
        public void invoke(ProdlineBugVO data, AnalysisContext context) {
            data.setRowIndex(context.readRowHolder().getRowIndex() + 1);
            list.add(data);
            if (list.size() >= BATCH_COUNT) {
                // 达到BATCH_COUNT再处理,防止内存中存储过大数据,容易OOM
                prodlineBugService.batchSave(list);
                list.clear();
            }
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext arg0) {
            // 这里也要保存数据,确保最后遗留的和不到batch_count的数据也存储到数据库
            prodlineBugService.batchSave(list);
        }
    }

注意事项

  1. 事务问题:如果不想Excel处理过程中被分成好多比事务,那请将读Excel的方法写到有事务控制的业务层。这样,当某一行内容校验失败,抛出异常,前面的操作就会回滚。
  2. 对于日期格式,可以用Date来接收。框架默认已经支持很多常用的数字格式,例如:"yyyy-MM-dd"等,但如果没有解析成功,那就需要使用@DateTimeFormat("yyyy年MM月dd日")注解配置
  3. @ExcelProperty注解支持按列名或列索引来配置表头,如果使用列名的方式,那即使列顺序变更也不会影响读数据。但千万不要2种方式同时配置
  4. 对于少数Excel单元格内容格式不确定的情况(比如动态内容导入)。可能无法定义明确的VO值对象,可以使用Map<Integer, Object>来接受行内容。key是列索引。
    1. 如果单元格内容为空,map中不会有这个单元格对应的Entry,所以不要用EntrySet来循环
    2. 可以一开始就记录好表头的列数量,然后用这个列数量来循环Map,这样就知道哪一列是空的了。
  5. 如果Excel中有空行,框架会自动跳过不会调用invoke方法。
  6. autoTrim如果是true,读单元格内容时会自动trim。
  7. 框架默认第一行是表头,数据是从第二行开始的。如果不是可以使用headRowNumber方法指定。
  8. 该框架已经解决了POI头疼的性能问题而且简化了很多Excel读写的代码。更多功能可以参考官方github
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值