java 导入导出excel表格
业务上有需求上传excel表格并读取内容,本文记录一下该方法
表格导入
引入相应的工具包
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.4.0</version>
</dependency>
要清楚表格的格式,这样才能建对应的实体类,例如,表格如下:
要明确有3列,每列标题是什么,从第2行开始是正式的数据。
创建对应的实体类
注意使用@Excel注解,name即表格中列的名称,orderNum是列的顺序
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Province {
@Excel(name = "id", orderNum = "0")
private String id;
@Excel(name = "countryCode", orderNum = "1")
private String countryCode;
@Excel(name = "countryName", orderNum = "2", type = 10)
private String countryName;
}
controller层写方法接收excel文件并解析
本文举例比较简单,实际项目中可以封装一个解析excel的工具类,controller层收到请求后在service层调用工具类解析,再处理相关逻辑。本文直接在controller层解析了,不写那么麻烦了。
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.Vo.Province;
import org.apache.commons.compress.utils.Lists;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@RestController
public class TestController {
@PostMapping("/test/import")
public void getList(@RequestPart MultipartFile file) {
ImportParams params = new ImportParams();
//从第一行开始解析
params.setHeadRows(1);
List<Province> provinces = Lists.newArrayList();
try {
//解析后每一行转换成Province对象
provinces = ExcelImportUtil.importExcel(file.getInputStream(), Province.class, params);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(provinces);
}
}
运行方法进行测试
引入swagger或者使用postman或其他方法调用该接口,传入excel,看是否打印正确。
本文使用postman进行测试,如图:
控制台输出的结果如图,已经正确读到excel的内容。
表格导出
创建对应的实体类
与导入表格类似,创建要导出表格的实体类,注意使用@Excel注解,标明列明和第几列。
比如我想导出一个手机品牌的excel,第一列是序号,第二列是手机品牌名称。
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PhoneVo {
@Excel(name = "序号", orderNum = "1")
private int code;
@Excel(name = "手机品牌名称", orderNum = "1")
private String name;
}
controller层写方法导出
本文简写,直接在controller层写逻辑方法了,可按实际情况放到service层写。
比如我用一个枚举列举所有手机品牌,将枚举的内容导出。
@Getter
public enum PhoneEnum {
APPLE(0,"苹果"),
HUAWEI(1,"华为"),
MI(2,"小米"),
OPPO(3,"oppo"),
SAMSUNG(4,"三星"),
;
private int code;
private String type;
PhoneEnum(int code, String type) {
this.code = code;
this.type = type;
}
}
controller层先把枚举内容转换成list,然后调工具类接口生成excel。
@RestController
@RequestMapping("/")
public class TestController {
@GetMapping("/test/download")
public void getExcel(HttpServletResponse response) throws Exception {
// 表格名称
String fileName = "手机品牌.xls";
// 导出的表格title和sheet页名
ExportParams exportParams = new ExportParams("手机品牌", "phone");
// 导出的内容的list
List<PhoneVo> list = getList();
// 传参是要转换的对象类型,和list
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, PhoneVo.class, list);
if (workbook != null) {
try {
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
workbook.write(response.getOutputStream());
} catch (IOException e) {
throw new Exception(e.getMessage());
} finally {
workbook.close();
}
}
}
private List<PhoneVo> getList(){
List<PhoneVo> res = Lists.newArrayList();
for (PhoneEnum phoneEnum : PhoneEnum.values()) {
res.add(PhoneVo.builder().code(phoneEnum.getCode()).name(phoneEnum.getType()).build());
}
return res;
}
}
测试
启动工程,在浏览器中输入url,回车,即可下载excel。
导出结果如图片所示
@Excel注解参数介绍
@Excel 注解源码对于参数的介绍
package cn.afterturn.easypoi.excel.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel 导出基本注释
* @author JueYue
* 2014年6月20日 下午10:25:12
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {
/**
* 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式
*/
public String databaseFormat() default "yyyyMMddHHmmss";
/**
* 导出的时间格式,以这个是否为空来判断是否需要格式化日期
*/
public String exportFormat() default "";
/**
* 导入的时间格式,以这个是否为空来判断是否需要格式化日期
*/
public String importFormat() default "";
/**
* 时间格式,相当于同时设置了exportFormat 和 importFormat
*/
public String format() default "";
/**
* 时间时区
*/
public String timezone() default "";
/**
* 数字格式化,参数是Pattern,使用的对象是DecimalFormat
*/
public String numFormat() default "";
/**
* 导出时在excel中每个列的高度 单位为字符,一个汉字=2个字符
* 优先选择@ExportParams中的 height
*/
@Deprecated
public double height() default 10;
/**
* 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的
*/
public int imageType() default 1;
/**
* 文字后缀,如% 90 变成90%
*/
public String suffix() default "";
/**
* 是否换行 即支持\n
*/
public boolean isWrap() default true;
/**
* 合并单元格依赖关系,比如第二列合并是基于第一列 则{1}就可以了
*/
public int[] mergeRely() default {};
/**
* 纵向合并内容相同的单元格
*/
public boolean mergeVertical() default false;
/**
* 导出时,对应数据库的字段 主要是用户区分每个字段, 不能有annocation重名的 导出时的列名
* 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用
*/
public String name();
/**
* 导出时,表头双行显示,聚合,排序以最小的值参与总体排序再内部排序
* 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用
* 优先弱与 @ExcelEntity 的name和show属性
*/
public String groupName() default "";
/**
* 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row)
*/
public boolean needMerge() default false;
/**
* 展示到第几个可以使用a_id,b_id来确定不同排序
*/
public String orderNum() default "0";
/**
* 值得替换 导出是{a_id,b_id} 导入反过来,所以只用写一个
*/
public String[] replace() default {};
/**
* 字典名称
*/
public String dict() default "";
/**
* 下拉列表,仅支持replace和dict,dict优先
* @return
*/
public boolean addressList() default false;
/**
* 导入路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/
*
*/
public String savePath() default "/excel/upload/img";
/**
* 导出类型 1 是文本 2 是图片,3 是函数,10 是数字, 11 特殊符号 默认是文本
*/
public int type() default 1;
/**
* 导出时在excel中每个列的宽 单位为字符,一个汉字=2个字符 如 以列名列内容中较合适的长度 例如姓名列6 【姓名一般三个字】
* 性别列4【男女占1,但是列标题两个汉字】 限制1-255
*/
public double width() default 10;
/**
* 是否自动统计数据,如果是统计,true的话在最后追加一行统计,把所有数据都和
* 这个处理会吞没异常,请注意这一点
* @return
*/
public boolean isStatistics() default false;
/**
* 这个是不是超链接,如果是需要实现接口返回对象
* @return
*/
public boolean isHyperlink() default false;
/**
* 导入时会校验这个字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel
* 本意是想用true的,想想还是false比较好
* 可以使用a_id,b_id来确实是否使用
* @return
*/
public String isImportField() default "false";
/**
* 固定的某一列,解决不好解析的问题
* @return
*/
public int fixedIndex() default -1;
/**
* 是否需要隐藏该列
* @return
*/
public boolean isColumnHidden() default false;
/**
* 枚举导出使用的字段
* @return
*/
public String enumExportField() default "";
/**
* 枚举导入使用的函数
* @return
*/
public String enumImportMethod() default "";
/**
* 数据脱敏规则
* 规则1: 采用保留头和尾的方式,中间数据加星号
* 如: 身份证 6_4 则保留 370101********1234
* 手机号 3_4 则保留 131****1234
* 规则2: 采用确定隐藏字段的进行隐藏,优先保留头
* 如: 姓名 1,3 表示最大隐藏3位,最小一位
* 李 --> *
* 李三 --> 李*
* 张全蛋 --> 张*蛋
* 李张全蛋 --> 李**蛋
* 尼古拉斯.李张全蛋 -> 尼古拉***张全蛋
* 规则3: 特殊符号后保留
* 如: 邮箱 1~@ 表示只保留第一位和@之后的字段
* afterturn@wupaas.com -> a********@wupaas.com
* 复杂版本请使用接口
* {@link cn.afterturn.easypoi.handler.inter.IExcelDataHandler}
*/
public String desensitizationRule() default "";
}
测试几个常用的参数
修改导出excel例子中的PhoneVo,用来测试suffix,format,replace参数
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PhoneVo {
@Excel(name = "序号", orderNum = "1")
private int code;
// suffix:后面加上“手机” replace:replace = {"展示数据_替换数据"}
@Excel(name = "手机品牌名称", orderNum = "1",suffix = "手机", replace = {"OPPO_oppo"})
private String name;
// format指定时间转换的格式
@Excel(name = "时间", orderNum = "2",format = "yyyy-MM-dd-HH:mm:ss")
private Timestamp time;
}
controller层方法修改如下
private List<PhoneVo> getList() {
List<PhoneVo> res = Lists.newArrayList();
for (PhoneEnum phoneEnum : PhoneEnum.values()) {
res.add(PhoneVo.builder().code(phoneEnum.getCode()).name(phoneEnum.getType()).time(new Timestamp(System.currentTimeMillis())).build());
}
return res;
}
其余不动,在浏览器中输入url,回车,查看下载的excel。
相关文章
暂无评论...