OpenCSV 默认导出列顺序错乱?用这招搞定!

目录

1:引入csv依赖

2:实体类

3:列顺序策略类

4:导出工具类

5:使用方式


1:引入csv依赖

    implementation 'com.opencsv:opencsv:5.7.1'

2:实体类

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String capitalAccount;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String cashOutCapitalAccount;

    @ExcelProperty("兑入资金账户")
    @CsvBindByName(column = "xxxx")
    private String cashInCapitalAccount;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String name;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String cashOutCurrencyType;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String cashOutAmount;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String referenceRate;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String cashOutRate;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String cashInCurrencyType;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String cashInAmount;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String remark;

    @ExcelProperty(value = "xxxx")
    @CsvBindByName(column = "xxxx")
    private String approveStatus;

    @ExcelProperty("xxxx")
    @CsvBindByName(column = "xxxx")
    private String approveInfo;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = NORMAL_DATETIME)
    @ExcelProperty(value = "申请时间", converter = LocalDateTimeStringConverter.class)
    private LocalDateTime applicationTime;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = NORMAL_DATETIME)
    @ExcelProperty(value = "审批时间", converter = LocalDateTimeStringConverter.class)
    private LocalDateTime transferDate;

    @ExcelIgnore
    @CsvBindByName(column = "xxxx")
    private String createTime;

    @ExcelIgnore
    @CsvBindByName(column = "xxxx")
    private String approveTime;

3:列顺序策略类

/**
 * 列顺序策略类
 *
 * @param <T>
 */
public class OrderedColumnMappingStrategy<T> extends HeaderColumnNameMappingStrategy<T> {
    //为了加快比较顺序,用Map
    private final Map<String, Integer> columnOrderMap = new HashMap<>();

    public OrderedColumnMappingStrategy(Class<T> clazz, String[] columnOrder) {
        setType(clazz);
        for (int i = 0; i < columnOrder.length; i++) {
            columnOrderMap.put(columnOrder[i].toUpperCase(), i);
        }
        setColumnOrderOnWrite(Comparator.comparingInt(c -> columnOrderMap.getOrDefault(c.toUpperCase(), Integer.MAX_VALUE)));
    }

    @Override
    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
        String[] defaultHeaders = super.generateHeader(bean);
        List<String> sorted = Arrays.asList(defaultHeaders);
        sorted.sort(Comparator.comparingInt(c -> columnOrderMap.getOrDefault(c.toUpperCase(), Integer.MAX_VALUE)));
        return sorted.toArray(new String[0]);
    }
}

4:导出工具类

public class CsvExportUtils {

    /**
     * 通用导出方法:支持列顺序 + UTF-8 BOM + 自定义列头
     *
     * @param outputStream 输出流(例如 response.getOutputStream())
     * @param dataList     数据列表
     * @param columnOrder  列顺序(要与实体类 @CsvBindByName 的 column 对应)
     * @param clazz        实体类类型
     */
    public static <T> void exportCsvOrdered(java.io.OutputStream outputStream, List<T> dataList, String[] columnOrder, Class<T> clazz) throws Exception {
        // 写入 UTF-8 BOM,防止中文乱码
        outputStream.write(0xEF);
        outputStream.write(0xBB);
        outputStream.write(0xBF);

        try (Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) {
            OrderedColumnMappingStrategy<T> strategy = new OrderedColumnMappingStrategy<>(clazz, columnOrder);
            StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
                    .withMappingStrategy(strategy)
                    .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
                    .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER) // 防止加引号
                    .withApplyQuotesToAll(false)
                    .build();
            beanToCsv.write(dataList);
            writer.flush();
        }
    }

    /**
     * 获取实体类中所有字段的 @CsvBindByName(column = "") 值,按字段声明顺序排列
     */
    public static String[] getCsvColumnOrder(Class<?> clazz) {
        List<String> columnList = new CopyOnWriteArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            CsvBindByName annotation = field.getAnnotation(CsvBindByName.class);
            if (annotation != null) {
                columnList.add(annotation.column());
            }
        }
        return columnList.toArray(new String[0]);
    }

}

5:使用方式

  FileUtils.downloadFileCsvResponse(response, "货币兑换申请");
  String[] columnOrder CsvExportUtils.getCsvColumnOrder(CurrencyExchangeExcelCsvDTO.class);
  CsvExportUtils.exportCsvOrdered(response.getOutputStream(), csvList,columnOrder, CurrencyExchangeExcelCsvDTO.class);

// 调用方法
  public static void downloadFileCsvResponse(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
        response.setContentType("text/csv;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
    }

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大大怪~将军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值