Excel读取和写入工具
前言
Github:https://github.com/HealerJean
一、工具类
1、ExcelEnums
枚举
package com.healerjean.proj.utils.file;
import com.healerjean.proj.dto.csv.CsvDemoExcel;
/**
* ExcelEnums
*
* @author HealerJean
* @date 2023-06-21 02:06:04
*/
public class ExcelEnums {
/**
* Excel写入枚举
*/
public enum Excel {
/**
* Excel写入枚举
*/
CSV_DEMO("CsvDemoExcel", CsvDemoExcel.class) {
@Override
public String[] header() {
// 顺序不能轻易变动
return new String[]{"名称"};
}
@Override
public String[] column() {
return new String[]{"name"};
}
},
;
/**
* ExcelWriter
*
* @param info info
* @param dataClass dataClass
*/
Excel(String info, Class dataClass) {
this.info = info;
this.dataClass = dataClass;
}
/**
* getInfo
*/
public String getInfo() {
return info;
}
/**
* getDataClass
*/
public Class getDataClass() {
return dataClass;
}
/**
* 说明
*/
private final String info;
/**
* 导出数据对应Bean
*/
private final Class dataClass;
/**
* 导出表头名
* 对应写入的展示名
*
* @return 表头名
*/
public abstract String[] header();
/**
* 导出列
* 对应写入bean字段名称
*
* @return 列
*/
public abstract String[] column();
}
}
2、工具
1)CsvReaderUtils
package com.healerjean.proj.utils.file;
import com.opencsv.ICSVParser;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.List;
@Slf4j
public final class CsvReaderUtils {
/**
* readerCsvFromFile 跳过第一行
*
* @param file 文件
* @param excel {@link ExcelEnums.Excel}
* @param <E> E
* @return List<E>
*/
public static <E> List<E> readerCsvFromFile(File file, ExcelEnums.Excel excel) {
try {
ColumnPositionMappingStrategy<E> mappingStrategy = new ColumnPositionMappingStrategy<>();
mappingStrategy.setType(excel.getDataClass());
mappingStrategy.setColumnMapping(excel.column());
CsvToBean<E> build = new CsvToBeanBuilder<E>(new FileReader(file))
.withSkipLines(1)
.withMappingStrategy(mappingStrategy)
.withSeparator(ICSVParser.DEFAULT_SEPARATOR)
.build();
return build.parse();
} catch (Exception e) {
log.error("[ExcelReaCsvReaderUtilsderUtils#readerCsvFromFile] error", e);
return Collections.emptyList();
}
}
/**
* readerCsvFromInputStream 跳过第一行
*
* @param inputStream 文件
* @param excel {@link ExcelEnums.Excel}
* @param <E> E
* @return List<E>
*/
public static <E> List<E> readerCsvFromInputStream(InputStream inputStream, ExcelEnums.Excel excel) {
try {
ColumnPositionMappingStrategy<E> mappingStrategy = new ColumnPositionMappingStrategy<>();
mappingStrategy.setType(excel.getDataClass());
mappingStrategy.setColumnMapping(excel.column());
CsvToBean<E> build = new CsvToBeanBuilder<E>(new InputStreamReader(inputStream))
.withSkipLines(1)
.withMappingStrategy(mappingStrategy)
.withSeparator(ICSVParser.DEFAULT_SEPARATOR)
.build();
return build.parse();
}catch (Exception e){
log.error("[ExcelReaCsvReaderUtilsderUtils#readerCsvFromInputStream] error", e);
return Collections.emptyList();
}
}
/**
* readerExcelFromUrl 跳过第一行
*
* @param url 文件HTTP下载链接
* @param excel {@link ExcelEnums.Excel}
* @param <E> E
* @return List<E>
*/
public static <E> List<E> readerExcelFromUrl(String url, ExcelEnums.Excel excel) {
try {
URL httpUrl = new URL(url);
URLConnection urlConnection = httpUrl.openConnection();
return readerCsvFromInputStream(urlConnection.getInputStream(), excel);
} catch (Exception e) {
log.error("[CsvReaderUtils#readerExcelFromUrl] error, url:{}", url, e);
return Collections.emptyList();
}
}
}
2)CsvWriterUtils
package com.healerjean.proj.utils.file;
import com.opencsv.CSVWriter;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.opencsv.exceptions.CsvDataTypeMismatchException;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
import lombok.extern.slf4j.Slf4j;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public final class CsvWriterUtils {
/**
* 默认文件路径
*/
private static final String DEFAULT_LOCAL_PATH = "/Users/healerjean/Desktop/logs/";
/**
* 写入CSV
*
* @param dataList 数据列表
* @param fileName 文件名称
* @param excel 写入文件类型
* @return 生成后的路径
*/
public static <E> String writerCsv(List<E> dataList, String fileName, ExcelEnums.Excel excel) {
if (null == dataList) {
dataList = new ArrayList<>();
}
// 服务器绝对路径
String date = DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now());
String baseLocalPath = DEFAULT_LOCAL_PATH + excel.name().toLowerCase() + "/" + date + "/";
// 创建目录
FileUtils.createDir(baseLocalPath, Boolean.FALSE);
log.info("ExcelUtils writeCsv begin,size:{}", dataList.size());
CSVWriter csvWriter;
// 写入文件的绝对路径
String writeFilePath = baseLocalPath + System.currentTimeMillis() + "_" + fileName;
try (Writer writer = new FileWriter(writeFilePath)) {
// 手动增加BOM标识
writer.write(new String(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}));
// 映射策略
ColumnPositionMappingStrategy<E> strategy = new ColumnPositionMappingStrategy<>();
strategy.setType(excel.getDataClass());
strategy.setColumnMapping(excel.column());
csvWriter = new CSVWriter(writer,
CSVWriter.DEFAULT_SEPARATOR,
CSVWriter.NO_QUOTE_CHARACTER,
CSVWriter.DEFAULT_ESCAPE_CHARACTER,
CSVWriter.DEFAULT_LINE_END);
// 写表头
csvWriter.writeNext(excel.header());
StatefulBeanToCsv<E> beanToCsv = new StatefulBeanToCsvBuilder<E>(writer).
withMappingStrategy(strategy).
withQuotechar(CSVWriter.NO_QUOTE_CHARACTER).
withSeparator(CSVWriter.DEFAULT_SEPARATOR).
withEscapechar('\\').build();
beanToCsv.write(dataList);
csvWriter.close();
log.info("FileUtils writeCsv end,size:{}", dataList.size());
return writeFilePath;
} catch (IOException e) {
log.error("FileUtils writeCsv IOException", e);
} catch (CsvRequiredFieldEmptyException e) {
log.error("FileUtils writeCsv CsvRequiredFieldEmptyException", e);
} catch (CsvDataTypeMismatchException e) {
log.error("FileUtils writeCsv CsvDataTypeMismatchException", e);
}
return null;
}
}
3)ExcelReaderUtils
package com.healerjean.proj.utils.file;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* ExcelReaderUtils
*
* @author zhangyujin
* @date 2023/6/21$ 15:37$
*/
@Slf4j
public final class ExcelReaderUtils {
/**
* readerCsvFromFile 跳过第一行
*
* @param file 文件
* @param excel {@link ExcelEnums.Excel}
* @param <E> E
* @return List<E>
*/
public static <E> List<E> readerExcelFromFile(File file, ExcelEnums.Excel excel) {
try (FileInputStream fileInputStream = new FileInputStream(file)) {
return readerExcelFromInputStream(fileInputStream, excel);
} catch (Exception e) {
log.error("[ExcelReaderUtils#readerExcelFromFile] error", e);
return Collections.emptyList();
}
}
/**
* 处理Excel数据
*
* @param inputStream InputStream
* @param excel ExcelEnums.Excel
* @param <E> E
* @return List<E>
*/
public static <E> List<E> readerExcelFromInputStream(InputStream inputStream, ExcelEnums.Excel excel) {
List<E> list = new ArrayList<>();
EasyExcel.read(inputStream, excel.getDataClass(), new ReadListener<E>() {
@Override
public void invoke(E data, AnalysisContext context) {
list.add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
}).sheet().doRead();
return list;
}
/**
* 处理Excel数据
*
* @param url 文件HTTP下载链接
* @param excel ExcelEnums.Excel
* @param <E> E
* @return List<E>
*/
public static <E> List<E> readerExcelFromUrl(String url, ExcelEnums.Excel excel) {
try {
URL httpUrl = new URL(url);
URLConnection urlConnection = httpUrl.openConnection();
return readerExcelFromInputStream(urlConnection.getInputStream(), excel);
} catch (Exception e) {
log.error("[ExcelReaderUtils#readerExcelFromUrl] error, url:{}", url, e);
return Collections.emptyList();
}
}
}
4)ExcelWriterrUtils
package com.healerjean.proj.utils.file;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.data.DataFormatData;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Sheet;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* ExcelWriterrUtils
*
* @author zhangyujin
* @date 2023/6/21$ 15:38$
*/
@Slf4j
public final class ExcelWriterrUtils {
/**
* 默认文件路径
*/
private static final String DEFAULT_LOCAL_PATH = "/Users/healerjean/Desktop/logs/";
/**
* 写入Excel
*
* @param dataList 数据列表
* @param fileName 文件名称
* @param excel 写入文件类型
* @return 生成后的路径
*/
public static <E> String writerExcel(List<E> dataList, String fileName, ExcelEnums.Excel excel) {
ExcelWriter excelWriter = null;
try {
if (null == dataList) {
dataList = new ArrayList<>();
}
String date = DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now());
String baseLocalPath = DEFAULT_LOCAL_PATH + excel.name().toLowerCase() + "/" + date + "/";
// 创建目录
FileUtils.createDir(baseLocalPath, Boolean.FALSE);
String writeFilePath = baseLocalPath + System.currentTimeMillis() + "_" + fileName;
excelWriter = EasyExcel.write(writeFilePath, excel.getDataClass()).build();
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 设置表头居中对齐
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
// 颜色
headWriteCellStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
headWriteCellStyle.setWrapped(true);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
// 设置内容靠中对齐
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
// 设置文本类型
DataFormatData dataFormatData = new DataFormatData();
dataFormatData.setIndex((short) 49);
contentWriteCellStyle.setDataFormatData(dataFormatData);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
WriteSheet writeSheet = EasyExcel.writerSheet().registerWriteHandler(horizontalCellStyleStrategy).
registerWriteHandler(new AbstractColumnWidthStyleStrategy() {
@Override
protected void setColumnWidth(CellWriteHandlerContext context) {
Sheet sheet = context.getWriteSheetHolder().getSheet();
Cell cell = context.getCell();
sheet.setColumnWidth(cell.getColumnIndex(), 5000);
}
}).
includeColumnFiledNames(Arrays.asList(excel.column())).build();
excelWriter.write(dataList, writeSheet);
return writeFilePath;
} catch (Exception e) {
log.error("ExcelUtils writerExcel exception", e);
} finally {
//关闭流
if (excelWriter != null) {
excelWriter.finish();
}
}
return null;
}
}
3、验证
1)DemoExcel
package com.healerjean.proj.dto.csv;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* CsvDemoExcel
* @author zhangyujin
* @date 2022/3/28 17:55.
*/
@Data
public class DemoExcel {
/**
* name
*/
@ExcelProperty(value = "名称")
private String name;
}
2)MainTest
package com.healerjean.proj.utils.file;
import com.healerjean.proj.dto.csv.DemoExcel;
import com.healerjean.proj.utils.json.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhangyujin
* @date 2022/3/28 17:53.
* @description
*/
@Slf4j
public class MainTest {
@Test
public void testCsvWrite() {
List<DemoExcel> list = new ArrayList<>();
for (int i = 0; i < 500000; i++) {
DemoExcel demo = new DemoExcel();
demo.setName("11111111111" + i);
list.add(demo);
}
String filePath = CsvWriterUtils.writerCsv(list, "test.csv", ExcelEnums.Excel.DEMO_EXCEL);
log.info("[CsvWriterUtils#writerCsv] filePath:{}", filePath);
}
@Test
public void testCsvRead(){
File file = new File("/Users/healerjean/Desktop/logs/demo_excel/test.csv");
List<DemoExcel> list = CsvReaderUtils.readerCsvFromFile(file, ExcelEnums.Excel.DEMO_EXCEL);
log.info("[CsvWriterUtils#readerCsvFromFile] size:{}, lineOne:{}", list.size(),
JsonUtils.toJsonString(list.get(0)));
}
@Test
public void testExcelWrite() {
List<DemoExcel> list = new ArrayList<>();
for (int i = 0; i < 500000; i++) {
DemoExcel demo = new DemoExcel();
demo.setName("11111111111" + i);
list.add(demo);
}
String filePath = ExcelWriterrUtils.writerExcel(list, "test.xlsx", ExcelEnums.Excel.DEMO_EXCEL);
log.info("[ExcelWriterrUtils#writerExcel] filePath:{}", filePath);
}
@Test
public void testExcelRead() {
File file = new File("/Users/healerjean/Desktop/logs/demo_excel/test.xlsx");
List<DemoExcel> list = ExcelReaderUtils.readerExcelFromFile(file, ExcelEnums.Excel.DEMO_EXCEL);
log.info("[CsvTest#readerExcelFromFile] size:{}, lineOne:{}", list.size(),
JsonUtils.toJsonString(list.get(0)));
}
}
2、说明
2.1、@CsvBindByPosition
@CsvBindByPosition
是根据文件中字段的位置来映射
@CsvBindByPosition(position = 1)
private String name;
2.1.1、@CsvCustomBindByPosition
@CsvCustomBindByPosition(position = 2, converter = StringToClaimMetaDate.class)
private Date dataTime;
2.1.1.1、converter
public class StringToClaimMetaDate extends AbstractBeanField<Date> {
@SneakyThrows
@Override
protected Object convert(String value) {
if (StringUtils.isBlank(value)) {
return null;
}
return DateUtils.parseDate(value, "yyyyMMdd");
}
}
2.2、@CsvBindByName
@ CsvBindByName
根据表头的名称来映射;@CsvBindByPosition
是根据文件中字段的位置来映射。当 2 个注解一起使用时则基于位置的方式有效。
@CsvBindByPosition(column = "name")
private String name;