AbstractViewを継承してJasperReportでCSVファイルを作成する
CSVファイルをダウンロードする実装として、CSV形式でレンダリングするviewを実装します。
実装
コントローラー
パラメータに応じてデータを検索します。検索結果はbeanのListに設定します。
org.springframework.web.servlet.view.AbstractView
の具象クラスにパラメータとして下のものを渡します。
- JasperReportの
.japser
ファイル - JasperReportのデータソースになる検索結果
- JasperReportのパラメータになるMap
@Controller @RequestMapping("report") public class ReportController { @Autowired TimeEntriesFacade timeEntrysEntriesFacade; /** * 作業時間をCSVにして出力する * * @return * @throws NotFoundException */ @RequestMapping(value = "/timeEntries/{from_date}/{to_date}", method = RequestMethod.GET) public ModelAndView timeEntries( @PathVariable("from_date") @DateTimeFormat(pattern="yyyy-MM-dd")Date from_date , @PathVariable("to_date") @DateTimeFormat(pattern="yyyy-MM-dd")Date to_date) { List<ReportingTimeEntries>csvRows = timeEntrysEntriesFacade.loadEntries(from_date, to_date); final File jasper = JavaUtil.findResourceFile("jasper_report/timeEntries.jasper"); Map<String,Object> reportParams = new HashMap<String,Object>(); ModelAndView mav = new ModelAndView(new JasperReprotCSVView<ReportingTimeEntries>(jasper, reportParams, csvRows, "time_entries.csv")); return mav; } }
AbstractViewの具象クラス
レスポンスに必要なヘッダーを設定し、response.getOutputStream()で得られるoutputStreamにJaspreReportが書き込みます。JaspreReportを利用する実装はCSVクラスに異状します。
import java.io.File; import java.io.OutputStream; import java.util.List; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import org.springframework.web.servlet.view.AbstractView; /** * * Jasper Reportを利用してCSVを出力するView * */ public class JasperReprotCSVView<T> extends AbstractView { /** The content type for an Excel response */ private static final String CONTENT_TYPE = "text/csv;charset=SHIFT-JIS"; /** * jasperファイル */ private File jasperFile ; /** * CSVの元データ */ private List<T> csvRows ; /** JasperReportに渡すパラメター */ private Map<String,Object> reportParams; /** * <code>Content-Disposition: attachment; filename=xxx</coe>に設定するファイル名 */ private String filename ; /** * デフォルトコンストラクター. * Sets the content type of the view to "text/csv;charset=SHIFT-JIS". */ public JasperReprotCSVView() { setContentType(CONTENT_TYPE); } public JasperReprotCSVView(File jasperFile, Map<String,Object> reportParams, List<T> csvRows, String filename){ this(); this.jasperFile = jasperFile; this.csvRows =csvRows; this.reportParams = reportParams; this.filename = filename; } /* (非 Javadoc) * @see org.springframework.web.servlet.view.AbstractView#generatesDownloadContent() */ @Override protected boolean generatesDownloadContent() { return true; } /* (非 Javadoc) * @see org.springframework.web.servlet.view.AbstractView#renderMergedOutputModel(java.util.Map<java.lang.String,java.lang.Object>, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { if ( jasperFile == null || ! jasperFile.exists()){ // TODO throw exception!! } // Set the content type. response.setContentType(getContentType()); response.setHeader("Content-Disposition","attachment; filename=" + filename); ServletOutputStream out = response.getOutputStream(); write(jasperFile, reportParams, csvRows, out); out.flush(); } /** * CSVを作成ストリームに書き込みます。 * @param jasperFile jasper reportの.jasperファイル * @param reportParams レポートのパラメータ * @param dataSource データソース * @param out CSVを書き込むストリーム * @throws JRException * @throws JasperFileNotFoundException */ void write(File jasperFile, Map<String,Object> reportParams, List<T> dataSource,OutputStream out) throws JRException, JasperFileNotFoundException { write(jasperFile, reportParams, new JRBeanCollectionDataSource(dataSource), out); } /** * CSVを作成ストリームに書き込みます。 * @param jasperFile jasper reportのJRXMLファイル * @param reportParams レポートのパラメータ * @param dataSource データソース * @param out CSVを書き込むストリーム * @throws JRException * @throws JasperFileNotFoundException */ void write(File jasperFile, Map<String,Object> reportParams, JRDataSource dataSource,OutputStream out) throws JRException, JasperFileNotFoundException { new Csv().write(jasperFile, reportParams, dataSource, out); } }
CSVクラスの実装
ダウンロードしたCSVファイルはExcelで利用することを期待しているので、文字コードをShift-JISにし、改行コードをCRLFにします。
import java.io.File; import java.io.OutputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JREmptyDataSource; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRParameter; 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.JRCsvExporter; import net.sf.jasperreports.export.SimpleExporterInput; import net.sf.jasperreports.export.SimpleWriterExporterOutput; /** * CSV形式のデータを作成します。 * */ public class Csv { /** * CSVファイルのエンコード */ final String fileEncode = "MS932"; /** * CSVファイルを作成します。 * @param jasperFile jasper reportの.jasperファイル * @param reportParams レポートのパラメータ * @param dataSource データソース * @param out CSVを書き込むストリーム * @throws JRException * @throws JasperFileNotFoundException 引数 jasperFileに指定されたファイルが存在しない */ public <T> void write(File jasperFile, Map<String,Object> reportParams, List<T> dataSource,OutputStream out) throws JRException, JasperFileNotFoundException { write(jasperFile, reportParams, new JRBeanCollectionDataSource(dataSource), out); } /** * CSVファイルを作成します。 * @param jasperFile jasper reportの.jasperファイル * @param reportParams レポートのパラメータ * @param dataSource データソース * @param out CSVを書き込むストリーム * @throws JRException jasper reportの処理で例外が発生した * @throws JasperFileNotFoundException 引数 jasperFileに指定されたファイルが存在しない */ public void write(File jasperFile, Map<String,Object> reportParams, JRDataSource dataSource,OutputStream out) throws JRException, JasperFileNotFoundException { if ( jasperFile == null || ! jasperFile.exists()){ throw new JasperFileNotFoundException(jasperFile); } Map<String, Object> fillParams = new HashMap<String,Object>(reportParams); fillParams.put("SUBREPORT_DIR", jasperFile.getParentFile().getAbsolutePath()+File.separator); fillParams.put(JRParameter.IS_IGNORE_PAGINATION, Boolean.TRUE); JasperPrint jrprint = JasperFillManager.fillReport(jasperFile.getAbsolutePath(), fillParams, dataSource != null ? dataSource : new JREmptyDataSource()); // 改行コードをCRLFにする jrprint.setProperty("net.sf.jasperreports.export.csv.record.delimiter", "\r\n"); JRCsvExporter exporter = new JRCsvExporter(); exporter.setExporterInput(new SimpleExporterInput(jrprint)); exporter.setExporterOutput(new SimpleWriterExporterOutput(out, fileEncode)); exporter.exportReport(); } }
TODO
spring bootを一つのjarにして実行するとhttp://huruyosi.hatenablog.com/entry/2015/12/08/100445.jasper
ファイルの読み込みに失敗する- JasperReportの定義ファイル
.jrxml
から.jasper
へのコンパイルをビルドの一環として組み込む - エラー実装
- PDFやExcelなどにも転用しやすいリファクタリング
- エラーチェックが面倒だからJasperReprotCSVView のコンストラクターで必要なパラメータを受け取っている。これでよいのか?