上篇轻微的了解了反射,及将要用的工具类,今天咱继续站在巨人的肩膀上学习从浏览器下载文件相关工具类,开发最快的速度,创造更高的成就,就是直接传送门:https://blog.csdn.net/accountwcx/article/details/46775397;
今天要讲的是第一种实现csv格式数据导出(注解式),闲话少说直接刚;
private static final String FILE_FORMAT = ".csv";
private static final String COMMA_DELIMITER = ",";
private static final String NEW_LINE_SEPARATOR = "\n";
private static final String ENCODINT_GBK = "gbk";
private static final String BLANK_STRING = "";
// 处理下载数据时数字出现科学计数法问题
private static final String SPECIAL_SYMBOLS = "`";
/**
* 创建csv文件,基于注解实现
* @param fileName 文件名
* @param list 导出数据对象集合
* @return csv文件
*/
public File createCsvFile(String fileName, List<T> list) {
// 注解
List<Object[]> annotationList = Lists.newArrayList();
// 标题头
List<String> headerList = Lists.newArrayList();
// 域名
List<String> fieldList = Lists.newArrayList();
File f = null;
OutputStreamWriter fileWriter = null;
try {
f = File.createTempFile(fileName, FILE_FORMAT);
fileWriter = new OutputStreamWriter(new FileOutputStream(f), ENCODINT_GBK);
Class<? extends Object> cla = list.get(0).getClass();
Field[] fields = cla.getDeclaredFields();
for (Field field : fields) {
CsvField csvField = field.getAnnotation(CsvField.class);
if (Objects.nonNull(csvField)) {
annotationList.add(new Object[] { csvField, field });
// 获取域名
fieldList.add(field.getName());
}
}
// 获取表头
for (Object[] os : annotationList) {
String fieldTitle = ((CsvField) os[0]).title();
// 如果是非必填,加上注释
int required = ((CsvField) os[0]).required();
if (required == 0) {
fieldTitle = fieldTitle + "(非必填)";
}
headerList.add(fieldTitle);
}
fileWriter.append(listToString(fieldList));
fileWriter.append(NEW_LINE_SEPARATOR);
fileWriter.append(listToString(headerList));
fileWriter.append(NEW_LINE_SEPARATOR);
//
for (Object v : list) {
for (Object[] os : annotationList) {
CsvField ef = (CsvField) os[0];
Object val = null;
// 获取入参值
try {
if (StringUtils.isNotBlank(ef.value())) {
val = ReflectUtils.invokeGetter(v, ef.value());
} else if (os[1] instanceof Field) {
val = ReflectUtils.invokeGetter(v, ((Field) os[1]).getName());
}
} catch (Exception ex) {
logger.error("the reflect is error msg is {}", ex);
// 如果异常,忽略
val = BLANK_STRING;
}
if (ef.isSpecial()) {
fileWriter.append(SPECIAL_SYMBOLS);
fileWriter.append(Objects.isNull(val) ? BLANK_STRING : val.toString());
} else {
fileWriter.append(Objects.isNull(val) ? BLANK_STRING : val.toString());
}
fileWriter.append(COMMA_DELIMITER);
}
fileWriter.append(NEW_LINE_SEPARATOR);
}
return f;
} catch (Exception e) {
logger.error("export data is error,msg is {}", e);
throw new RuntimeException("export data is error,msg is " + e.getMessage());
} finally {
try {
fileWriter.flush();
fileWriter.close();
} catch (IOException e) {
logger.error("close stream is error,msg is {}", e);
}
}
}
protected String listToString(List<String> exportName) {
return Joiner.on(",").join(exportName);
}
csv注释自定义(可根据自身需求进行导出数据时的限制):
/**
* csv注解定义
*/
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CsvField {
/**
* 导出字段名(默认调用当前字段的“get”方法,如指定导出字段为对象,请填写“对象名.对象属性”,例:“area.name”、“office.
* name”)
*/
String value() default "";
/**
* 标题
*/
String title();
/**
* 必输项(0:非必输;1:必输)
*/
int required() default 1;
/**
* 是否加含特殊符号(主要针对导出数据出现科学计数法)
*/
boolean isSpecial() default false;
}
测试类:
class Test {
@CsvField(title = "测试a")
String a;
@CsvField(title = "测试b", isSpecial = true)
Integer b;
@CsvField(value = "test1.c", title = "测试类Test1")
Test1 test1;
public Test1 getTest1() {
return test1;
}
public void setTest1(Test1 test1) {
this.test1 = test1;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public Integer getB() {
return b;
}
public void setB(Integer b) {
this.b = b;
}
public Test(String a, Integer b) {
super();
this.a = a;
this.b = b;
}
public Test(String a, Integer b, Test1 test1) {
super();
this.a = a;
this.b = b;
this.test1 = test1;
}
@Override
public String toString() {
return "Test [a=" + a + ", b=" + b + "]";
}
}
class Test1 {
@CsvField(title = "测试c")
String c;
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public Test1(String c) {
super();
this.c = c;
}
@Override
public String toString() {
return "Test1 [c=" + c + "]";
}
}
测试使用:
public static void main(String[] args) {
List<Test> list = Lists.newArrayList(new Test("zhang", 1, new Test1("c1")),
new Test("wang", 2, new Test1("c1")));
ExportCsvUtils<Test> csvUtils = new ExportCsvUtils<Test>();
File file = csvUtils.createCsvFile("测试导出", list);
System.err.println(file);
}
测试结果(print的是file的文件路径,直接在我的电脑打开就行):
基于注解的方式实现数据导出不可置否的要用到反射,每次用到java反射时都需要慎重考虑是否会影响性能,在实际开发中有些常用数据会频繁被下载,即使加上缓存等一些提高性能手段,也会增加开发过程,效率比较低;同时该方式限制了所需导出数据源的自由(即如果Test1增加属性d被导出,Test也需要增加相应Dto属性d比较繁琐).因此,需要考虑其他可行性方式.