背景
- JasperReportsでPDFを作成する記事は結構あるけどExcelに関する記事は少なかったので
- JasperReports自体はJavaで動作する帳票出力ライブラリ。昔からあるライブラリで日本語の情報も豊富。
環境
- Java 11
- jasperreports 6.19.1
- Gradle 7.4.2
実装
下準備
- 関連ライブラリを入れておく
build.gradle
plugins {
id 'java'
}
group = 'qiita.jasper.sample'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
targetCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
// Test
testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.0.1'
// Jasper
// https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports
implementation 'net.sf.jasperreports:jasperreports:6.19.1'
// Jasper Excel操作用
// Excelを操作するライブラリで入れないと実行時にエラーになるので注意
implementation 'org.apache.poi:poi:4.0.1'
}
- イメージだけですが以下のような帳票テンプレートを作成
- パラメーター名とフィールド名があっていればよいのでEXCEL出力においては重要なとこじゃないです
メインのとこ
- 出力データの読み込みといった処理の大筋はPDFを出力する場合と同じ。ざっくり以下のような流れ。
- 帳票テンプレートの保存場所とかの基本設定を読込
- 出力データを設定
- 帳票テンプレートをコンパイル
- 出力データとコンパイルしたものから出力形式などを決めるJasperPrintクラスのインスタンスを生成
- お好みの形式で出力
- 出力の時にJRXlsExporterクラスを使うところが違う。ここだけ気をつければOK。
ReportSample.java
package qiita.jasper.sample;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.JRXlsExporter;
import net.sf.jasperreports.export.OutputStreamExporterOutput;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
public class ReportSample {
protected JasperReportConfig jasperReportConfig;
/** 生成した帳票のパス */
protected Path path;
public Path getPath() {
return path;
}
/**
* 帳票の設定値を読込
*/
protected ReportSample() {
this.jasperReportConfig = new JasperReportConfig();
}
/**
* 帳票を生成する
*
* @throws IOException
* @throws JRException
* @throws FileNotFoundException
*/
public void generateExcelReport() throws FileNotFoundException, JRException, IOException {
// サマリー(繰返し部分以外)を詰める
Map<String, Object> params = makeBeansParams();
// 明細(繰返し部分)を詰める
JRDataSource dataSource = makeBeansDataSource();
// 作成実行
executeExcelReportGeneration(params, dataSource);
}
/**
* JasperReportsのParamtersに渡すインスタンスを生成する
* ※. 適当なサマリーデータを生成しているので、使う際は要修正
*
* @return サマリーデータ
*/
protected Map<String, Object> makeBeansParams() {
Map<String, Object> params = new HashMap<>();
params.put("title", "サンプルのタイトル");
params.put("pageHeader", "サンプルのページヘッダー");
params.put("columnHeader", "サンプルのカラムヘッダー");
params.put("columnFooter", "サンプルのカラムフッター");
params.put("pageFooter", "サンプルのページフッター");
params.put("summary", "サンプルのサマリー");
return params;
}
/**
* JasperReportsのFieldsに渡すインスタンスを生成する
* ※. 適当な明細データを生成しているので、使う際は要修正
*
* @return 明細データ
*/
protected JRDataSource makeBeansDataSource() {
List<Detail> items = IntStream.range(1, 10).boxed().map(i -> new Detail("明細名" + i, i * 10, "個"))
.collect(Collectors.toList());
return new JRBeanCollectionDataSource(items);
}
/**
* jasperReportsを使用してExcelファイルを作成する。 今回の一番重要な処理!!!
*
* @param params サマリーデータ
* @param dataSource 明細データ
* @throws JRException
* @throws IOException
* @throws FileNotFoundException
*/
private void executeExcelReportGeneration(Map<String, Object> params, JRDataSource dataSource)
throws JRException, FileNotFoundException, IOException {
complieJasperReports();
JasperPrint basePrint = JasperFillManager.fillReport(jasperReportConfig.jasperFilePath, params, dataSource);
// ↓↓ここ以降の処理がPDFの場合と異なる ↓↓
JRXlsExporter exporter = new JRXlsExporter();
exporter.setExporterInput(new SimpleExporterInput(basePrint));
// EXCELファイルの出力先を指定
String outputExcelFilePath = jasperReportConfig.outputExcelFilePath;
try (ByteArrayOutputStream excelStream = new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream(outputExcelFilePath)) {
OutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(excelStream);
exporter.setExporterOutput(exporterOutput);
exporter.exportReport();
excelStream.writeTo(fos);
}
// 生成したEXCELファイルのパスを入れておく
path = FileSystems.getDefault().getPath(outputExcelFilePath);
}
/**
* jrxmlファイルをコンパイルする。
*
* @throws JRException
*
*/
private void complieJasperReports() throws JRException {
JasperCompileManager.compileReportToFile(jasperReportConfig.jrxmlFilePath, jasperReportConfig.jasperFilePath);
}
/**
* JasperReportのファイル参照設定
*/
private class JasperReportConfig {
/** 帳票テンプレート */
private String jrxmlFilePath;
/** コンパイルしたもの */
private String jasperFilePath;
/** 生成したEXCELファイル */
private String outputExcelFilePath;
JasperReportConfig() {
File jrxmlFile = new File("src/main/resources/input/sample.jrxml");
this.jrxmlFilePath = jrxmlFile.getAbsolutePath();
File jasperFile = new File("src/main/resources/output/sample.jasper");
this.jasperFilePath = jasperFile.getAbsolutePath();
File outputPdfFile = new File("src/main/resources/output/sample.xls");
this.outputExcelFilePath = outputPdfFile.getAbsolutePath();
}
}
/**
* 帳票の明細部
*/
public class Detail {
private String name;
private Integer volume;
private String unit;
public String getName() {
return name;
}
public Integer getVolume() {
return volume;
}
public String getUnit() {
return unit;
}
public Detail(String name, Integer volume, String unit) {
super();
this.name = name;
this.volume = volume;
this.unit = unit;
}
}
}
テスト
- EXCEL作成とは直接関係はないです
ReportSampleTest.java
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package qiita.jasper.sample;
import static org.junit.jupiter.api.Assertions.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import net.sf.jasperreports.engine.JRException;
public class ReportSampleTest {
private static final String WORK_DIR = "src/main/resources/output/";
@BeforeAll
public static void initAll() throws IOException {
// 作業フォルダを空にする
cleanAllFile(WORK_DIR);
}
/**
* ディレクトリ以外のファイルを削除する。
*
* @param dir
* @throws IOException
*/
private static void cleanAllFile(String dir) throws IOException {
Path workDir = FileSystems.getDefault().getPath(dir);
try (Stream<Path> stream = Files.list(workDir).filter(p -> p.toFile().isFile())) {
stream.forEach(p -> {
try {
Files.deleteIfExists(p);
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
});
}
}
@Test
public void エクセルが生成されること() throws FileNotFoundException, JRException, IOException {
Path expected = FileSystems.getDefault().getPath(WORK_DIR + "sample.xls").toAbsolutePath();
ReportSample report = new ReportSample();
report.generateExcelReport();
Path actual = report.getPath().toAbsolutePath();
assertEquals(expected, actual);
}
}
結果
- 以下のように出力される
感想
- PDFとEXCELを1フォーマットで両方作成できるのはやっぱり便利