如何用matlab读取excel指定列(excelwriter如何调整工具位置)
excel内容只有文字却显示内存大 使用Hutool中ExcelWriter类导出Excel出现多余列问题解决思路
一、问题背景
近期笔者在公司开发平台的框架中引进Hutool中的ExcelWriter做通用的导入及导出,提高开发平台的灵活性和简易性,从而能够降低开发人员的二次开发难度。针对数据库查询出来的实体,字段上添加ExcelColumn注记,并且设置上isExport=true,才能免在Excel中导出,具体代码如下
实体定义@Data@EqualsAndHashCode(callSuper = true)@Accessors(chain = true)@TableName(#34user#34)public class User extends MySqlBaseEntity { @ExcelColumn(isExport = true, trimSpace = false, allowNull = false, isImport = true) @ApiModelProperty(value = #34用户类型#34) @TableField(#34USER_TYPE#34) private String userType @ExcelColumn(isExport = true) @ApiModelProperty(value = #34登录名#34) @TableField(#34USER_NAME#34) private String userName @ExcelColumn(isExport = true) @ApiModelProperty(value = #34姓名#34) @TableField(#34PERSONAL_NAME#34) private String personalName @ApiModelProperty(value = #34加密过的密码#34) @TableField(#34PASSWORD_ENCRYPTED#34) @ ONField(serialize = false) private String passwordEncrypted @ExcelColumn(isExport = true) @ApiModelProperty(value = #34员工编号#34) @TableField(#34EMPLOYEE_CODE#34) private String employeeCode @ApiModelProperty(value = #34邮箱地址#34) @TableField(#34EMAIL#34) private String email @ApiModelProperty(value = #34 号码#34) @TableField(#34PHONE#34) private String phone}
如上,定义的实体中字段共有7个,需要导出(使用ExcelColumn注记,并标识为需要导出isExport=true)的列只有4个
用户类型userType、登录名userName、姓名userType、员工编号employeeCode
2、导出代码
/ 将返回数据导出到EXCEL,并写入HTTP响应流 @param invocation @param resultList / private void doExportExcel(MethodInvocation invocation, Collection resultList) { //1、取 注记上需要导出的Excel文件名称,以及定义的导出实体类型 Export export = invocation.getMethod().getAnnotation(Export.class) String excelName = nvl(export.value(), invocation.getMethod().getName()) Class beanClazz = export.beanType() //2、生成EXCEL文件写入器 ExcelWriter excelWriter = createExcelWriter(beanClazz) try { excelWriter.write(resultList) //3、将EXCEL文件写入HTTP响应流 HttpServletResponse httpServletResponse = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse() final OutputStream output = httpServletResponse.getOutputStream() httpServletResponse.setContentType(#34application/vnd.ms-excelcharset=utf-8#34) httpServletResponse.setHeader(#34Content-Disposition#34, #34attachmentfilename=#34 + excelName + #34filename=utf-8#39#39#34 + URLEncoder.encode(excelName, #34UTF-8#34)) //4、刷新缓存,提交数据 excelWriter.flush(output, true) } catch (Exception e) { throw new ServiceException(#34Excel导出异常:#34 + e.getMessage(), e) } finally { excelWriter.close() } } / 创建Excel生成器 @param beanClazz 实体类传入进行列名映射 @return / private ExcelWriter createExcelWriter(Class beanClazz) { // 生成列名与实体字段映射 MapltString, Stringgt headerAlias = this.builExportHeaderAlias(beanClazz) // 创建大文件写入器 ExcelWriter excelWriter = ExcelUtil.getBigWriter() excelWriter.setHeaderAlias(headerAlias) // 设置Excel文件样式 PojoMeta pojoMeta = PojoCache.getPojoMeta(beanClazz) ListltPojoFieldgt fieldList = pojoMeta.getFields() // 设置列宽 for (int i = 0 i lt fieldList.size() i++) { PojoField pojoField = fieldList.get(i) excelWriter.setColumnWidth(i, pojoField.getWidth()) } // 设置冻结行 excelWriter.setFreezePane(1) return excelWriter }
从如上可看出builExportHeaderAlias是用来生成实体entity字段与Excel列名的映射,代码如下
/ 构建导出模板模板映射 @param entityClazz 实体类 @return / private MapltString, Stringgt builExportHeaderAlias(Class entityClazz) { PojoMeta pojoMeta = PojoCache.getPojoMeta(entityClazz) ListltPojoFieldgt fieldList = pojoMeta.getFields() // 使用LinkedHashmap保证映射字段有序 MapltString, Stringgt headerAliasMap = new LinkedHashMapltgt() for (int i = 0 i lt fieldList.size() i++) { PojoField pojoField = fieldList.get(i) if (!pojoField.isExport()) { continue } headerAliasMap.put(fieldList.get(i).getName(), fieldList.get(i).getDisplayName()) } return headerAliasMap }其中headerAliasMap存储的就是Key为实体字段名称,而Value即为Excel列显示的名称(注笔者所在公司为跨国公司,提示语及列名需支持多语言)
3、最终的程序执行结果
按当初设计,实体中仅需要导出(用户类型userType、登录名userName、姓名userType、员工编号employeeCode)这四个字段,程序执行完成后,结果如下
导出结果异常截图
从图上可看出,多导出了一列passwordEncrypted,使用debug检查了字段映射的运行过程,的确只做了四个字段的映射
字段映射执行结果
那问题是,到底调用hutool的ExcelWriter的问题出在哪呢?实体 有7个字段,4个要导出,还有3个不需要导出,为什么就多了passwordEncrypted字段呢?还有为什么会出现在Excel列表的之一个位置呢?
二、排查过程
带着如下之一大点的问题,通过查看Hutool工具的源码,发现调用hutool的excelWriter.write(resultList)时,是将实体的字段属性根据headerAlias映射表,设置到TreeMap实例中,在其构造函数中使用的是在其构造函数中使用的是IndexedComparator比较器
Hutool写入数据截图
/ 获取单例的别名比较器,比较器的顺序为别名加入的顺序 @return Comparator @since 4.1.5 / private ComparatorltStringgt getCachedAliasComparator() { if (MapUtil.isEmpty(this.headerAlias)) { return null } ComparatorltStringgt aliasComparator = this.aliasComparator if (null == aliasComparator) { SetltStringgt keySet = this.headerAlias.keySet() aliasComparator = new IndexedComparatorltgt(keySet.toArray(new String[0])) this.aliasComparator = aliasComparator } return aliasComparator }
IndexedComparator比较器具体逻辑
TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的
从图上可看出,TreeMap的Entry对象是按照Key值按从自定义的小到大顺序(在aliasMap中有存在映射,则按出现的顺序依次排序,没有出现的字段,则都是-1,排到前面之一个)进行排序,相信到此为止,应该对之一点提到的几个灵魂级别的拷问的解释就豁然开朗了
还有3个不需要导出,为什么就多了passwordEncrypted字段呢?还有为什么会出现在Excel列表的之一个位置呢?
--因为passwordEncrypted在类字段除了需要导出的4个字段外,位置在之一个,由于没有在定义的aliasMap中,所以排序值是-1,排到了之一个。
三、解决思路
通过第二点,已知道问题所在点,这时就要想着如何解决了,本着相信Hutool工具功能很全,也不可能会犯这种常见错误(实体中没有出现在Excel列名映射表中的字段都会凭空生成一列)。于是排查官方的技术文档,示例如下
// 通过工具类创建writerExcelWriter writer = ExcelUtil.getWriter(#34d:/writeBeanTest.xlsx#34)//自定义标题别名writer.addHeaderAlias(#34name#34, #34姓名#34)writer.addHeaderAlias(#34age#34, #34年龄#34)writer.addHeaderAlias(#34score#34, #34分数#34)writer.addHeaderAlias(#34isPass#34, #34是否通过#34)writer.addHeaderAlias(#34examDate#34, #34考试时间#34)// 默认的,未添加alias的属性也会写出,如果想只写出加了别名的字段,可以调用此 排除之writer.setOnlyAlias(true)// 合并单元格后的标题行,使用默认标题样式writer.merge(4, #34一班成绩单#34)// 一次性写出内容,使用默认样式,强制输出标题writer.write(rows, true)// 关闭writer,释放内存writer.close()
通过代码与官方的例子代码对比,相信大家已经猜到解决思路,我和官方相差了writer.setOnlyAlias(true)通过把这句加到代码中,最终生成EXCEL代码调整如下
调整后生成EXCEL代码
执行验证,程序执行结果正常,导出结果与预期一致,没有再多出一列。
最终导出执行结果
四、
碰到问题需要冷静,排查BUG有种断案,剥丝抽茧的毅力和决心,从问题出现到问题最终解决后的豁然开朗,原来如此的喜悦,我相信是所有喜爱编程的技术人员都会有的感觉。
excelwriter如何调整工具位置 excelwriter 转换为excel文件