Java Excel的数据导入导出
导入数据时,程序解析和读取数据用,必须要!
EasyExcel简介
easyexcel 是阿里巴巴开源的一款excel 解析工具,底层逻辑也是基于apache poi进行二次开发的。不同的是,再读写数据的时候,采用 sax 模式一行一行解析,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)在并发量很大的情况下,依然能稳定运行!easyexcel支持采用注解方式进行导出、导入!
引入依赖
<!-- EasyExcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
<!--csv文件操作-->
<dependency>
<groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId>
<version>2.0</version>
</dependency>
AnalysisEventListener读取监听
对象命名时严格遵守驼峰命名法,如果忽略可能会导致读取数据失败。
通用读取监听类
import com.baomidou.mybatisplus.toolkit.ReflectionKit;
/**
* excel通用读取监听类
*/
public class ExcelListener<T> extends AnalysisEventListener<T> {
/**
* 自定义用于暂时存储data 可以通过实例获取该值
*/
private final List<T> list = new ArrayList<>();
/**
* 这个每一条数据解析都会来调用
*
* @param data
* @param context
*/
@Override
public void invoke(T data, AnalysisContext context) {
// 如果一行Excel数据均为空值,则不装载该行数据
if (isLineNullValue(data)) {
return;
}
if (context.readRowHolder().getRowIndex() > 2) {
System.out.println("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
String sheetName = context.readWorkbookHolder().getExcelType().equals(ExcelTypeEnum.CSV) ? "CSV" : context.readSheetHolder().getSheetName();
System.out.println(sheetName + " 所有数据解析完成,数据条数:" + list.size());
}
/**
* 返回所有记录
*
* @return
*/
public List<T> getRows() {
return list;
}
/**
* 判断整行单元格数据是否均为空
*/
private boolean isLineNullValue(Object data) {
if (data == null) {
return true;
}
List<Field> fields = Arrays.stream(data.getClass().getDeclaredFields())
.filter(f -> f.isAnnotationPresent(ExcelProperty.class))
.collect(Collectors.toList());
List<Boolean> lineNullList = new ArrayList<>(fields.size());
for (Field key : fields){
if (ReflectionKit.getMethodValue(data, key.getName()) == null) {
lineNullList.add(Boolean.TRUE);
} else {
lineNullList.add(Boolean.FALSE);
}
}
return lineNullList.stream().allMatch(Boolean.TRUE::equals);
}
}
通用非model类型监听
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NoModelDataListener extends AnalysisEventListener<Map<Integer, String>> {
/**
* Excel数据缓存结构
*/
private List<Map<String, String>> list = new ArrayList<>();
/**
* Excel表头(列名)数据缓存结构
*/
private Map<Integer, String> headTitleMap = new HashMap<>();
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
//excel的行号
Integer rowIndex = context.readRowHolder().getRowIndex();
log.info("行号:{},Excel数据:{}", rowIndex, getRowContentWithHeaderKey(data));
list.add(getRowContentWithHeaderKey(data));
}
/**
* 解析表头数据,解析第一行触发
**/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
headTitleMap = new HashMap<>();
for (Integer i : headMap.keySet()) {
String value = "";
if (headMap.get(i) != null) {
value = headMap.get(i).trim();
}
headTitleMap.put(i, value);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
log.info("数据解析完成!");
}
/**
* 按照表头作为map的key,提供Excel行数据。兼容key重复。行乱序
*
* @param data
* @return
*/
public Map<String, String> getRowContentWithHeaderKey(Map<Integer, String> data) {
Map<String, String> dataWithHeader = new IdentityHashMap<>();
data.forEach((index, value) -> {
dataWithHeader.put(headTitleMap.get(index), value);
});
return dataWithHeader;
}
public List<Map<String, String>> getList() {
return list;
}
}
单sheet海量excel的数据读取
根据设置每批次数据量的大小,当list里面数据量达到设置每批次最大数据量时,进行入库操作,海量数据的话,通常每批次1000的速度最快,可以避免内存溢出
public void invoke(CallExcelVO data, AnalysisContext context) {
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
//存储完成清理 list
list.clear();
}
}
读取Excel内容
ExcelListener listener = new ExcelListener();
String fileName = "demo.xlsx";
EasyExcel.read(fileName, DemoData.class, listener).sheet().autoTrim(true).doRead();
List<DemoData> dataList = listener.getRows();
//EasyExcel3.3 导入csv
ExcelListener listener = new ExcelListener();
ExcelReader excelReader = EasyExcel.read(inputStream, listener).build();
String code = CsvReader.getFileCharset(fileName);
System.out.println("文件编码为:" + code);
//excelReader = EasyExcel.read(inputStream, listener).excelType(ExcelTypeEnum.CSV).charset(Charset.forName(code)).build();
inputStream.close();
Integer sheetSize = 1;//csv
if (excelReader != null) {
List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
sheetSize = readSheets.size();
}
for (int sheetNo = 0; sheetNo < sheetSize; sheetNo++) {
ExcelUtil.readExcel(fileName, sheetNo);
}
接收外部参数,需添加构造方法
public ExcelListener(String fileName) {
this.fileName = fileName;
}
ExcelListener parseListener = new ExcelListener("a.xls");
读类
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import java.io.InputStream;
import java.util.List;
/**
* 定制化Excel读写工具类
*/
public class ExcelUtil {
/**
* 单sheet版本Excel读取 从Excel中读取文件,读取的文件是一个DTO类
*
* @param inputStream 文件流
* @param clazz 行数据类型
* @param rowIndex 表示Excel的表头的行数,默认为1,设置为1时,表示第一行是表头,从第二行是表数据
* @param sheetNo sheet编号 从0开始
*/
public static <T> List<T> readExcelOneSheet(InputStream inputStream, final Class<?> clazz, Integer rowIndex, Integer sheetNo) {
// 1.创建监听类
ExcelListener<T> listener = new ExcelListener<>();
// 2.构建工作簿对象的输入流
ExcelReader excelReader = EasyExcel.read(inputStream, clazz, listener).build();
// 3.构建工作表对象的输入流,默认是第一张工作表
ReadSheet readSheet = EasyExcel.readSheet(sheetNo).headRowNumber(rowIndex).build();
// 4.读取信息,每读取一行都会调用监听类的 invoke 方法
excelReader.read(readSheet);
// 5.关闭流,如果不关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
return listener.getRows();
}
/**
* 将Excel中的日期数值转换为对应的日期字符串,单元格格式设置为文本可以看数字。 45457.4227662037 => 2024-06-14 10:08:46.999
*
* @param excelDate Excel中的日期数值,表示从1900年1月1日起的天数与时间的组合。
* @return 对应的日期字符串,格式为yyyy-MM-dd HH:mm:ss。
*/
public static String getExcelDateFormat2Date(double excelDate) {
Calendar calendar = Calendar.getInstance();
//整数部分代表天数(天数取决于所用的日期系统,1900或1904系统)
calendar.set(1900, Calendar.JANUARY, 0);
calendar.add(Calendar.DAY_OF_MONTH, (int) excelDate - 1);
// Calculate the time portion of the day
double fractionOfDay = excelDate - (int) excelDate;
int hours = (int) (fractionOfDay * 24);
int minutes = (int) ((fractionOfDay * 24 * 60) % 60);
int seconds = (int) ((fractionOfDay * 24 * 60 * 60) % 60);
int milliseconds = (int) (((fractionOfDay * 24 * 60 * 60 * 1000) % 1000));
calendar.set(Calendar.HOUR_OF_DAY, hours);
calendar.set(Calendar.MINUTE, minutes);
calendar.set(Calendar.SECOND, seconds);
calendar.set(Calendar.MILLISECOND, milliseconds);
Date date = calendar.getTime();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return sdf.format(date);
}
}
EasyExcel导入invoke函数不执行,不报错
- 重写hasNext方法时写成返回false了
- 使用easyexcel时默认是第一张sheet页,不是第一张sheet
- 将不使用的字段使用@ExcelIgnore标注
- EasyExcel和Lombok结合使用导致的问题,去掉@Accessors(chain = true)
EasyExcel 相关参数
- readListener 监听器,在读取数据的过程中会不断的调用监听器。
- converter 转换器,默认加载了很多转换器。也可以自定义,如果使用的是 registerConverter,那么该转换器是全局的,如果要对单个字段生效,可以在 ExcelProperty 注解的 converter 指定转换器。
- headRowNumber 需要读的表格有几行头数据。默认有一行头,也就是认为第二行开始起为数据。
- head 与 clazz 二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用 class。
- autoTrim 字符串、表头等数据自动 trim。
- sheetNo 需要读取 Sheet 的编码,建议使用这个来指定读取哪个 Sheet。
- sheetName 根据名字去匹配 Sheet,excel 2003 不支持根据名字去匹配。
Excel工具类
根据EasyExcel Model 导出工具类,CSV支持easyexcel获取ExcelProperty
ExportUtil
package net.demo.excel.common.util;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.csvreader.CsvWriter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* @desc: 根据EasyExcel Model 导出工具类
* @Author: Swift
* @Date: 2019-08-26 15:18
*/
public class ExportUtil {
/**
* 获取ExcelProperty Value
* @return
*/
public static <T extends BaseRowModel> String[] getFieldNames(Class<T> tClass) {
Field[] fields = tClass.getDeclaredFields();
List<String> headers = new ArrayList<>();
for (Field field: fields) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
if (property != null) {
String[] s = property.value();
if (s.length > 0) {
headers.add(s[0]);
}
}
}
String[] strings = new String[headers.size()];
headers.toArray(strings);
return strings;
}
/**
* 获取filedName
* @param vo
* @param <T>
* @return
*/
public static <T extends BaseRowModel> String[] getFields(T vo) {
Field[] fields = vo.getClass().getDeclaredFields();
List<String> columns = new ArrayList<>();
for (Field field: fields) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
if (property != null) {
try {
field.setAccessible(true);
columns.add(field.get(vo)==null ? "" : field.get(vo).toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException("写入内容到csv失败!");
}
}
}
String[] strings = new String[columns.size()];
columns.toArray(strings);
return strings;
}
public static <T extends BaseRowModel> void export(String type, List<T> vos, HttpServletResponse response, Class<T> t
) throws IOException {
Boolean isCsv = "csv".equals(type);
String fileName = "data" + CalendarUtils.getCurrentTime();
response.setCharacterEncoding("utf-8");
if (isCsv) {
fileName += ".csv";
response.setContentType("text/csv;charset=UTF-8");
} else {
fileName += ".xlsx";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
ServletOutputStream outputStream = response.getOutputStream();
if (isCsv) {
CsvWriter csvWriter = new CsvWriter(outputStream, ',', Charset.forName("GBK"));
csvWriter.writeRecord(getFieldNames(t));
for (T vo : vos) {
String[] fields = getFields(vo);
csvWriter.writeRecord(fields);
}
csvWriter.close();
} else {
ExcelWriter excelWriter = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX, true);
Sheet sheet = new Sheet(1, 0, t);
excelWriter.write(vos, sheet);
sheet.setAutoWidth(true);
excelWriter.finish();
}
outputStream.flush();
}
public static <T extends BaseRowModel> void export(String type,String fileName, List<T> vos, HttpServletResponse response, Class<T> t
) throws IOException {
Boolean isCsv = "csv".equals(type);
// String fileName = "data" + CalendarUtils.getCurrentTime();
response.setContentType("multipart/form-data");
response.setCharacterEncoding("utf-8");
if (isCsv) {
fileName += ".csv";
} else {
fileName += ".xlsx";
}
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
ServletOutputStream outputStream = response.getOutputStream();
if (isCsv) {
CsvWriter csvWriter = new CsvWriter(outputStream, ',', Charset.forName("GBK"));
csvWriter.writeRecord(getFieldNames(t));
for (T vo : vos) {
String[] fields = getFields(vo);
csvWriter.writeRecord(fields);
}
csvWriter.close();
} else {
ExcelWriter excelWriter = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX, true);
Sheet sheet = new Sheet(1, 0, t);
excelWriter.write(vos, sheet);
sheet.setAutoWidth(true);
excelWriter.finish();
}
outputStream.flush();
}
/**
* 根据Excel模板,批量导入数据
* @param file 导入的Excel
* @param clazz 解析的类型
* @return 解析完成的数据
*/
public static List<?> importExcel(MultipartFile file, Class<?> clazz){
if (file == null || file.isEmpty()){
throw new RuntimeException("没有文件或者文件内容为空!");
}
List<Object> dataList = null;
BufferedInputStream ipt = null;
try {
InputStream is = file.getInputStream();
// 用缓冲流对数据流进行包装
ipt = new BufferedInputStream(is);
// 数据解析监听器
ExcelListener<Object> listener = new ExcelListener<>();
// 读取数据
EasyExcel.read(ipt, clazz,listener).sheet().doRead();
// 获取去读完成之后的数据
dataList = listener.getDataList();
} catch (Exception e){
log.error(String.valueOf(e));
throw new RuntimeException("数据导入失败!" + e);
}
return dataList;
}
}
WriterFactory
导出工厂实现了xlsx与csv 两种方式
package net.demo.excel.common.export;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.demo.excel.common.constants.Constants;
import net.demo.excel.common.constants.ExportTypeEnum;
import net.demo.excel.common.export.csv.CsvExport;
import net.demo.excel.common.export.excel.ExcelExport;
/**
* 导出工厂
*/
@Data
@Slf4j
public class WriterFactory {
/**
* 返回文件流方式
*
* @param key
* @param fileName
* @param response
* @return
*/
public static ExportWriter getExportWriter(String key, String fileName,
HttpServletResponse response) {
ExportWriter exportWriter = null;
try {
OutputStream os = response.getOutputStream();
ExportTypeEnum exportTypeEnum = ExportTypeEnum.findByType(key);
fileName = fileName + exportTypeEnum.getSuffix();
exportWriter = generateExportWriter(exportTypeEnum, os);
response.setContentType("application/force-download");
// 设置文件名
response.addHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
} catch (UnsupportedEncodingException e) {
log.error("UnsupportedEncoding:", e);
} catch (IOException e) {
log.error("IOException:", e);
}
return exportWriter;
}
/**
* 输出到文件方式
*
* @param key
* @param fileName
* @return
*/
public static ExportWriter getExportWriter(String key, String fileName)
throws FileNotFoundException {
ExportTypeEnum exportTypeEnum = ExportTypeEnum.findByType(key);
String filePath =
Constants.EXPORT_TMP_DIR + File.separator + fileName + exportTypeEnum.getSuffix();
File file = new File(filePath);
OutputStream os = new FileOutputStream(file);
ExportWriter exportWriter = generateExportWriter(exportTypeEnum, os);
exportWriter.setFilePath(filePath);
return exportWriter;
}
private static ExportWriter generateExportWriter(ExportTypeEnum exportTypeEnum, OutputStream os) {
ExportWriter exportWriter = null;
switch (exportTypeEnum) {
case EXCEL:
exportWriter = new ExcelExport(os);
break;
case CSV:
exportWriter = new CsvExport(os);
break;
default:
}
return exportWriter;
}
}
ExportModel抽象类
package net.demo.excel.common.export.model;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import com.csvreader.CsvWriter;
/**
* 抽象实体
*/
public abstract class ExportModel {
public abstract ExcelWriter getExcelWriter();
public abstract Sheet getSheet();
public abstract CsvWriter getCsvWriter();
}
CsvModel 实体类
package net.demo.excel.common.export.csv.model;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import com.csvreader.CsvWriter;
import lombok.Data;
import net.demo.excel.common.export.model.ExportModel;
/**
* csv相关实体
*/
@Data
public class CsvModel extends ExportModel {
private CsvWriter csvWriter;
@Override
public ExcelWriter getExcelWriter() {
return null;
}
@Override
public Sheet getSheet() {
return null;
}
@Override
public CsvWriter getCsvWriter() {
return csvWriter;
}
public CsvModel(CsvWriter csvWriter) {
this.csvWriter = csvWriter;
}
}
ExcelModel 实体类
package net.demo.excel.common.export.excel.model;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import com.csvreader.CsvWriter;
import lombok.Data;
import net.demo.excel.common.export.model.ExportModel;
/**
* excel,csv相关实体
*/
@Data
public class ExcelModel extends ExportModel {
private ExcelWriter excelWriter;
private Sheet sheet;
@Override
public ExcelWriter getExcelWriter() {
return excelWriter;
}
@Override
public Sheet getSheet() {
return sheet;
}
@Override
public CsvWriter getCsvWriter() {
return null;
}
public ExcelModel(ExcelWriter excelWriter, Sheet sheet) {
this.excelWriter = excelWriter;
this.sheet = sheet;
}
}
ExportWriter接口
package net.demo.excel.common.export;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.fastjson.JSONArray;
import java.util.List;
import net.demo.excel.common.bean.Column;
/**
* 导出接口
*/
public interface ExportWriter {
public static final int EXPORT_PAGE_SIZE = 5000;
/**
* 写入表头 结构为{"code":"字段名","title":"显示名"}
*
* @param columnList
*/
public void writeTitle(List<Column> columnList);
public <T extends BaseRowModel> void writeTitle(Class<T> t);
/**
* 添加内容 data的Map结构为{"字段名":"字段值"}
*
* @param dataList
* @param columnList
*/
public void appendContent(JSONArray dataList, List<Column> columnList);
public <T extends BaseRowModel> void appendContent(List<T> vos, Class<T> t);
/**
* 关闭文件流
*/
public void close();
public String getFilePath();
public void setFilePath(String filePath);
}
Column
package net.demo.excel.common.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Column {
/**
* 英文名称
*/
private String code;
/**
* 中文名称
*/
private String title;
}
ExcelExport实现类
model.getSheet() 的sheet的初始化在ExcelExport中完成。new sheet(1, 0, ExcelProperty)第3个参数com.alibaba.excel.annotation.ExcelProperty通过注解来生成表头。
package net.demo.excel.common.export.excel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.demo.excel.common.bean.Column;
import net.demo.excel.common.export.ExportWriter;
import net.demo.excel.common.export.excel.model.ExcelModel;
import net.demo.excel.common.export.model.ExportModel;
import net.demo.excel.common.util.ExportUtil;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* excel具体实现
*/
@Slf4j
@Data
public class ExcelExport implements ExportWriter {
private ExportModel model;
private OutputStream os;
private String filePath;
/**
* 初始化
*
* @param os
*/
public ExcelExport(OutputStream os) {
ExcelWriter writer = new ExcelWriter(os, ExcelTypeEnum.XLSX);
Sheet sheet = new Sheet(1, 0);
sheet.setSheetName("1");
this.model = new ExcelModel(writer, sheet);
this.os = os;
}
/**
* 写标题
*
* @param columnList
*/
@Override
public void writeTitle(List<Column> columnList) {
List<List<String>> dataList = new ArrayList<List<String>>();
//组装标题行
for (int i = 0; i < columnList.size(); i++) {
List<String> titleList = new ArrayList<String>();
Column column = columnList.get(i);
String title = column.getTitle();
titleList.add(title);
dataList.add(titleList);
}
model.getSheet().setHead(dataList);
}
/**
* 写标题
*/
@Override
public <T extends BaseRowModel> void writeTitle(Class<T> t) {
List<List<String>> dataList = new ArrayList<List<String>>();
String[] columns = ExportUtil.getFieldNames(t);
//组装标题行
for (int i = 0; i < columns.length; i++) {
List<String> titleList = new ArrayList<String>();
String title = columns[i];
titleList.add(title);
dataList.add(titleList);
}
model.getSheet().setHead(dataList);
}
/**
* 内容追加
*
* @param dataList
* @param columnList
*/
@Override
public void appendContent(JSONArray dataList, List<Column> columnList) {
List<List<String>> dataArray = new ArrayList<List<String>>();
//组装数据行
for (Object obj : dataList) {
JSONObject json = (JSONObject) JSONObject.toJSON(obj);
List<String> rowList = new ArrayList<>();
for (int j = 0; j < columnList.size(); j++) {
Column columnMap = columnList.get(j);
String code = columnMap.getCode();
Object value = json.get(code);
String valueStr = value == null ? "" : value.toString();
rowList.add(valueStr);
}
dataArray.add(rowList);
}
model.getExcelWriter().write1(dataArray, model.getSheet());
}
/**
* 追加内容
*
* @param vos
* @param t
* @param <T>
*/
@Override
public <T extends BaseRowModel> void appendContent(List<T> vos, Class<T> t) {
//--start 修复sheet行数据为null时,列对齐错位--
List<List<Object>> dataArray = new ArrayList<List<Object>>();
for (T vo : vos) {
Field[] fields = vo.getClass().getDeclaredFields();
List<Object> rowList = new ArrayList<>();
for (Field field : fields) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
if (property != null) {
try {
field.setAccessible(true);
Object fieldValue = field.get(vo) == null ? "" : field.get(vo);
rowList.add(fieldValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException("写入内容到xlsx失败!");
}
}
}
dataArray.add(rowList);
}
model.getExcelWriter().write(dataArray, model.getSheet());
//--end 修复api行数据为null时,列对齐错位--
model.getSheet().setClazz(t); //注解来生成表头
//model.getExcelWriter().write(vos, model.getSheet());
model.getSheet().setAutoWidth(true);
}
/**
* 关闭流
*/
@Override
public void close() {
ExcelWriter writer = model.getExcelWriter();
try {
if (writer != null) {
writer.finish();
}
os.close();
} catch (IOException e) {
log.error("os close error", e);
}
}
@Override
public String getFilePath() {
return filePath;
}
@Override
public void setFilePath(String filePath) {
this.filePath = filePath;
}
}
CsvReader文件读取
import com.alibaba.excel.util.FileUtils;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
/**
* CSV读取工具类
*/
public class CsvReader {
/**
* 列分隔符
*/
private static final String WORDS_SPLIT = ",";
/**
* 读取200万条数据
*/
private static final int READ_LINES = Integer.MAX_VALUE;
/**
* csv、txt 文件处理
*
* @param filePath 文件路径
* @return
*/
public static Map<Integer, Map<Integer, String>> getTxtCsvFileContent(String filePath) {
Map<Integer, Map<Integer, String>> map = new HashMap<>();
try {
Map<Integer, String> rowContent = getTxtCsvLineContentMap(
FileUtils.openInputStream(new File(filePath)), filePath, READ_LINES);
if (rowContent != null) {
rowContent.forEach((k, v) -> {
Map<Integer, String> tmp = new LinkedHashMap<>();
String[] row = v.split(WORDS_SPLIT);
for (int i = 0; i < row.length; i++) {
tmp.put(i, row[i]);
}
map.put(k, tmp);
});
}
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
/**
* 兼容Excel多sheet格式
*
* @param filePath
* @return
*/
public static List<Map<Integer, Map<Integer, String>>> getExcelStyleData(String filePath) {
List sheet = new ArrayList<>();
Map<Integer, Map<Integer, String>> sheetData = getTxtCsvFileContent(filePath);
sheet.add(0, sheetData);
return sheet;
}
/**
* csv、txt 文件第一行表头
*
* @param filePath 文件路径
* @param charset
* @return
*/
public static Map<Integer, String> getTxtCsvFileHeader(String filePath, String charset) {
Map<Integer, String> headerMap = new LinkedHashMap<>();
try {
if (StringUtils.isBlank(charset)) {
charset = getFileCharset(filePath);
if (StringUtils.isBlank(charset)) {
charset = StandardCharsets.UTF_8.name();
}
}
InputStream is = new BufferedInputStream(FileUtils.openInputStream(new File(filePath)));
// 解析平面文件
BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
// 获取第一行的数据当做字段
String headContent = reader.readLine();
if (StringUtils.isNotBlank(headContent)) {
if (isMessyCode(headContent, Charset.forName(charset)) && !charset.equals(StandardCharsets.UTF_8.name())) {
System.out.println("文件编码:" + charset + ",内容存在乱码,自动修正编码为utf8");
is.close();
reader.close();
return getTxtCsvFileHeader(filePath, StandardCharsets.UTF_8.name());
}
String[] row = headContent.split(WORDS_SPLIT);
for (int i = 0; i < row.length; i++) {
headerMap.put(i, row[i]);
}
}
is.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return headerMap;
}
/**
* 获取 txt/csv 文件前 size 行数据
*
* @param inputStream
* @param filePath
* @param size
* @return
* @throws IOException
*/
public static Map<Integer, String> getTxtCsvLineContentMap(InputStream inputStream,
String filePath, int size) throws IOException {
Map<Integer, String> map = new LinkedHashMap<>();
String charset = getFileCharset(filePath);
if (StringUtils.isBlank(charset)) {
charset = StandardCharsets.UTF_8.name();
}
InputStream is = new BufferedInputStream(inputStream);
// 解析平面文件
BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
// 获取第一行的数据当做字段
String line = reader.readLine();
if (StringUtils.isBlank(line)) {
is.close();
reader.close();
return null;
}
int fileLineNum = getFileLineNum(filePath);
if (fileLineNum < size) {
size = fileLineNum;
}
for (int i = 1; i <= size; i++) {
String nowLineContent = reader.readLine();
nowLineContent = StringUtils.remove(nowLineContent, "\"");
nowLineContent = StringUtils.remove(nowLineContent, "\t");
Integer lineIndex = i + 1;
if (StringUtils.isNotBlank(nowLineContent)) {
if (lineIndex % 200 == 0) {
System.out.println("读取csv第" + lineIndex + "/" + size + "内容:" + nowLineContent);
}
map.put(lineIndex, nowLineContent);
}
}
is.close();
reader.close();
return map;
}
/**
* 高效获取大文件的行数:3千万行只要1秒钟
*
* @param filePath
* @return int
*/
public static int getFileLineNum(String filePath) {
int fileLines = 0;
try (LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(filePath))) {
lineNumberReader.skip(Long.MAX_VALUE);
int lineNumber = lineNumberReader.getLineNumber();
//实际上是读取换行符数量 , 所以需要+1
fileLines = lineNumber + 1;
} catch (IOException e) {
e.printStackTrace();
}
return fileLines;
}
/**
* 根据关键字获取表头
*
* @param filePath
* @param headerKey
* @return
*/
public static Map<Integer, String> getTxtCsvFileHeaderContainsKeywords(String filePath,
String headerKey) {
Map<Integer, String> headerMap = new LinkedHashMap<>();
try {
InputStream inputStream = new FileInputStream(filePath);
Map<Integer, String> tmp = CsvReader.getTxtCsvLineContentMap(inputStream, filePath, 50);
for (Map.Entry<Integer, String> entry : tmp.entrySet()) {
String[] row = entry.getValue().split(CsvReader.WORDS_SPLIT);
if (row.length > 0) {
row[0] = StringUtils.remove(row[0], "\"");
row[0] = StringUtils.remove(row[0], "\t");
String title = StringUtils.deleteWhitespace(row[0]);
for (int i = 0; i < row.length; i++) {
if (title.equals(headerKey)) {
headerMap.put(i, row[i]);
}
}
if (title.equals(headerKey)) {
break;
}
}
}
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return headerMap;
}
/**
* 获取 txt/csv/dat 文件编码
*
* @param filePath 文件路径
* @return
*/
private static String getFileCharset(String filePath) {
String charset = "UTF-8";
byte[] bytes = new byte[3];
try (InputStream inputStream = new FileInputStream(filePath)) {
inputStream.read(bytes);
if (bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF) {
charset = "UTF-8";
} else if (bytes[0] == (byte) 0xFE && bytes[1] == (byte) 0xFF) {
charset = "UTF-16BE";
} else if (bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE) {
charset = "UTF-16LE";
} else {
//或GB2312,即ANSI
charset = "GBK";
}
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return charset;
}
/**
* 判断在指定编码下是否包含乱码 输出 false,表示不包含乱码字符
*
* @param str
* @param charset
* @return
*/
public static boolean isMessyCode(String str, Charset charset) {
String decodedStr = new String(str.getBytes(charset), charset);
return !decodedStr.equals(str);
}
}
CsvExport实现类
package net.demo.excel.common.export.csv;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.csvreader.CsvWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.demo.excel.common.bean.Column;
import net.demo.excel.common.export.ExportWriter;
import net.demo.excel.common.export.csv.model.CsvModel;
import net.demo.excel.common.export.model.ExportModel;
import net.demo.excel.common.util.ExportUtil;
/**
* csv具体实现
*/
@Slf4j
@Data
public class CsvExport implements ExportWriter {
private ExportModel model;
private OutputStream os;
private String filePath;
/**
* 初始化
*
* @param os
*/
public CsvExport(OutputStream os) {
CsvWriter csvWriter = new CsvWriter(os, ',', Charset.forName("GBK"));
this.model = new CsvModel(csvWriter);
this.os = os;
}
/**
* 写标题
*
* @param columnList
*/
@Override
public void writeTitle(List<Column> columnList) {
String[] headers = new String[columnList.size()];
for (int i = 0; i < columnList.size(); i++) {
Column tmp = columnList.get(i);
headers[i] = tmp.getTitle();
}
try {
model.getCsvWriter().writeRecord(headers);
} catch (IOException e) {
log.error("写入标题到csv失败!", e);
throw new RuntimeException("写入标题到csv失败!");
}
}
@Override
public <T extends BaseRowModel> void writeTitle(Class<T> t){
try {
model.getCsvWriter().writeRecord(ExportUtil.getFieldNames(t));
} catch (IOException e) {
log.error("写入标题到csv失败!", e);
throw new RuntimeException("写入标题到csv失败!");
}
}
/**
* 内容追加
*
* @param dataList
* @param columnList
*/
@Override
public void appendContent(JSONArray dataList, List<Column> columnList) {
String[] content = null;
List<String> codeList = new ArrayList<String>();
for (int i = 0; i < columnList.size(); i++) {
Column tmp = columnList.get(i);
String code = tmp.getCode();
codeList.add(code);
}
try {
for (Object obj : dataList) {
JSONObject json = (JSONObject) JSONObject.toJSON(obj);
content = new String[codeList.size()];
for (int i = 0; i < codeList.size(); i++) {
String code = codeList.get(i);
Object value = json.get(code);
content[i] = value == null ? "" : value.toString();
}
model.getCsvWriter().writeRecord(content);
}
} catch (Exception e) {
log.error("写入内容到csv失败!", e);
throw new RuntimeException("写入内容到csv失败!");
}
}
@Override
public <T extends BaseRowModel> void appendContent(List<T> vos, Class<T> t) {
try {
for (T vo : vos) {
String[] fields = ExportUtil.getFields(vo);
model.getCsvWriter().writeRecord(fields);
}
} catch (IOException e) {
log.error("写入内容到csv失败!", e);
throw new RuntimeException("写入内容到csv失败!");
}
}
/**
* 关闭
*/
@Override
public void close() {
CsvWriter csvWriter = model.getCsvWriter();
if (csvWriter != null) {
csvWriter.close();
}
try {
os.close();
} catch (IOException e) {
log.error("os close error", e);
}
}
@Override
public String getFilePath() {
return filePath;
}
@Override
public void setFilePath(String filePath) {
this.filePath = filePath;
}
}
创建导入数据模板类
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
/**
* 数据导入的Excel模板实体
*/
@Data
public class ImportExcelVo implements Serializable {
private static final long serialVersionUID = 1L;
//使用index读取解决表头文字频繁变动问题
@ExcelProperty(index = 0)
private String name;
@ColumnWidth(20)
@ExcelProperty(value = "公司联系电话")
private String phone;
@ColumnWidth(28)
@ExcelProperty(value = "公司统一社会信用代码")
private String creditCode;
@ColumnWidth(15)
@ExcelProperty(value = "区域", index = 3)
private String province;
@ColumnWidth(15)
@ExcelProperty(value = "公司法人")
private String legalPerson;
@ExcelProperty(value = "备注")
private String remark;
@DateTimeFormat("yyyy-MM-dd") //指定日期格式
private Date date;
}
invoke时间格式,需要使用easyExcel中的@ExcelProperty
注解,并指定日期格式
public void invoke(Object data, AnalysisContext context) {
// 读取日期属性并进行处理
if (rowData.get(columnIndex) instanceof Date) {
Date date = (Date) rowData.get(columnIndex);
// 在这里对日期进行处理
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = sdf.format(date);
//其他属性的处理...
System.out.println(data);
}
}
创建数据导出模板
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 资质信息导出实体
*/
@Data // Lombok注解,用于生成getter setter
@Accessors(chain = true) //Lombok注解,链式赋值使用
public class ExportExcelVo extends BaseRowModel {
private static final long serialVersionUID = 1L;
@ColumnWidth(25)
@ExcelProperty(value = "企业名称")
private String name;
@ColumnWidth(25)
@ExcelProperty(value = "社会统一信用代码")
private String creditCode;
@ColumnWidth(15)
@ExcelProperty(value = "曾用名", converter = NullConverter.class)
private String formerName;
@ColumnWidth(15)
@ExcelProperty(value = "公司法人")
private String legalPerson;
@ExcelProperty(value = "区域")
private String province;
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty(value = "录入时间")
private String createTime;
@ColumnWidth(15)
@ExcelProperty(value = "公司股东")
private String stockholder;
@ExcelProperty(value = "企业联系方式")
private String contact;
@NumberFormat("#.##%") // 使用百分比格式格式化这个数字
@ExcelProperty("数字")
private Double doubleData;
}
ExcelProperty注解
可以定义表头的名称。这个注解还提供了index、order两个属性,可以定义列的位置和顺序。
- 1)关于index
如果所有字段都不加index的话,默认index会从0开始,最早的声明的字段的名字的index的值就是0。之后字段的index就从0开始递增,依次类推。index是几决定了该字段数据会赋值给Excel中的第几列。如果不想按这个顺序把数据写到Excel当中。那么就可以手动设置index的值,把字段写到想要的列中去。如果index相同,直接会抛出异常,因为程序无法判断这个列放那个字段。
- 2)关于order
index和order虽然都决定顺序,但是两者语义不同:如果order和index同时使用,index优先占据位置,order再进行排序。index=-1的话,使用jJava进行默认排序。order的默认值为Integer.MAX_VALUE,其中order的值越小,列越靠前。
其他注解
- @DateTimeFormat 日期转换,用String去接收excel日期格式的数据会调用这个注解。@DateTimeFormat(value = "yyyy-MM-dd HH:mm:ss")指定Excel中的日期类型
- @NumberFormat 数字转换,用
String
去接收excel
数字格式的数据会调用这个注解。 - @ColumnWith 设置列宽度。只有一个参数value,value的单位是字符长度,最大可以设置255个字符。
@ExcelIgnore
EasyExcel组件标注在成员变量上,默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
使用方法
/**
* Excel批量导入数据
*
* @param file 导入文件
*/
@RequestMapping(value = "/import", method = RequestMethod.POST)
public CommonResponse<String> importEvents(MultipartFile file) {
try {
List<?> list = ExportUtil.importExcel(file, ImportExcelVo.class);
System.out.println(list);
return CommonResponse.success("数据导入完成");
} catch (Exception e) {
return CommonResponse.error("数据导入失败!" + e.getMessage());
}
}
//生成excel文件
try {
exportWriter = WriterFactory.getExportWriter(
dto.getExportType() == null ? "csv" : dto.getExportType(), fileName);
exportWriter.writeTitle(PhoneExportVO.class);
exportWriter.appendContent(list, PhoneExportVO.class);
} catch (Exception e) {
log.error("导出失败", e);
throw new BaseException("导出失败");
} finally {
if (exportWriter != null) {
exportWriter.close();
}
}
ExportWriter exportWriter = WriterFactory.getExportWriter(exportType, fileName);
List<Column> columns = trunkAreaVOResp.getColumns();
exportWriter.writeTitle(columns);
JSONArray dataList = (JSONArray) JSON.toJSON(trunkAreaVOResp.getTrunkArea());
exportWriter.appendContent(dataList, columns);
exportWriter.close();
Excel中日期格式
在Excel中,日期是以所谓的“序列号”存储的,这种序列号实际上是一个浮点数(因此有时被称为“double格式”),它代表了自1900年1月1日以来的天数。整数部分表示天数,而小数部分表示一天内的小时、分钟和秒数。例如,1900年1月1日在Excel中被存储为数字1.0,因为这是序列的起始点。那么,2023年1月1日将被存储为一个较大的数字,因为这一天距离1900年1月1日更远。具体来说,Excel日期序列号的计算方式如下:
- 整数部分代表天数,例如,2023年1月1日是44622(取决于所用的日期系统,1900或1904系统)。
- 小数部分代表一天中的时间。例如,0.5表示中午12:00,因为一天有24小时,所以0.5即为24小时的一半。
在大多数编程环境中,程序通常读取单元格的数值内容,而不是其显示的日期格式,因此了解日期规则就能够还原出日期内容
EasyExcel自定义转换器Converter
Timestamp 转换器
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
public class TimestampConverter implements Converter<Timestamp> {
@Override
public Class<Timestamp> supportJavaTypeKey() {
return Timestamp.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 将excel对象转换为Java对象
*/
@Override
public Timestamp convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
return null;
}
/**
* 将 Java 对象转换为 excel 对象
*/
@Override
public CellData convertToExcelData(Timestamp timestamp, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp));
}
}
NullConverter
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
public class NullConverter implements Converter<String> {
/**
* 回到 Java 中的对象类型
*
* @return 支持 Java 类
*/
@Override
public Class supportJavaTypeKey() {
return String.class;
}
/**
* * 返回 excel 中的对象枚举
* * @return 支持 {@link Cell DataTypeEnum}
* */
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 将excel对象转换为Java对象
*
* @param cellData
* Excel 单元格数据。NotNull。
* @param contentProperty
* 内容属性。可空。
* @param globalConfiguration
* 全局配置。NotNull。
* @return 要放入 Java 对象的数据
* @抛出异常
* 例外。
*/
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return "-".equals(cellData.getStringValue()) ? null : cellData.getStringValue();
}
/**
* 将 Java 对象转换为 excel 对象
*
* @参数值
* Java 数据.NotNull。
* @param contentProperty
* 内容属性。可空。
* @param globalConfiguration
* 全局配置。NotNull。
* @return 数据放入 Excel
* @抛出异常
* 例外。
*/
@Override
public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData<>(null == value ? "-" : value);
}
}
ExcelToCsvConverter
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
/**
* Excel转换CSV的工具类
*
* @author hudy
*/
public class ExcelToCsvConverter {
/**
* 将Excel文件转换为CSV文件
*
* @param sourceFilePath Excel文件路径
* @param targetFilePath CSV文件输出路径
*/
public static void convertExcelToCsv(String sourceFilePath, String targetFilePath) {
// 使用try-with-resources语句确保输入输出流的关闭
try (InputStream inputStream = new FileInputStream(sourceFilePath);
OutputStream outputStream = new FileOutputStream(targetFilePath)) {
// 使用EasyExcel框架读取Excel文件并转换为CSV格式
EasyExcel.read(inputStream, null, new ExcelToCsvListener(outputStream)).sheet().doRead();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Excel到CSV的监听器,用于处理每行数据并写入CSV
*/
private static class ExcelToCsvListener extends AnalysisEventListener<Map<Integer, String>> {
private final OutputStream outputStream;
public ExcelToCsvListener(OutputStream outputStream) {
this.outputStream = outputStream;
}
/**
* 处理每行Excel数据,将其写入CSV格式
*/
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
writeData(data, context);
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
try {
outputStream.write(getCsvBom());
writeData(headMap, context);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 将行数据转换为CSV格式并写入输出流
*/
private void writeData(Map<Integer, String> data, AnalysisContext context) {
try {
StringBuilder rowContent = new StringBuilder();
// 构建CSV行内容
for (int i = 0; i < data.size(); i++) {
if (i > 0) {
rowContent.append(",");
}
rowContent.append(data.get(i));
}
// 写入行内容到输出流
if (rowContent.length() > 0) {
outputStream.write((rowContent.toString() + "\n").getBytes());
}
if (context.readRowHolder().getRowIndex() % 100 == 0) {
System.out.println("解析到Excel行号:" + context.readRowHolder().getRowIndex());
}
} catch (IOException e) {
throw new RuntimeException("写入CSV时发生错误", e);
}
}
/**
* 防止导出Csv乱码
*
* @return
*/
private byte[] getCsvBom() {
return new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
}
/**
* 当所有Excel数据被解析完成时调用,用于关闭输出流
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
try {
System.out.println("所有数据解析完成!");
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 示例主方法,用于演示类的使用
public static void main(String[] args) {
String sourceFilePath = "zjx.xlsx";
String targetFilePath = "zjx.csv";
convertExcelToCsv(sourceFilePath, targetFilePath);
}
}
StatusConverter
import com.test.project.StatusEnum;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
/**
* 状态枚举转换器
*/
public class StatusConverter implements Converter<Integer> {
@Override
public Class<Integer> supportJavaTypeKey() {
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 将excel对象转换为Java对象
*/
@Override
public Integer convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return StatusEnum.getKey(cellData.getStringValue());
}
/**
* 将 Java 对象转换为 excel 对象
*/
@Override
public CellData convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(StatusEnum.getValue(integer));
}
}
使用方法一
每个字段都要添加@ExcelProperty(converter = NullConverter.class)代码,如果遇到大量的数据字段去填充处理会增加很多工作量。转换器仅支持需要被处理的数据字段,也就是适用于从数据库查询出来已有的数据,如日期格式或性别字段做转换时才生效
使用方法二
File uploadFile = File.createTempFile("export", ".xlsx");
String templateFilePath = systemUrl + "/template/exportPublishShop.xlsx";
ExcelWriterSheetBuilder excelWriterSheetBuilder =
EasyExcel.write(uploadFile).registerConverter(new TimestampConverter()).withTemplate(templateFilePath).sheet();
List<Map<String, String>> productList = 查询数据数据
// productList 如果数据量很大一定要做分页查询,避免占用内存过大
excelWriterSheetBuilder.doFill(productList);
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)