Java 将对象List转为csv文件并上传远程文件服务器实现方案

问题情景:

最近项目中遇到了根据第三方系统传递过来的参数,封装为List<实体类对象>后,将该实体类转换为csv文件,然后上传到远程的sftp服务器指定目录的需求。

实现思路:

  1. List<实体类对象>转为csv文件的过程。通过OpenCsv实现。

阻塞点:

1.最开始遇到了生成的csv文件的第一行生成的字段名,变成了实体类的大写字段名了,例如userName变为USERNAME并且排序混乱。

2.通过查阅资料找到了以下两个注解,后期却发现两个注解不能同时出现。详见文章
为什么 opencsv 在写入文件时将 csv 标头大写

复制代码
@CsvBindByName(column = "TradeID")
@CsvBindByPosition(position = 0)

解决方案:

方案一:

创建自定义MappingStrategy :

class CustomMappingStrategy extends ColumnPositionMappingStrategy {

private static final String\[\] HEADER = new String\[\]{"TradeID", "GWML GUID", "MXML GUID", "GWML File", "MxML File", "MxML Counterparty", "GWML Counterparty"};

复制代码
@Override
public String[] generateHeader() {
    return HEADER;
}

}

并在StatefulBeanToCsvBuilder使用它:

final CustomMappingStrategy mappingStrategy = new CustomMappingStrategy<>();

mappingStrategy.setType(MappingsBean.class);

final StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer)

.withMappingStrategy(mappingStrategy)

.build();

beanToCsv.write(makeFinalMappingBeanList());

writer.close()

在MappingsBean类中,我们留下了CsvBindByPosition注释 - 以控制排序(在此解决方案中, CsvBindByName注释)。 由于自定义映射策略,标题列名称包含在生成的 CSV 文件中。

此解决方案的缺点是,当我们通过CsvBindByPosition注释更改列顺序时,我们必须手动更改自定义映射策略中的HEADER常量。

方案二:

该方案可以根据实体类字段名称生成CSV第一行,注意一定不要加@CsvBindByName注解,具体文章可以参考opencsv 将对象数组导出为 csv 文件时、文件列按对象字段定义顺序排序的实现

复制代码
@SneakyThrows
public <T> String generateCsvFile(List<? extends T> exportResults, String fileName)
        throws IOException, CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
    String finalFileName = new File(nginxDownloadPath,
            fileName + System.currentTimeMillis() + ".csv").getPath();
    Writer writer = new FileWriter(finalFileName);
    CSVWriter csvWriter = new CSVWriter(
            writer,
            CSVWriter.DEFAULT_SEPARATOR,
            CSVWriter.DEFAULT_QUOTE_CHARACTER,
            CSVWriter.NO_ESCAPE_CHARACTER,
            CSVWriter.DEFAULT_LINE_END);
            csvWriter.writeNext(header);
    if (exportResults.size() > 0) {
        //写内容
        StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder<T>(writer).
                withMappingStrategy(new OrderColumnMappingStrategy(exportResults.get(0).getClass())).
                withIgnoreField(exportResults.get(0).getClass(), Arrays.stream(exportResults.get(0).getClass().getDeclaredFields()).filter(one -> {
                    one.setAccessible(true);
                    return one.isAnnotationPresent(CsvIgnore.class);
                }).findFirst().orElse(null)).
                build();
        beanToCsv.write(exportResults);
    }
    csvWriter.close();
    writer.close();
    return finalFileName;
}

public class OrderColumnMappingStrategy<T> extends HeaderColumnNameMappingStrategy<T> {
    private Locale errorLocale = Locale.getDefault();

    public OrderColumnMappingStrategy(Class<? extends T> type) {
        super();
        this.setErrorLocale(errorLocale);
        this.setType(type);
    }

    @Override
    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
        if (type == null) {
            throw new IllegalStateException(ResourceBundle
                    .getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
                    .getString("type.before.header"));
        }

        if (headerIndex.isEmpty()) {
            List<String> realHeaderList = new ArrayList<>();
			/**getFieldNameForCsvHeader()方法是通过反射获取对象的字段, 字段
			是按照定义顺序返回的. 这里就不贴出代码了*/
            getFieldNameForCsvHeader(type).forEach(one -> {
                realHeaderList.add(one.toUpperCase());
            });
            String[] header = realHeaderList.toArray(new String[0]);
            headerIndex.initializeHeaderIndex(header);
            return header;
        }
        return headerIndex.getHeaderIndex();
    }
}
相关推荐
川石课堂软件测试9 分钟前
涨薪技术|0到1学会性能测试第8课-Analysis分析图
服务器·数据库·python·sql·jmeter·mysql·容器
暴雨课堂11 分钟前
tp5 tp6被攻击篡改后恢复
linux·服务器·php
学编程的小程13 分钟前
服务器日志太分散不好查?我用Promtail+Loki+Grafana把采集、查询和分析串起来了
服务器·python·grafana
Shaoshing15 分钟前
消息顺序
java·开发语言
djjjx.31 分钟前
Linux进程篇(四):内核托底与生死交错 ,深度硬核剖析“僵尸”与“孤儿”进程
linux·运维·服务器
凉茶钱33 分钟前
list:哨兵节点,const_iterator 和 const iterator的区别与迭代器失效
数据结构·c++·list
我命由我1234539 分钟前
SQL 检测脚本
java·数据库·sql·功能测试·单元测试·压力测试·可用性测试
为思念酝酿的痛41 分钟前
Reactor反应堆模式
linux·服务器·网络
沫璃染墨42 分钟前
从零入门计算机网络系列(六):TCP套接字编程实战——从listen监听到客户端与服务器通信
linux·服务器·c++·tcp/ip·计算机网络·tcp
2401_872418781 小时前
把 Ubuntu 服务器变成 AI Agent 终端:用自然语言或语音管理 Linux,任何命令都先审批(附完整 Python 源码)
linux·服务器·ubuntu·agent