首页 > 其他分享 >2.自定义@Excel注解实现数据Excel形式导入导出

2.自定义@Excel注解实现数据Excel形式导入导出

时间:2023-04-21 20:23:00浏览次数:57  
标签:String 自定义 default 导出 Excel 导入 public val

https://zzpc.net/content/206150.html

前言

  这几天在学习如何使用自定义注解实现Excel格式数据导入导出,参考的还是若依框架里面的代码,由于是初学,所以照猫画虎呗,但是难受的是需要复制并根据自己项目修改作者自定义的工具类以及导入这些工具类的依赖包。由于吃了这个苦,我决定把这个艰辛的CV操作通过一张逻辑图来表达,方便我以后复用。下面证实开始介绍这个功能的实现,但是由于对项目中的只是很不了解,我这里简单实现,并简单讲解,深层次的代码我会给出,后续会继续运用讲解。整篇博客分为两个部分,一部分是数据的导出,一部分是数据的导入。本文项目链接:WomPlus: 结合若依项目对原始工单项目内容进行增强 (gitee.com)

1.所需要的依赖

  在进行项目前,我们需要导入依赖才能引用具体的功能,因此第一步就是导入依赖了,这里除了Excel需要的依赖,一些工具类的依赖也需要导入,为什么呢?因为作者的项目写的很细致,比如在咋们的java中自带有StringUtils工具类,但是作者细致地自己写了一个,可见其基础功能之深呀!

 
<!--Excel工具类依赖-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency> <!--常用工具类依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency> <!--io常用工具依赖-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency> <!--解决@ConfigurationProperties(prefix = "wo")的
Springboot Configuration Annotation Processor not found in classPath问题依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

所需依赖

2.修改配置文件

  引入依赖是第一步,修改配置文件就是我们的第二步了,在这里修改配置文件是因为在该功能的数据导入,作者是在自己自定义的RuoYiConfig中通过


@ConfigurationProperties(prefix = "wo")注解获取配置文件中profile属性值,即我们上传下载文件的地址。我这里使用的是properties为后缀的文件,配置文件为yaml的请根据自己文件类型修改
 
//添加自己的项目配置类WoConfig需要配置的项目名和导出Excel形式数据下载路径
#项目相关配置
wo.name=WO
wo.profile=G:/Desktop/base_study/project/myself_springboot_project/wom-plus/uploadPath

3.数据以Excel格式导出的Controller层

 
@PostMapping("/export")
@ResponseBody
public AjaxResult export(@RequestParam(value = "name", required = false) String username){
List<SysUser> list = userDetailsService.getUserListByUsername(username);
ExcelUtil<SysUser> util = new ExcelUtil<>(SysUser.class);
return util.exportExcel(list, "用户数据");
}
  在UserController的export()方法中,首先传入一个Class.class作为ExcelUtils类的参数来new一个ExcelUtils对象util,然后传递一个Class的list对象和表名称到util的exportExcel()方法中返回Class的Excel信息。

3.1 ExcelUtils的有参构造

 
//1.传入需要Excel导出类的Class.class,返回一个ExcelUtil<Class>对象
public ExcelUtil(Class<T> clazz)
{
this.clazz = clazz;
}

  这里就是传入一个Class类来创建该类的Excel对象,本篇博客围绕着SysUser类实现该功能的,因此这里传入SysUser.class

3.2 exportExcel(List<T>list, String sheetName)

 
public AjaxResult exportExcel(List<T> list, String sheetName)
{
return exportExcel(list, sheetName, StringUtils.EMPTY);
}

  在方法中,调用exportExcel(List<SysUser>list, String sheetName, String title)封装Excel格式数据导出

3.3 exportExcel(List<T> list, String sheetName, String title)

 
public AjaxResult exportExcel(List<T> list, String sheetName, String title)
{
this.init(list, sheetName, title, Type.EXPORT);
return exportExcel();
}

  本文方法首先初始化要创建的Excel表格,然后返回exportExcel()方法来讲要导出的数据写进前面的profile路径中

3.3.1 init(list, sheetName, title, Type.EXPORT)

 
public void init(List<T> list, String sheetName, String title, Type type)
{
if (list == null)
{
list = new ArrayList<T>();
}
this.list = list;
this.sheetName = sheetName;
this.type = type;
this.title = title;
//根据要导出Excel的实体类的字段创建字段Class类所需要的Excel字段
createExcelField();
createWorkbook();//创建一个工作薄
createTitle();//创建Excel第一行标题
createSubHead();//创建对象子列表名称
}

3.3.2 exportExcel()

 
public AjaxResult exportExcel()
{
OutputStream out = null;
try
{
writeSheet();//写入数据到Sheet
String filename = encodingFilename(sheetName);//编辑文件名
//getAbsoluteFile(filename)根据文件名称获取下载路径
//创建一个输出流
out = new FileOutputStream(getAbsoluteFile(filename));
wb.write(out);//写入Excel信息到该路径
return AjaxResult.success(filename);//返回导出成功信息
}
catch (Exception e)
{
log.error("导出Excel异常{}", e.getMessage());
throw new UtilException("导出Excel失败,请联系网站管理员!");
}
finally
{
IOUtils.closeQuietly(wb);//关闭工作薄对象输出流
IOUtils.closeQuietly(out);//关闭输出流
}
}

导出数据库数据到指定路径

3.4 @Excel和@Excels注解

 
package com.ku.wo.framework.aspectj.lang.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.ku.wo.common.utils.poi.ExcelHandlerAdapter; /**
* 自定义导出Excel数据注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//作用在什么地方
public @interface Excel
{
/**
* 导出时在excel中排序
*/
public int sort() default Integer.MAX_VALUE; /**
* 导出到Excel中的名字.
*/
public String name() default ""; /**
* 日期格式, 如: yyyy-MM-dd
*/
public String dateFormat() default ""; /**
* 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
*/
public String dictType() default ""; /**
* 读取内容转表达式 (如: 0=男,1=女,2=未知)
*/
public String readConverterExp() default ""; /**
* 分隔符,读取字符串组内容
*/
public String separator() default ","; /**
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
*/
public int scale() default -1; /**
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
*/
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; /**
* 导出时在excel中每个列的高度 单位为字符
*/
public double height() default 14; /**
* 导出时在excel中每个列的宽 单位为字符
*/
public double width() default 16; /**
* 文字后缀,如% 90 变成90%
*/
public String suffix() default ""; /**
* 当值为空时,字段的默认值
*/
public String defaultValue() default ""; /**
* 提示信息
*/
public String prompt() default ""; /**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {}; /**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
public boolean needMerge() default false; /**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/
public boolean isExport() default true; /**
* 另一个类中的属性名称,支持多级获取,以小数点隔开
*/
public String targetAttr() default ""; /**
* 是否自动统计数据,在最后追加一行统计数据总和
*/
public boolean isStatistics() default false; /**
* 导出类型(0数字 1字符串 2图片)
*/
public ColumnType cellType() default ColumnType.STRING; /**
* 导出列头背景颜色
*/
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; /**
* 导出列头字体颜色
*/
public IndexedColors headerColor() default IndexedColors.WHITE; /**
* 导出单元格背景颜色
*/
public IndexedColors backgroundColor() default IndexedColors.WHITE; /**
* 导出单元格字体颜色
*/
public IndexedColors color() default IndexedColors.BLACK; /**
* 导出字段对齐方式
*/
public HorizontalAlignment align() default HorizontalAlignment.CENTER; /**
* 自定义数据处理器
*/
public Class<?> handler() default ExcelHandlerAdapter.class; /**
* 自定义数据处理器参数
*/
public String[] args() default {}; /**
* 字段类型(0:导出导入;1:仅导出;2:仅导入)
*/
Type type() default Type.ALL; public enum Type
{
ALL(0), EXPORT(1), IMPORT(2);
private final int value; Type(int value)
{
this.value = value;
} public int value()
{
return this.value;
}
} public enum ColumnType
{
NUMERIC(0), STRING(1), IMAGE(2);
private final int value; ColumnType(int value)
{
this.value = value;
} public int value()
{
return this.value;
}
}
}
//@Excels
package com.ku.wo.framework.aspectj.lang.annotation; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* Excel注解集
*
* @author ruoyi
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Excels
{
Excel[] value();
}

@Excel和@Excels

3.5 运行结果

 

 

4.数据以Excel格式导出的Controller层

 
@PostMapping("/importData")
@ResponseBody
public AjaxResult importData(MultipartFile file) throws Exception {
ExcelUtil<SysUser> util = new ExcelUtil<>(SysUser.class);
List<SysUser> userList = util.importExcel(file.getInputStream());
String message = userDetailsService.importUser(userList);
return AjaxResult.success(message);
}
  在UserController的importData()方法中,首先传入一个Class.class作为ExcelUtils类的参数来new一个ExcelUtils对象util,然后传入一个要导入的Excel形式的表格数据作为utils.importData()函数的输入流is,importData()方法根据is返回Excel表格数据中的Class类的list对象,接着传入一个Class的list对象和一个关于是否支持更新数据库已有数据的布尔变量到importUser()方法中,该方法根据这两个参数来返回插入成功或失败的信息message,最后传入message到AjaxResult.success()方法返回导入数据成功消息。

4.1 importExcel(InputStream)

 
public List<T> importExcel(InputStream is) throws Exception
{
return importExcel(is, 0);
} public List<T> importExcel(InputStream is, int titleNum) throws Exception
{
return importExcel(StringUtils.EMPTY, is, titleNum);
} //核心实现代码
public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception
{
this.type = Type.IMPORT;
this.wb = WorkbookFactory.create(is);
List<T> list = new ArrayList<T>();
// 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
if (sheet == null)
{
throw new IOException("文件sheet不存在");
}
boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
Map<String, PictureData> pictures;
if (isXSSFWorkbook)
{
pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
}
else
{
pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);
}
// 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
int rows = sheet.getLastRowNum(); if (rows > 0)
{
// 定义一个map用于存放excel列的序号和field.
Map<String, Integer> cellMap = new HashMap<String, Integer>();
// 获取表头
Row heard = sheet.getRow(titleNum);
for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
{
Cell cell = heard.getCell(i);
if (StringUtils.isNotNull(cell))
{
String value = this.getCellValue(heard, i).toString();
cellMap.put(value, i);
}
else
{
cellMap.put(null, i);
}
}
// 有数据时才处理 得到类的所有field.
List<Object[]> fields = this.getFields();
Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
for (Object[] objects : fields)
{
Excel attr = (Excel) objects[1];
Integer column = cellMap.get(attr.name());
if (column != null)
{
fieldsMap.put(column, objects);
}
}
for (int i = titleNum + 1; i <= rows; i++)
{
// 从第2行开始取数据,默认第一行是表头.
Row row = sheet.getRow(i);
// 判断当前行是否是空行
if (isRowEmpty(row))
{
continue;
}
T entity = null;
for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet())
{
Object val = this.getCellValue(row, entry.getKey()); // 如果不存在实例则新建.
entity = (entity == null ? clazz.newInstance() : entity);
// 从map中得到对应列的field.
Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1];
// 取得类型,并根据对象类型设置值.
Class<?> fieldType = field.getType();
if (String.class == fieldType)
{
String s = Convert.toStr(val);
if (StringUtils.endsWith(s, ".0"))
{
val = StringUtils.substringBefore(s, ".0");
}
else
{
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat))
{
val = parseDateToStr(dateFormat, val);
}
else
{
val = Convert.toStr(val);
}
}
}
else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
{
val = Convert.toInt(val);
}
else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
{
val = Convert.toLong(val);
}
else if (Double.TYPE == fieldType || Double.class == fieldType)
{
val = Convert.toDouble(val);
}
else if (Float.TYPE == fieldType || Float.class == fieldType)
{
val = Convert.toFloat(val);
}
else if (BigDecimal.class == fieldType)
{
val = Convert.toBigDecimal(val);
}
else if (Date.class == fieldType)
{
if (val instanceof String)
{
val = DateUtils.parseDate(val);
}
else if (val instanceof Double)
{
val = DateUtil.getJavaDate((Double) val);
}
}
else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
{
val = Convert.toBool(val, false);
}
if (StringUtils.isNotNull(fieldType))
{
String propertyName = field.getName();
if (StringUtils.isNotEmpty(attr.targetAttr()))
{
propertyName = field.getName() + "." + attr.targetAttr();
}
else if (StringUtils.isNotEmpty(attr.readConverterExp()))
{
val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
}
else if (StringUtils.isNotEmpty(attr.dictType()))
{
val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
}
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
val = dataFormatHandlerAdapter(val, attr);
}
else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
{
PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
if (image == null)
{
val = "";
}
else
{
byte[] data = image.getData();
val = FileUtils.writeImportBytes(data);
}
}
ReflectUtils.invokeSetter(entity, propertyName, val);
}
}
list.add(entity);
}
}
return list;
}

importExcel

  该方法主要是通过传入的文件获取Excel表格中的数据,这里是真正导入数据的核心,我暂时还没想清楚。

4.2 importUser(InputStream)

 
String importUser(List<SysUser> userList);
@Override
public String importUser(List<SysUser> userList) {
//此处的isNull判断的是一个对象,此时我传入的是一个用户list对象
if (StringUtils.isNull(userList) || userList.size() == 0){
throw new ServiceException("导入用户数据不能为空!");
}
int successNum = 0;
//StringBuilder字符拼接工具类,是一个可变类不安全,StringBuffer是一个不可变的安全字符拼接工具类
StringBuilder successMsg = new StringBuilder();
for (SysUser user: userList) {
//因为前面验证了该list对象不为空,故直接插入
userMapper.insertUser(user);
successNum++;
successMsg.append("<br/>" + successNum + "、账号 " + user.getUsername() + " 导入成功");
}
successMsg.insert(0, "恭喜你,数据已经全部导入成功!共" + successNum + "条,数据如下:");
return successMsg.toString();
}

Excel表格数据插入到对应数据库的数据表

  该方法实现将Excel中获取的数据导入到对应数据库的数据表中,并返回导入成功的响应信息。

4.3 运行结果

 

5.参考项目连链接

RuoYi: 基于SpringBoot的权限管理系统 易读易懂、界面简洁美观。 核心技术采用Spring、MyBatis、Shiro没有任何其它重度依赖。直接运行即可用 (gitee.com)

 

总结

以上是真正的电脑专家为你收集整理的2.自定义@Excel注解实现数据Excel形式导入导出的全部内容,希望文章能够帮你解决所遇到的问题。                             

标签:String,自定义,default,导出,Excel,导入,public,val
From: https://www.cnblogs.com/chuangsi/p/17341696.html

相关文章

  • ruoyi导入导出Excel
    https://www.cnblogs.com/SjhCode/p/excel.htmlruoyi导入导出Excel导入导出excel:使用若依自带的工具ExcelUtil;(例子:SysUserController)导入之前需要生成一个excel模板给用户填写(前端通过接口获取模板名字,再调用下载接口common/download,进行下载);导入业务根据若依的SysUserService......
  • Spring Boot中 自定义注解
    SpringBoot中自定义注解定义一个注解创建一个Java注解,可以使用@interface关键字来定义,例如:less@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceMyAnnotation{Stringvalue()default"";}这个注解称为MyAnnotation,并且它具......
  • 【四二学堂】基于UiBot RPA开发入门---Excel操作
    dimobjExcelWorkBook=""dimiRowsobjExcelWorkBook=Excel.OpenExcel(@res"测试数据一.xlsx",true,"Excel","","")iRows=Excel.GetRowsCount(objExcelWorkBook,"Sheet1")Excel.WriteRow(objExcelWorkBook,&......
  • Excel的列数如何用数字表示?
      本文介绍在Excel表格文件中,用数字而非字母来表示列号的方法。  在日常生活、工作中,我们不免经常使用各种、各类Excel表格文件;而在Excel表格文件中,微软Office是默认用数字表示行数,用字母表示列数的,如下图所示:  而这样就带来一个问题:当一个Excel表格文件的列数相对较多时......
  • excel单元格日期类型改为文本类型
    步骤一:完成第一个单元格的初步转换在源日期列右侧插入一列,准备放置转换后的文本格式日期。然后在第一个日期对应的单元格输入公式“=text(A2,"yyyymmdd")”注:如果日期中还包含时、分、秒,则公式可改成“=text(A2,"yyyymmddhhmmss")”。其中的文本格式可以按自己的要求灵活运用,如“y......
  • phpStorm自定义快捷键,输出代码块,模板
    在开发过程中经常需要打印数据调试,var_dump()或print_r都没办法直观的查看数据,我一般用如下代码打印数据,但是每次手动输入又麻烦,所以设置一个快捷键就能输出一下代码,岂不是一劳永逸:1.进入设置对话框:File->Setting2.接下自定义快捷键:按一下步骤操作完,点击"ok"键![在这......
  • tableViewCell 自定义圆角
     在一个多cell的Tab了View中设置第一个和最后一个cell的圆角其实还是贝塞尔去画给cell加一个view -(void)tableView:(UITableView*)tableViewwillDisplayCell:(UITableViewCell*)cellforRowAtIndexPath:(NSIndexPath*)indexPath{  if(indexPath.row==0&&......
  • 我是不会运行你的代码吗?不,我是不会导入自己的数据!
    常常遇到有人问起看到分享的教程导入数据的方式是data(dune)等直接调用系统的数据,而自己怎么读入自己的数据呢?对于初学者来讲,这确实是个问题。如何准备数据、拿到正确格式的数据并导入后续的代码进行分析,是学习和应用过程中的第一个拦路虎。为什么教程会习惯使用内置数据?简单省事、......
  • Spring 自定义属性编辑器
    装Spring自定义属性编辑器 SpringDI注入的时候可以把普通属性注入进来,但是像Date类型的就无法被识别。这时可以通过Spring的属性编辑器把配置文件中的字符串转化成相应的对象进行注入。Spring有自带的属性编辑器,我们也可以写自定义的属性编辑器 自定义属性编辑器:继承java.bean......
  • Android-自定义控件
    原文地址zhuanlan.zhihu.com残枫cps​目录收起framelayout触发覆盖页面的点击事件SurfaceView自定义控件1.构造函数2.测量View大小(onMeasure)注意:3.确定View大小(onSizeChanged)4.确定子View布局位置(onLayout)5.绘制内容(onDraw)Android自定义控件之自定义组合控件前言:使......