安徽房和城鄉(xiāng)建設(shè)部網(wǎng)站網(wǎng)站優(yōu)化排名技巧
問(wèn)題情景:
最近項(xiàng)目中遇到了根據(jù)第三方系統(tǒng)傳遞過(guò)來(lái)的參數(shù),封裝為L(zhǎng)ist<實(shí)體類(lèi)對(duì)象>后,將該實(shí)體類(lèi)轉(zhuǎn)換為csv文件,然后上傳到遠(yuǎn)程的sftp服務(wù)器指定目錄的需求。
實(shí)現(xiàn)思路:
- List<實(shí)體類(lèi)對(duì)象>轉(zhuǎn)為csv文件的過(guò)程。通過(guò)OpenCsv實(shí)現(xiàn)。
阻塞點(diǎn):
1.最開(kāi)始遇到了生成的csv文件的第一行生成的字段名,變成了實(shí)體類(lèi)的大寫(xiě)字段名了,例如userName變?yōu)閁SERNAME并且排序混亂。
2.通過(guò)查閱資料找到了以下兩個(gè)注解,后期卻發(fā)現(xiàn)兩個(gè)注解不能同時(shí)出現(xiàn)。詳見(jiàn)文章
為什么 opencsv 在寫(xiě)入文件時(shí)將 csv 標(biāo)頭大寫(xiě)
@CsvBindByName(column = "TradeID")
@CsvBindByPosition(position = 0)
解決方案:
方案一:
創(chuàng)建自定義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類(lèi)中,我們留下了CsvBindByPosition注釋 - 以控制排序(在此解決方案中, CsvBindByName注釋)。 由于自定義映射策略,標(biāo)題列名稱(chēng)包含在生成的 CSV 文件中。
此解決方案的缺點(diǎn)是,當(dāng)我們通過(guò)CsvBindByPosition注釋更改列順序時(shí),我們必須手動(dòng)更改自定義映射策略中的HEADER常量。
方案二:
該方案可以根據(jù)實(shí)體類(lèi)字段名稱(chēng)生成CSV第一行,注意一定不要加@CsvBindByName注解,具體文章可以參考o(jì)pencsv 將對(duì)象數(shù)組導(dǎo)出為 csv 文件時(shí)、文件列按對(duì)象字段定義順序排序的實(shí)現(xiàn)
@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) {//寫(xiě)內(nèi)容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);}@Overridepublic 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()方法是通過(guò)反射獲取對(duì)象的字段, 字段是按照定義順序返回的. 這里就不貼出代碼了*/getFieldNameForCsvHeader(type).forEach(one -> {realHeaderList.add(one.toUpperCase());});String[] header = realHeaderList.toArray(new String[0]);headerIndex.initializeHeaderIndex(header);return header;}return headerIndex.getHeaderIndex();}
}