1 简介
基于hutool,结合自定义注解实现csv文件的生成。
2 引入hutool依赖包
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.12</version>
</dependency>
3 定义注解@Csv
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface Csv {
/**
* csv文件字段名称(表头名称)
*/
String name();
/**
* 日期格式化(目前仅支持Date)
*/
String format() default "yyyy-MM-dd HH:mm:ss";
/**
* 字段排序
*/
int orderNum() default 0;
}
4 实现CsvUtils
public final class CsvUtils {
private static final ConcurrentHashMap<Class<?>, List<CsvFieldEntity>> CSV_FIELD_CACHE = new ConcurrentHashMap<>();
/**
* 生成csv文件
* @param dataSet 需要导出的数据
* @param pojoClass 数据对象
* @param filePath 导出文件的目录
*/
public static void export(List<?> dataSet, Class<?> pojoClass, String filePath) {
List<CsvFieldEntity> exportFields = getExportFields(pojoClass);
if (CollectionUtil.isEmpty(exportFields)) {
throw new RuntimeException(String.format("There is no export field in '%s'", pojoClass));
}
try (CsvWriter writer = CsvUtil.getWriter(filePath, CharsetUtil.CHARSET_UTF_8)) {
writer.setAlwaysDelimitText(true);
// header
String[] headers = exportFields.stream().map(f -> f.getCsvAnn().name()).toArray(String[]::new);
writer.writeLine(headers);
// data
String[] line = new String[exportFields.size()];
for (Object entity : dataSet) {
addLine(entity, exportFields, line);
writer.writeLine(line);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void addLine(Object entity, List<CsvFieldEntity> fields, String[] line) {
for (int i = 0,length = fields.size(); i < length; i++) {
try {
CsvFieldEntity fieldEntity = fields.get(i);
Object invoke = fieldEntity.getGetter().invoke(entity);
if (invoke instanceof Date) {
line[i] = new SimpleDateFormat(fieldEntity.getCsvAnn().format()).format(invoke);
} else {
line[i] = invoke != null ? invoke.toString() : null;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private static List<CsvFieldEntity> getExportFields(Class<?> pojoClass) {
List<CsvFieldEntity> csvFieldEntities = CSV_FIELD_CACHE.get(pojoClass);
if (csvFieldEntities != null) {
return csvFieldEntities;
}
Field[] fields = ReflectUtil.getFields(pojoClass);
List<CsvFieldEntity> collect = Arrays.stream(fields).map(field -> {
Csv annotation = field.getAnnotation(Csv.class);
if (annotation == null) {
return null;
}
String name = field.getName();
CsvFieldEntity entity = new CsvFieldEntity();
entity.setFieldName(name);
entity.setCsvAnn(annotation);
entity.setGetter(getGetter(pojoClass, name));
return entity;
}).filter(Objects::nonNull)
.sorted(Comparator.comparingInt(f -> f.getCsvAnn().orderNum()))
.collect(Collectors.toList());
CSV_FIELD_CACHE.put(pojoClass, collect);
return collect;
}
private static Method getGetter(Class<?> pojoClass, String name) {
try {
char[] chars = name.toCharArray();
if (chars[0] >= 'a' && chars[0] <= 'z') {
chars[0] -= 32;
}
return pojoClass.getMethod("get" + new String(chars));
} catch (NoSuchMethodException e) {
throw new RuntimeException(String.format("There is no getter for property named '%s' in '%s'", name, pojoClass), e);
}
}
private static final class CsvFieldEntity {
private String fieldName; // 字段名
private Csv csvAnn;
private Method getter;
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public Csv getCsvAnn() {
return csvAnn;
}
public void setCsvAnn(Csv csvAnn) {
this.csvAnn = csvAnn;
}
public Method getGetter() {
return getter;
}
public void setGetter(Method getter) {
this.getter = getter;
}
}
}
5 使用
step1:定义数据model
public class MsgClient implements java.io.Serializable {
/** id */
private String id;
// 电话号码(主键)
@Csv(name = "电话号码")
private String clientPhone = null;
// 客户姓名
@Csv(name = "姓名", orderNum = -1)
private String clientName = null;
// 所属分组
// 备注
@Csv(name = "备注", orderNum = 999)
private String remark = null;
// 生日
@Csv(name = "出生日期")
private Date birthday = null;
// 创建人
private String createBy = null;
public Date getBirthday() {
return this.birthday;
}
public String getClientName() {
return this.clientName;
}
public String getClientPhone() {
return this.clientPhone;
}
public String getCreateBy() {
return createBy;
}
public String getId() {
return this.id;
}
public String getRemark() {
return this.remark;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public void setClientPhone(String clientPhone) {
this.clientPhone = clientPhone;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public void setId(String id) {
this.id = id;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
step2:创建模拟数据并调用
public static class CsvUtilsTest {
public static void main(String[] args) throws Exception {
// 准备测试数据
List<MsgClient> list = getData();
// 调用工具类
CsvUtils.export(list, MsgClient.class, "D:/data/test.csv");
}
private static List<MsgClient> getData() {
List<MsgClient> list = new ArrayList<MsgClient>();
for (int i = 0; i < 5; i++) {
MsgClient client = new MsgClient();
client.setBirthday(new Date());
client.setClientName("小明" + i);
String phone = "18,797" + i;
client.setClientPhone("1310001000" + i);
client.setCreateBy("JueYue");
client.setId("1" + i);
client.setRemark("测试" + i);
list.add(client);
}
return list;
}
}
得到文件数据如下
"姓名","电话号码","出生日期","备注"
"小明0","13100010000","2025-05-29 21:28:25","测试0"
"小明1","13100010001","2025-05-29 21:28:25","测试1"
"小明2","13100010002","2025-05-29 21:28:25","测试2"
"小明3","13100010003","2025-05-29 21:28:25","测试3"
"小明4","13100010004","2025-05-29 21:28:25","测试4"
691

被折叠的 条评论
为什么被折叠?



