ファイルダウンロード ================================================================================ .. only:: html .. contents:: 目次 :depth: 3 :local: Overview -------------------------------------------------------------------------------- | 本節では、Springでクライアントにサーバからファイルをダウンロードする機能について説明する。 | Spring MVCのViewで、ファイルのレンダリングを行うことを推奨する。 \ .. note:: コントローラクラスで、ファイルレンダリングのロジックを持たせることは推奨しない。 理由としては、コントローラの役割から逸脱するためである。 また、コントローラから分離することで、Viewの入れ替えが、容易にできる。 ファイルのダウンロード処理の概要を、以下に示す。 #. DispatchServletは、コントローラへファイルダウンロードのリクエストを送信する。 #. コントローラは、ファイル表示の情報を取得する。 #. コントローラは、Viewを選択する。 #. ファイルレンダリングは、Viewで行われる。 | SpringベースのWebアプリケーションで、ファイルをレンダリングするため、 | 本ガイドラインでは、カスタムビューを実装することを推奨する。 | Spring フレームワークでは、カスタムビューの実装に | ``org.springframework.web.servlet.View`` インタフェースを提供している。 | | **PDFファイルの場合** | Springから提供されている\ ``org.springframework.web.servlet.view.document.AbstractPdfView``\ | クラスは、modelの情報を用いてPDFファイルをレンダリングするときに、サブクラスとして利用するクラスである。 | | **Excelファイルの場合** | Springから提供されている\ ``org.springframework.web.servlet.view.document.AbstractXlsxView``\ | クラスは、modelの情報を用いてExcelファイルをレンダリングするときに、サブクラスとして利用するクラスである。 | | Spring では上記以外にも、いろいろなViewの実装を提供している。 | Viewの技術詳細は、\ `Spring Reference View technologies `_\ を参照されたい。 | 共通ライブラリから提供している、\ ``org.terasoluna.gfw.web.download.AbstractFileDownloadView``\ は、 | 任意のファイルをダウンロードするために使用する抽象クラスである。 | PDFやExcel形式以外のファイルをレンダリングする際に、本クラスをサブクラスに定義する。 .. tip:: ファイルダウンロード機能を提供する際には、ディレクトリトラバーサル攻撃への対策が必要な場合がある。 ディレクトリトラバーサル攻撃については、\ :ref:`file-upload_security_related_warning_points_directory_traversal` \を参照すること。 | How to use -------------------------------------------------------------------------------- PDFファイルのダウンロード ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | PDFファイルのレンダリングには、Springから提供されている、 | \ ``org.springframework.web.servlet.view.document.AbstractPdfView``\ を継承したクラスを作成する必要がある。 | コントローラでPDFダウンロードを実装するための手順は、以下で説明する。 カスタムViewの実装 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" **AbstractPdfViewを継承したクラスの実装例** .. code-block:: java @Component // (1) public class SamplePdfView extends AbstractPdfView { // (2) @Override protected void buildPdfDocument(Map model, Document document, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception { // (3) document.add(new Paragraph((Date) model.get("serverTime")).toString()); } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 本例では、\ ``@Component``\ アノテーションを使用して、component-scanの対象としている。 | 後述する、\ ``org.springframework.web.servlet.view.BeanNameViewResolver``\ の対象とすることができる。 * - | (2) - | \ ``AbstractPdfView``\ を継承する。 * - | (3) - | \ ``buildPdfDocument``\ メソッドを実装する。 | \ ``AbstractPdfView``\ は、PDFのレンダリングに、\ `iText `_\ を利用している。 | そのため、Mavenのpom.xmlに itextの定義を追加する必要がある。 .. code-block:: xml com.lowagie itext xml-apis xml-apis bctsp-jdk14 org.bouncycastle jfreechart jfree dom4j dom4j org.swinglabs pdf-renderer org.bouncycastle bcprov-jdk14 org.bouncycastle bcprov-jdk14 1.38 \ .. note:: 上記設定例は、依存ライブラリのバージョンを親プロジェクトである terasoluna-gfw-parent で管理する前提であるため、pom.xmlでのバージョンの指定は不要である。 上記のitextはterasoluna-gfw-parentが利用している\ `Spring IO Platform `_\ で定義済みである。 .. _viewresolver-label: ViewResolverの定義 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" \ ``org.springframework.web.servlet.view.BeanNameViewResolver``\ とは、 Springのコンテキストで管理されたbean名を用いて実行するViewを選択するクラスである。 \ ``BeanNameViewResolver``\ を使用する際は、通常使用する、 * JSP用の\ ``ViewResolver``\(\ ``InternalResourceViewResolver``\) * Tiles用の\ ``ViewResolver``\(\ ``TilesViewResolver``\) より先に\ ``BeanNameViewResolver``\が実行されるように定義する事を推奨する。 .. note:: Spring Frameworkはさまざまな\ ``ViewResolver``\ を提供しており、複数の\ ``ViewResolver``\をチェーンすることができる。 そのため、特定の状況では、意図しないViewが選択されてしまうことがある。 この動作は、\ ``ViewResolver``\に適切な優先順位を設定する事で防ぐことができる。 優先順位の設定方法は、\ ``ViewResolver``\ の定義方法によって異なる。 * Spring Framework 4.1から追加された\ ````\ 要素を使用して\ ``ViewResolver``\ を定義する場合は、子要素に指定する\ ``ViewResolver``\の定義順が優先順位となる。(上から順に実行される) * 従来通り\ ````\ 要素を使用して\ ``ViewResolver``\ を指定する場合は、\ ``order``\ プロパティに優先順位を設定する。(設定値が小さいものから実行される) | **bean定義ファイル** .. code-block:: xml :emphasize-lines: 2 .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | Spring Framework 4.1から追加された\ ````\ 要素を使用して、\ ``BeanNameViewResolver``\ を定義する。 * - | (2) - | \ ````\ 要素を先頭に定義し、通常使用する\ ``ViewResolver``\ (JSP用の\ ``ViewResolver``\ )より優先度を高くする。 .. tip:: \ ````\ 要素はSpring Framework 4.1から追加されたXML要素である。 \ ````\ 要素を使用すると、\ ``ViewResolver``\ をシンプルに定義することが出来る。 従来通り\ ````\ 要素を使用した場合の定義例を以下に示す。 .. code-block:: xml :emphasize-lines: 1-3 \ ``order``\ プロパティに、\ ``InternalResourceViewResolver``\ より小さい値を指定し、優先度を高くする。 | コントローラでのViewの指定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | \ ``BeanNameViewResolver``\ により、コントローラで"samplePdfView"を返却することで、 | Springのコンテキストで管理されたBeanIDにより、"samplePdfView"であるViewが使用される。 **Javaソースコード** .. code-block:: java @RequestMapping(value = "home", params= "pdf", method = RequestMethod.GET) public String homePdf(Model model) { model.addAttribute("serverTime", new Date()); return "samplePdfView"; // (1) } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | "samplePdfView" をメソッドの戻り値として返却することで、 | Springのコンテキストで管理された、\ ``SamplePdfView``\ クラスが実行される。 | 上記の手順を実行した後、以下に示すようなPDFを開くことができる。 .. figure:: ./images/file-download-pdf.png :alt: FILEDOWNLOAD PDF :width: 60% :align: center **Picture - FileDownload PDF** | Excelファイルのダウンロード ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | EXCELファイルのレンダリングには、Springから提供されている、 | \ ``org.springframework.web.servlet.view.document.AbstractXlsxView``\ を継承したクラスを作成する必要がある。 | コントローラでEXCELファイルをダウンロードさせるための実装手順は、以下で説明する。 カスタムViewの実装 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" **AbstractXlsxViewを継承したクラスの実装例** .. code-block:: java @Component // (1) public class SampleExcelView extends AbstractXlsxView { // (2) @Override protected void buildExcelDocument(Map model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { // (3) Sheet sheet; Cell cell; sheet = workbook.createSheet("Spring"); sheet.setDefaultColumnWidth(12); // write a text at A1 cell = getCell(sheet, 0, 0); setText(cell, "Spring-Excel test"); cell = getCell(sheet, 2, 0); setText(cell, ((Date) model.get("serverTime")).toString()); } private Cell getCell(Sheet sheet, int rowNumber, int cellNumber) { Row row = sheet.createRow(rowNumber); return row.createCell(cellNumber); } private void setText(Cell cell, String text) { cell.setCellValue(text); } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 本例では、\ ``@Component``\ アノテーションを使用して、component-scanの対象としている。 | 前述した、\ ``org.springframework.web.servlet.view.BeanNameViewResolver``\ の対象とすることができる。 * - | (2) - | \ ``AbstractXlsxView``\ を継承する。 * - | (3) - | \ ``buildExcelDocument``\ メソッドを実装する。 | \ ``AbstractXlsxView``\ は、EXCELのレンダリングに、\ `Apache POI `_\ を利用している。 | そのため、Mavenのpom.xmlに POIの定義を追加する必要がある。 .. code-block:: xml org.apache.poi poi-ooxml stax stax-api \ .. note:: poi-ooxmlが依存しているstax-apiはJava SEより標準で提供されるようになったため、不要なライブラリである。また、ライブラリの競合が発生する可能性があることから、 \ ````\ 要素を追加し、当該ライブラリがアプリケーションに含まれないよう考慮する必要がある。 \ .. note:: 上記設定例は、依存ライブラリのバージョンを親プロジェクトである terasoluna-gfw-parent で管理する前提であるため、pom.xmlでのバージョンの指定は不要である。 上記の依存ライブラリはterasoluna-gfw-parentが利用している\ `Spring IO Platform `_\ で定義済みである。 また、\ ``AbstractExcelView``\ はSpring Framework 4.2から@Deprecatedとなった。そのため、xlsファイルを使用したい場合も同様に\ ``AbstractXlsxView``\ の使用を推奨する。 詳細は、\ `AbstractExcelViewのJavaDoc `_\ を参照されたい。 ViewResolverの定義 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 設定は、PDFファイルをレンダリングする場合と同様である。詳しくは、\ :ref:`viewresolver-label`\ を参照されたい。 コントローラでのViewの指定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | \ ``BeanNameViewResolver``\ により、コントローラで"sampleExcelView"を返却することで、 | Springのコンテキストで管理されたBeanIDにより、”sampleExcelView”であるViewが使用される。 **Javaソース** .. code-block:: java @RequestMapping(value = "home", params= "excel", method = RequestMethod.GET) public String homeExcel(Model model) { model.addAttribute("serverTime", new Date()); return "sampleExcelView"; // (1) } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | "sampleExcelView" をメソッドの戻り値として返却することで、 | Springのコンテキストで管理された、\ ``SampleExcelView``\ クラスが実行される。 | 上記の手順を実行した後、以下に示すようなEXCELを開くことができる。 .. figure:: ./images/file-download-excel.png :alt: FILEDOWNLOAD EXCEL :width: 60% :align: center **Picture - FileDownload EXCEL** 任意のファイルのダウンロード ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 前述した、PDFやEXCELファイル以外のファイルのダウンロードを行う場合、 | 共通ライブラリが提供している、\ ``org.terasoluna.gfw.web.download.AbstractFileDownloadView``\ を継承したクラスを実装すればよい。 | 他の形式にファイルレンダリングするために、\ ``AbstractFileDownloadView``\では、以下を実装する必要がある。 1. レスポンスボディへの書き込むためのInputStreamを取得する。 2. HTTPヘッダに情報を設定する。 | コントローラでファイルダウンロードを実装するための手順は、以下で説明する。 カスタムViewの実装 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | テキストファイルをダウンロードする例を用いて、説明を行う。 **AbstractFileDownloadViewを継承したクラスの実装例** .. code-block:: java @Component // (1) public class TextFileDownloadView extends AbstractFileDownloadView { // (2) @Override protected InputStream getInputStream(Map model, HttpServletRequest request) throws IOException { // (3) Resource resource = new ClassPathResource("abc.txt"); return resource.getInputStream(); } @Override protected void addResponseHeader(Map model, HttpServletRequest request, HttpServletResponse response) { // (4) response.setHeader("Content-Disposition", "attachment; filename=abc.txt"); response.setContentType("text/plain"); } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 本例では、\ ``@Component``\ アノテーションを使用して、component-scanの対象としている。 | 前述した、\ ``org.springframework.web.servlet.view.BeanNameViewResolver``\ の対象とすることができる。 * - | (2) - | \ ``AbstractFileDownloadView``\ を継承する。 * - | (3) - | \ ``getInputStream``\ メソッドを実装する。 | ダウンロード対象の、\ ``InputStream``\ を返却すること。 * - | (4) - | \ ``addResponseHeaderメソッド``\ を実装する。 | ダウンロードするファイルに合わせた、 Content-Dispositionや、ContentTypeを設定する。 ViewResolverの定義 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 設定は、PDFファイルをレンダリングする場合と同様である。詳しくは、\ :ref:`viewresolver-label`\ を参照されたい。 コントローラでのViewの指定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | \ ``BeanNameViewResolver``\ により、コントローラで"textFileDownloadView"を返却することで、 | Springのコンテキストで管理されたBeanIDにより、”textFileDownloadView”であるViewが使用される。 **Javaソース** .. code-block:: java @RequestMapping(value = "download", method = RequestMethod.GET) public String download() { return "textFileDownloadView"; // (1) } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | "textFileDownloadView" をメソッドの戻り値として返却することで、 | Springのコンテキストで管理された、\ ``TextFileDownloadView``\ クラスが実行される。 \ .. tip:: 前述してきたように、SpringはModelの情報をいろいろなViewにレンダリングすることができる。 Springでは、Jasper Reportsのようなレンダリングエンジンをサポートし、さまざまなViewを返却することも可能である。 詳細は、Spring の公式ドキュメント\ `Spring reference `_\ を参照されたい。 .. raw:: latex \newpage