概要
SpringBootでcsvファイルを出力します。
csv出力機能はjackson-dataformats-textというライブラリを使います。
テストはJunit5とMockitoを使います。
成果物
動作環境
OS:Windows10
IDE:IntelliJ Community or CodeSpace
Javaバージョン:11(pom.xmlでバージョン変えればok、17は確認済み)
Spring bootバージョン:2.7.0
起動方法
1.githubからプロジェクトをクローンします。branchは「csv_output」を選択します。
https://github.com/RYA234/spring_boot_memo
2.src/main/java/com/example/spring_boot_memo/SpringBootMemoApplicationを実行します。
3.以下URLを開きます
http://localhost:5000/
開くと以下場面が表示されます。
プロダクトコード
MVC系
Model(データモデル)
package com.example.spring_boot_memo.csv;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Data;
// データモデルです。
@Data
@JsonPropertyOrder({"CODE", "名前", "値段"})
public class CsvData {
@JsonProperty("CODE")
int code;
@JsonProperty("名前")
String name;
@JsonProperty("値段")
int price;
public CsvData(int code, String name, int price) {
this.code = code;
this.name = name;
this.price = price;
}
}
Controller
package com.example.spring_boot_memo.csv;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@org.springframework.stereotype.Controller
public class Controller {
@Autowired
CsvService csvService;
@Autowired
DownloadHelper downloadHelper;
@RequestMapping(value = "index", params = "OK", method = RequestMethod.GET)
public String indexController() {
return "index";
}
@RequestMapping(value = "download", method = RequestMethod.POST)
public ResponseEntity<byte[]> download() throws IOException {
CsvSchema csvSchema;
List<CsvData> csvDataList = new ArrayList<>();
CsvData csvData = new CsvData(1, "寿司", 120);
csvDataList.add(csvData);
HttpHeaders headers = new HttpHeaders();
downloadHelper.addContentDisposition(headers, "日本語ファイル名.csv");
System.out.print(csvService.getCsvHeader());
csvSchema = csvService.getCsvHeader();
return new ResponseEntity<>(csvService.WriteCsvText(csvDataList, csvSchema).getBytes("MS932"), headers, HttpStatus.OK);
}
}
View
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>インデックス画面</title>
</head>
<body>
<form id="csvform" method="post" action="/download">
<input type="hidden" name="filename"/>
<button type="submit" name="OK">CSVを取得</button>
</form>
</body>
</html>
Service
package com.example.spring_boot_memo.csv;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CsvService {
@Autowired
private final CsvMapper mapper;
@Autowired
private CsvSchema csvSchema;
//DI
public CsvService(CsvMapper mapper, CsvSchema csvSchema) {
this.csvSchema = csvSchema;
this.mapper = mapper;
}
//CsvDataからCsvファイルのヘッダー情報を取得
public CsvSchema getCsvHeader() {
// CsvDataの@Jsonpropertyの文字列をヘッダーとして書き込む
csvSchema = mapper.schemaFor(CsvData.class).withHeader();
return csvSchema;
}
// csvファイルの内容をString型で作成します。
// 引数のcsvDataListはデータ部分、schemaはヘッダー部分
public String WriteCsvText(List<CsvData> csvDataList, CsvSchema csvSchema) throws JsonProcessingException {
return mapper.writer(csvSchema).writeValueAsString(csvDataList);
}
}
Config(Bean化する用)
jackson-dataformats-textのクラスをBean化します。
package com.example.spring_boot_memo.csv;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Bean
public CsvMapper getCsvMapper() {
return new CsvMapper();
}
@Bean
public CsvSchema getCsvSchema() {
return CsvSchema.builder().
build();
}
}
テストコード(Serviceのみです)
Serviceの単体テストをします。
package com.example.spring_boot_memo;
import com.example.spring_boot_memo.csv.CsvData;
import com.example.spring_boot_memo.csv.CsvService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(MockitoExtension.class)
@SpringBootTest
public class CsvServiceTest {
@Autowired
CsvService csvService;
@Test
@DisplayName("ヘッダーのみテスト")
void getCsvHeaderTest() {
CsvSchema testSchema = csvService.getCsvHeader();
String expect1 = "CODE";
String expect2 = "名前";
String expect3 = "値段";
String actual1 = testSchema.column(0).getName();
String actual2 = testSchema.column(1).getName();
String actual3 = testSchema.column(2).getName();
assertEquals(expect1, actual1);
assertEquals(expect2, actual2);
assertEquals(expect3, actual3);
}
@Test
@DisplayName("Csvファイルで出力される内容確認(ヘッダーと値)")
void getCsvHeadersTest() throws JsonProcessingException {
List<CsvData> csvDataList = new ArrayList<>() {
{
add(new CsvData(2, "高級焼肉", 1200));
}
};
String actual = csvService.WriteCsvText(csvDataList, csvService.getCsvHeader());
String expect = "CODE,名前,値段\n2,高級焼肉,1200\n";
assertEquals(expect, actual);
}
}
参考資料
プロダクトコード:
テストコード:
テストコードは元のgithubに書かれている場合が多いので、それを参考にすると上手く実装できると気づきました。
https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-csv/2.13.2