EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目
读写基本分为两种方式
- 采用创建对象的方式
- 不创建对象进行操作
导入依赖:
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.0.5</version>
</dependency>
本文将myql中city表作为读写demo的数据源
方式一:不创建对象
写入Excel
mybatis:语句编写
<sql id="exportSql">
c.ID "城市编号",c.Name "城市名称",c.CountryCode "城市编码",
c.District "地理区域",
c.Population "人口数量",
status "状态",sex "性别",birthday "生日",createTime "时间"
</sql>
<select id="getAll" parameterType="map" resultType="map">
select
<include refid="exportSql"/>
from city as c
</select>
数据需要处理:
/**
* 不创建对象获取数据
*
* @return
*/
private List<List<Object>> dataList2() {
List<List<Object>> list = new ArrayList<List<Object>>();
List<Map<String, Object>> all = cityDao.getAll();
all.forEach(t -> {
Map<String, Object> t1 = t;
List<Object> data = new ArrayList<Object>();
List<List<String>> lists = head2();
lists.forEach(s -> {
Object o = t1.get(s.get(0));
if (o instanceof Date) { //时间类型的不能处理,直接转为String类型
String s1 = String.valueOf(o);
o=s1;
}
data.add(o);
});
list.add(data);
});
return list;
}
/**
* excel 第一行数据
*
* @return
*/
private List<List<String>> head2() {
List<String> strings = Arrays.asList(new String[]
{"城市编号", "城市名称", "城市编码", "地理区域", "人口数量", "状态", "性别", "生日", "时间"});
List<List<String>> list = new ArrayList<List<String>>();
strings.forEach(t -> {
list.add(Collections.singletonList(t));
});
return list;
}
测试:
/**
* 不创建对象简单导出功能-----------------------------------
*/
@Test
public void exportByData() {
// dataList2();
String file = "C:\\Users\\lhl\\Desktop\\tem.xlsx";
EasyExcel.write(file).head(head2()).sheet("模板").doWrite(dataList2());
}
结果:下面数据太多不再展开
读取Excel
创建监听类:每一行数据都会读到
public class NoModelDataListener extends AnalysisEventListener<Map<Integer, Object>> {
/**
* 每隔100条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
List<Map<Integer, Object>> list = new ArrayList<Map<Integer, Object>>();
@Override
public void invoke(Map<Integer, Object> data, AnalysisContext context) {
System.out.println("----------------读取excel数据-------------------------");
System.out.println(data);
list.add(data);
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
}
/**
* 加上存储数据库
*/
private void saveData() {
System.out.println("存储数据库");
}
@Test
public void fadfda(){
// System.out.println("hello world !!!");
try {
//创建一个URL实例
URL url = new URL("https://mp.weixin.qq.com/s/ZGjVb3gwYuj3mGv0sCyAbw");
try {
//通过URL的openStrean方法获取URL对象所表示的自愿字节输入流
InputStream is = url.openStream();
InputStreamReader isr = new InputStreamReader(is,"utf-8");
//为字符输入流添加缓冲
BufferedReader br = new BufferedReader(isr);
String data = br.readLine();//读取数据
while (data!=null){//循环读取数据
System.out.println(data);//输出数据
data = br.readLine();
}
br.close();
isr.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
测试:
/**
* 读取excel
*/
@Test
public void readExcelByNoObj(){
// 这里 只要,然后读取第一个sheet 同步读取会自动finish
String file = "C:\\Users\\lhl\\Desktop\\tem.xlsx";
EasyExcel.read(file, new NoModelDataListener()).sheet().doRead();
}
效果:
excel简易的读取到此实现了,没有多复杂的处理。 当我们遇到复杂的数据处理的时候,更多还是要创建对象来实现excel数据的导入和读取。
方式二:创建对象
写入Excel
创建实体类:ExportExcel,get 、set方法不再赘述
@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)
public class ExportExcel {
@ExcelProperty("城市编号")
private Integer id;
@ExcelProperty("城市名称")
private String name;
/**
* 宽度为50
*/
// @ColumnWidth(50) 可以单个列进行设置
@ExcelProperty("城市编码")
private String countryCode;
@ExcelProperty("地区")
private String district;
@ExcelProperty("人口")
private Integer population;
@ExcelProperty("状态")
private Integer status;
@ExcelProperty("性别")
private String sex;
@DateTimeFormat("yyyy年MM月dd日")
@ExcelProperty("生日")
private Date birthday;
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty("时间")
private Date createTime;
...................................
}
测试:效果相同,不再赘述
mybatis层:sql是查询所有不再赘述:
/**
* ---------------------------------------------
* 创建对象导出-----非常方便
*/
@Test
public void fdsasfa() {
List<ExportExcel> list = cityDao.exportAll();
String file = "C:\\Users\\lhl\\Desktop\\tem.xlsx";
EasyExcel.write(file, ExportExcel.class).sheet("模板").doWrite(list);
}
读取Excel
创建监听类:
// 有个很重要的点 ModelDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class ModelDataListener extends AnalysisEventListener<ExportExcel> {
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<ExportExcel> list = new ArrayList<ExportExcel>();
private CityDao cityDao;//可以操作数据库存储数据
public ModelDataListener(CityDao demoDAO) {
System.out.println("-------这个获取传入的dao层-------");
List<ExportExcel> all = demoDAO.exportAll();
all.forEach(t-> System.out.println(t));
this.cityDao=demoDAO;
}
/**
* 这个每一条数据解析都会来调用
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
*/
@Override
public void invoke(ExportExcel data, AnalysisContext analysisContext) {
// System.out.println(data);
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
}
/**
* 加上存储数据库
*/
private void saveData() {
}
}
测试:结果正确
/**
* 按照创建对象的读取
*/
@Test
public void readByModel(){
String file = "C:\\Users\\lhl\\Desktop\\tem.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(file, ExportExcel.class, new ModelDataListener(cityDao)).sheet().doRead();
}
特别注意:由于不创建对象,我没有正确使用,将日期等格式转成了字符串,不要串用,否则会报监听错误。
更严密用法,监听类可以使用泛型,不用每个都创建。
其他更多用法:使用说明 · 语雀
ServletOutputStream fo=response.getOutputStream();
response.addHeader("Content-Disposition","attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setContentType("application/vnd.ms-excel;charset=utf-8");