huruyosi’s blog

プログラミングとかインフラとかのメモです。

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