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

官方使用说明:https://alibaba-easyexcel.github.io/index.html

使用步骤:

  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方法调用后台,代码参考:
     1     function selectFile(){
     2         $("#filebox").click();
     3     }
     4     function uploadFile() {
     5         if(isLowerIE10()){
     6             $.messager.alert('错误', "当前浏览器版本太低,请使用IE10及以上或者其他浏览器",'error');
     7         } else {
     8             var file = $("#filebox").val();    
     9              if (file.endWith(".xlsx")) {
    10                 var options = {
    11                     type : 'post',
    12                     url : "upload.do",
    13                     dataType : 'json',
    14                     complete : function(result) {
    15                         $('#dg').datagrid("loaded");
    16                         var result = result.responseJSON;
    17                         $.messager.alert('操作提示', result.msg,'info',function(){
    18                             $('#dg').datagrid('reload');
    19                         });
    20                     }
    21                 }
    22                 $('#dg').datagrid("loading");
    23                 $("#searchForm").ajaxSubmit(options);
    24             } else {
    25                 $.messager.alert('错误', "请选择Excel2007以上版本文件,扩展名:xlsx",'error');
    26             }
    27         }
    28     }
    Javacript方法
  3. 后台控制器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;
        }
  4. 业务层service
    public Result importByExcel(MultipartFile file) throws IOException {
            EasyExcel.read(file.getInputStream(), ProdlineBugVO.class, new ProdlineBugListener(this)).sheet().autoTrim(true)
                    .doRead();
            return Result.getInstanceSuccess();
        }
  5. 业务对象VO用注解标识一下列名。
      1 @ExcelIgnoreUnannotated
      2 public class ProdlineBugVO {
      3 
      4     /**
      5      * ID
      6      */
      7     @ExcelProperty(value = "ID")
      8     private Long id;
      9     /**
     10      * 期间(yyyy-mm)
     11      */
     12     private String yearMonth;
     13     /**
     14      * 供应商编码
     15      */
     16     @ExcelProperty(value = "供应商编码")
     17     private String supplierCode;
     18     /**
     19      * 物料编码
     20      */
     21     @ExcelProperty(value = "物料编码")
     22     private String itemNumber;
     23     /**
     24      * 包含Y 不包含N
     25      */
     26     @ExcelProperty(value = "不良数")
     27     private BigDecimal bugNum;
     28     /**
     29      * 总数
     30      */
     31     private BigDecimal totalNum;
     32     /**
     33      * 包含Y 不包含N
     34      */
     35     @ExcelProperty(value = "工单号")
     36     private String icmoCode;
     37     /**
     38      * 描述
     39      */
     40     @ExcelProperty(value = "描述")
     41     private String description;
     42     /**
     43      * 季度考核ID
     44      */
     45     private Long quarterExamineId;
     46     /**
     47      * 发生时间
     48      */
     49     @ExcelProperty(value = "发生日期")
     50     private Date actualTime;
     51 
     52     private String itemName;
     53     private String supplierName;
     54     /** excel行号 */
     55     private Integer rowIndex;
     56 
     57     /**
     58      * 设置 ID
     59      */
     60     public Long getId() {
     61         return id;
     62     }
     63 
     64     /**
     65      * 获取 ID
     66      */
     67     public void setId(Long id) {
     68         this.id = id;
     69     }
     70 
     71     /**
     72      * 设置 期间(yyyy-mm)
     73      */
     74     public String getYearMonth() {
     75         return yearMonth;
     76     }
     77 
     78     /**
     79      * 获取 期间(yyyy-mm)
     80      */
     81     public void setYearMonth(String yearMonth) {
     82         this.yearMonth = yearMonth;
     83     }
     84 
     85     /**
     86      * 设置 供应商编码
     87      */
     88     public String getSupplierCode() {
     89         return supplierCode;
     90     }
     91 
     92     /**
     93      * 获取 供应商编码
     94      */
     95     public void setSupplierCode(String supplierCode) {
     96         this.supplierCode = supplierCode;
     97     }
     98 
     99     /**
    100      * 设置 物料编码
    101      */
    102     public String getItemNumber() {
    103         return itemNumber;
    104     }
    105 
    106     /**
    107      * 获取 物料编码
    108      */
    109     public void setItemNumber(String itemNumber) {
    110         this.itemNumber = itemNumber;
    111     }
    112 
    113     public BigDecimal getBugNum() {
    114         return bugNum;
    115     }
    116 
    117     public void setBugNum(BigDecimal bugNum) {
    118         this.bugNum = bugNum;
    119     }
    120 
    121     public BigDecimal getTotalNum() {
    122         return totalNum;
    123     }
    124 
    125     public void setTotalNum(BigDecimal totalNum) {
    126         this.totalNum = totalNum;
    127     }
    128 
    129     /**
    130      * 设置 包含Y 不包含N
    131      */
    132     public String getIcmoCode() {
    133         return icmoCode;
    134     }
    135 
    136     /**
    137      * 获取 包含Y 不包含N
    138      */
    139     public void setIcmoCode(String icmoCode) {
    140         this.icmoCode = icmoCode;
    141     }
    142 
    143     /**
    144      * 设置 描述
    145      */
    146     public String getDescription() {
    147         return description;
    148     }
    149 
    150     /**
    151      * 获取 描述
    152      */
    153     public void setDescription(String description) {
    154         this.description = description;
    155     }
    156 
    157     /**
    158      * 设置 季度考核ID
    159      */
    160     public Long getQuarterExamineId() {
    161         return quarterExamineId;
    162     }
    163 
    164     /**
    165      * 获取 季度考核ID
    166      */
    167     public void setQuarterExamineId(Long quarterExamineId) {
    168         this.quarterExamineId = quarterExamineId;
    169     }
    170 
    171     /**
    172      * 设置 发生时间
    173      */
    174     public Date getActualTime() {
    175         return actualTime;
    176     }
    177 
    178     /**
    179      * 获取 发生时间
    180      */
    181     public void setActualTime(Date actualTime) {
    182         this.actualTime = actualTime;
    183     }
    184 
    185     /**
    186      * 创建人(邮箱)
    187      */
    188     private String createBy;
    189     /**
    190      * 创建时间
    191      */
    192     private Date createTime;
    193     /**
    194      * 更新人(邮箱)
    195      */
    196     private String updateBy;
    197     /**
    198      * 更新时间
    199      */
    200     private Date updateTime;
    201 
    202     public String getCreateBy() {
    203         return createBy;
    204     }
    205 
    206     public void setCreateBy(String createBy) {
    207         this.createBy = createBy;
    208     }
    209 
    210     public Date getCreateTime() {
    211         return createTime;
    212     }
    213 
    214     public void setCreateTime(Date createTime) {
    215         this.createTime = createTime;
    216     }
    217 
    218     public String getUpdateBy() {
    219         return updateBy;
    220     }
    221 
    222     public void setUpdateBy(String updateBy) {
    223         this.updateBy = updateBy;
    224     }
    225 
    226     public Date getUpdateTime() {
    227         return updateTime;
    228     }
    229 
    230     public void setUpdateTime(Date updateTime) {
    231         this.updateTime = updateTime;
    232     }
    233 
    234     public String getItemName() {
    235         return itemName;
    236     }
    237 
    238     public void setItemName(String itemName) {
    239         this.itemName = itemName;
    240     }
    241 
    242     public String getSupplierName() {
    243         return supplierName;
    244     }
    245 
    246     public void setSupplierName(String supplierName) {
    247         this.supplierName = supplierName;
    248     }
    249 
    250     public Integer getRowIndex() {
    251         return rowIndex;
    252     }
    253 
    254     public void setRowIndex(Integer rowIndex) {
    255         this.rowIndex = rowIndex;
    256     }
    257 }
    View Code
  6. 定义好Listener来调用保存方法。
     1 public class ProdlineBugListener extends AnalysisEventListener<ProdlineBugVO> {
     2     private Logger log = LoggerFactory.getLogger(this.getClass());
     3     
     4     /** 一次处理的数量,防止一次处理的过多造成oom */
     5     private static final int BATCH_COUNT = 5000;
     6     
     7     List<ProdlineBugVO> list = new ArrayList<ProdlineBugVO>();
     8     
     9     private ProdlineBugService prodlineBugService;
    10     
    11     public ProdlineBugListener() {
    12     }
    13 
    14     public ProdlineBugListener(ProdlineBugService prodlineBugService) {
    15         this.prodlineBugService = prodlineBugService;
    16     }
    17     
    18     @Override
    19     public void invoke(ProdlineBugVO data, AnalysisContext context) {
    20         data.setRowIndex(context.readRowHolder().getRowIndex() + 1);
    21         list.add(data);
    22         if (list.size() >= BATCH_COUNT) {
    23             // 达到BATCH_COUNT再处理,防止内存中存储过大数据,容易OOM
    24             prodlineBugService.batchSave(list);
    25             list.clear();
    26         }
    27     }
    28 
    29     @Override
    30     public void doAfterAllAnalysed(AnalysisContext arg0) {
    31         // 这里也要保存数据,确保最后遗留的和不到batch_count的数据也存储到数据库
    32         prodlineBugService.batchSave(list);
    33     }
    34 }
    View Code

注意事项

  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
posted on 2020-02-20 10:12  namelessmyth  阅读(3069)  评论(0编辑  收藏  举报