前提・実現したいこと
参考文献によると、ENTITYリストだけあればサクッとCSVがダウンロードできる。
これを実装した時は本当に感動しました。
ですが、
CsvMapperもCsvSchemaも使うのが初めてで、うまくカスタマイズできないでいます。
やりたいことは、
ENTITYに項目A、項目B、項目C、項目Dとあるとして、
今回は項目A、項目BだけのCSVをダウンロードしたい。
今回は項目A、項目C、項目DのCSVをダウンロードしたい。
といったことです。
ENTITYに関して、固定でCSVへの除外項目を設定する方法は見かけました。
でも、可変でというものが見つからないです。
全項目出力する場合のコード
【Controller】画面から"csvDL"でポチっと呼ばれてるだけ
java
1@RestController 2public class CsvDLController { 3 @RequestMapping(value = "csvDL", method = RequestMethod.POST) 4 public ResponseEntity<byte[]> download() { 5 return service.downloadCsv(); 6 } 7}
【Service】fieldsは項目を絞るためのもの(現時点では使っていない⇒使いたい)
java
1@Service 2@Transactional 3public class CsvDLService { 4 public ResponseEntity<byte[]> downloadCsv() { 5 List<String> fields = dao.colmuns(); 6 fields.remove("除外フィールド1"); 7 fields.remove("除外フィールド2"); 8 9 return csvUtil.dlCsv(dao.selectAll(), fields); 10 } 11}
【postgresql】dao.colmuns で呼んでるSQL (dao.selectAllは単純な全件検索なので割愛)
sql
1SELECT 2 colmuns.attname AS colmun 3FROM 4 pg_attribute AS colmuns 5INNER JOIN 6 pg_class AS tables 7 ON colmuns.attrelid = tables.oid 8WHERE 9 tables.relname = '出力テーブル' 10 and colmuns.attnum > 0 11order by colmuns.attnum
【Utility】レスポンスに乗せる処理(ほぼ参考文献のまま)
java
1@Component 2public class DownloadHelper { 3 private static final String CONTENT_DISPOSITION_FORMAT 4 = "attachment; filename=\"%s\"; filename*=UTF-8''%s"; 5 6 public void addContentDisposition(HttpHeaders headers, String fileName) 7 throws UnsupportedEncodingException { 8 String headerValue = String.format(CONTENT_DISPOSITION_FORMAT, 9 fileName, UriUtils.encode(fileName, StandardCharsets.UTF_8.name())); 10 headers.add(HttpHeaders.CONTENT_DISPOSITION, headerValue); 11 } 12 13 public ResponseEntity<byte[]> fileDL(String lines, String fileName, String enc) throws Exception { 14 HttpHeaders headers = new HttpHeaders(); 15 addContentDisposition(headers, fileName); 16 return new ResponseEntity<>(lines.getBytes(enc), headers, HttpStatus.OK); 17 } 18}
【Utility】エンティティリストからCSVを作る(引数cols以外は、ほぼ参考文献のまま)※colsは使っていない
java
1public class CsvUtil { 2 @Autowired 3 DownloadHelper dlHelper; 4 5 public ResponseEntity<byte[]> dlCsv(List<?> entityList, List<String> cols) { 6 try { 7 // CsvMapperの設定 8 CsvMapper mapper = new CsvMapper(); 9 // 文字列にダブルクオートをつける 10 mapper.configure(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS, true); 11 // CsvSchemaの設定(ヘッダをつける) 12 CsvSchema schema = mapper.schemaFor(entityList.get(0).getClass()).withHeader(); 13 14 return dlHelper.fileDL(mapper.writer(schema).writeValueAsString(entityList), 15 entityToFilename(entityList.get(0), ".csv"), "UTF-8"); 16 17 } catch(Exception e) { 18 log.error("csv出力失敗"); 19 e.printStackTrace(); 20 return new ResponseEntity<>("error end".getBytes(), HttpStatus.OK); 21 } 22 } 23 24 public String entityToFilename(Object entity, String ext) { 25 String[] spl = entity.getClass().getName().split(Pattern.quote(".")); 26 return spl[spl.length-1].concat(ext); 27 } 28}
組み込みたいこと
【Utility】上記csvUtilの全項目CSVダウンロード機能に、項目絞込み(可変)を組み込みたい
⇒ getMethodsメソッドで、欲しい項目の分だけGETメソッドを抽出
⇒ toObjLstメソッドで、ENTITYオブジェクトから欲しい項目だけList<Object>に入れている
⇒ 引数のstgt は、"set" または "get" です
java
1 List<Object> objLine; 2 3 public Method[] getMethods (String[] fields, Class<?> entity, String stgt) { 4 Method[] methods = entity.getMethods(); 5 Method[] ret = new Method[fields.length]; 6 7 mss: for(Method m : methods) { 8 if (stgt.equals(m.getName().substring(0, 3))) { 9 for(int i = 0; i < fields.length; i++) { 10 if (fields[i].equals(StrUtil.camelToSnake(m.getName().substring(3)))) { 11 ret[i] = m; 12 continue mss; 13 } 14 } 15 } 16 } 17 return ret; 18 } 19 20 public List<List<Object>> toObjLst(String[] fields, List<?> recLst) throws Exception { 21 // エンティティリスト⇒オブジェクトリスト 22 List<List<Object>> lines = new ArrayList<List<Object>>(recLst.size()); 23 // 取り出したい項目のGETメソッド 24 final Method[] methods = getMethods (fields, recLst.get(0).getClass(), "get"); 25 26 recLst.forEach(rec -> { 27 objLine = new ArrayList<Object>(fields.length); 28 try { 29 for (Method m : methods) objLine.add(m.invoke(rec)); 30 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 31 e.printStackTrace(); 32 return; 33 } 34 lines.add(objLine); 35 }); 36 37 return lines; 38 }}
試したこと
最初に参考にしたサイト:Springboot でCSVをダウンロードしたい
その他、CsvMapperとCsvSchema、jacksonの使い方で検索しまくったが、欲しい答えが得られず。
これらについて解説されている文献(本でもOK)を教えていただけるだけでも助かります。
CSVを指定したフォルダに作る機能はすでに作ってあり、そこで使っている機能(組み込みたいこと)と組み合わせて、なんとかならないかと考えています。
できればCsvMapperやCsvSchemaを使って、できるだけ綺麗にやりたいことを実現したいですが、
response.setHeader("Content-Disposition","attachment;filename=""
+ URLEncoder.encode(filenm, "UTF-8") + """);
で、作成したファイルをダウンロードさせる方法でもいいです。
こちらも、いまひとつ使い方がわかりません。自分メモ:参考文献
補足情報(FW/ツールのバージョンなど)
spring、postgres、java 新しめだと思います…。あとで追加します。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。