4.10. ファイルダウンロード¶
Caution
本バージョンの内容は既に古くなっています。最新のガイドラインはこちらからご参照ください。
目次
4.10.1. Overview¶
- Note - コントローラクラスで、ファイルレンダリングのロジックを持たせることは推奨しない。 - 理由としては、コントローラの役割から逸脱するためである。 また、コントローラから分離することで、Viewの入れ替えが、容易にできる。 
ファイルのダウンロード処理の概要を、以下に示す。
- DispatchServletは、コントローラへファイルダウンロードのリクエストを送信する。
- コントローラは、ファイル表示の情報を取得する。
- コントローラは、Viewを選択する。
- ファイルレンダリングは、Viewで行われる。
org.springframework.web.servlet.View インタフェースを提供している。org.springframework.web.servlet.view.document.AbstractPdfVieworg.springframework.web.servlet.view.document.AbstractXlsxVieworg.terasoluna.gfw.web.download.AbstractFileDownloadViewは、Tip
ファイルダウンロード機能を提供する際には、ディレクトリトラバーサル攻撃への対策が必要な場合がある。 ディレクトリトラバーサル攻撃については、ディレクトリトラバーサル攻撃 を参照すること。
4.10.2. How to use¶
4.10.2.1. PDFファイルのダウンロード¶
org.springframework.web.servlet.view.document.AbstractPdfViewを継承したクラスを作成する必要がある。4.10.2.1.1. カスタムViewの実装¶
AbstractPdfViewを継承したクラスの実装例
@Component  // (1)
public class SamplePdfView extends AbstractPdfView {  // (2)
  @Override
  protected void buildPdfDocument(Map<String, Object> model,
          Document document, PdfWriter writer, HttpServletRequest request,
          HttpServletResponse response) throws Exception {  // (3)
      document.add(new Paragraph((Date) model.get("serverTime")).toString());
  }
}
| 項番 | 説明 | 
|---|---|
| (1) | 本例では、 @Componentアノテーションを使用して、component-scanの対象としている。後述する、 org.springframework.web.servlet.view.BeanNameViewResolverの対象とすることができる。 | 
| (2) | AbstractPdfViewを継承する。 | 
| (3) | buildPdfDocumentメソッドを実装する。 | 
AbstractPdfViewは、PDFのレンダリングに、OpenPDFを利用している。Note
TERASOLUNA Server Framework for Java 5.4.xではiText 2.1.7をサポートしていたが、後継のiText 5.0.0よりAGPLライセンスに変更されたため、TERASOLUNA Server Framework for Java 5.5.1.RELEASEからiTextからフォークされたOpenPDFをサポートする。
OpenPDFでは、iText 2.1.7からいくつかのバグや脆弱性が修正されている。
<dependencies>
    <!-- omitted -->
    <dependency>
        <groupId>com.github.librepdf</groupId>
        <artifactId>openpdf</artifactId>
    </dependency>
</dependencies>
- Note - 上記設定例は、依存ライブラリのバージョンを親プロジェクトである terasoluna-gfw-parent で管理する前提であるため、pom.xmlでのバージョンの指定は不要である。 - Note - iText 2.1.7を利用する場合、日本語の出力を行うためにはiTextAsianを依存ライブラリに追加する必要があったが、OpenPDFはデフォルトで日本語に対応しているため、追加は不要である。 
4.10.2.1.2. ViewResolverの定義¶
org.springframework.web.servlet.view.BeanNameViewResolverとは、
Springのコンテキストで管理されたbean名を用いて実行するViewを選択するクラスである。
BeanNameViewResolverを使用する際は、通常使用する、
- JSP用のViewResolver(InternalResourceViewResolver)
- Tiles用のViewResolver(TilesViewResolver)
より先にBeanNameViewResolverが実行されるように定義する事を推奨する。
Note
Spring FrameworkはさまざまなViewResolverを提供しており、複数のViewResolverをチェーンすることができる。
そのため、特定の状況では、意図しないViewが選択されてしまうことがある。
この動作は、<mvc:view-resolvers>要素の子要素に、優先したいViewResolverを上から順に定義する事で防ぐことができる。
bean定義ファイル
 <mvc:view-resolvers>
     <mvc:bean-name /> <!-- (1) (2) -->
     <mvc:jsp prefix="/WEB-INF/views/" />
 </mvc:view-resolvers>
| 項番 | 説明 | 
|---|---|
| (1) | <mvc:bean-name>要素を使用して、BeanNameViewResolverを定義する。 | 
| (2) | <mvc:bean-name>要素を先頭に定義し、通常使用するViewResolver(JSP用のViewResolver)より優先度を高くする。 | 
4.10.2.1.3. コントローラでのViewの指定¶
BeanNameViewResolverにより、コントローラで”samplePdfView”を返却することで、Javaソースコード
@RequestMapping(value = "home", params= "pdf", method = RequestMethod.GET)
public String homePdf(Model model) {
    model.addAttribute("serverTime", new Date());
    return "samplePdfView";   // (1)
}
| 項番 | 説明 | 
|---|---|
| (1) | “samplePdfView” をメソッドの戻り値として返却することで、 Springのコンテキストで管理された、 SamplePdfViewクラスが実行される。 | 
4.10.2.2. Excelファイルのダウンロード¶
org.springframework.web.servlet.view.document.AbstractXlsxViewを継承したクラスを作成する必要がある。4.10.2.2.1. カスタムViewの実装¶
AbstractXlsxViewを継承したクラスの実装例
@Component  // (1)
public class SampleExcelView extends AbstractXlsxView {  // (2)
    @Override
    protected void buildExcelDocument(Map<String, Object> 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);
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | 本例では、 @Componentアノテーションを使用して、component-scanの対象としている。前述した、 org.springframework.web.servlet.view.BeanNameViewResolverの対象とすることができる。 | 
| (2) | AbstractXlsxViewを継承する。 | 
| (3) | buildExcelDocumentメソッドを実装する。 | 
AbstractXlsxViewは、EXCELのレンダリングに、Apache POIを利用している。<dependencies>
    <!-- omitted -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
    </dependency>
</dependencies>
- Note - 上記設定例は、依存ライブラリのバージョンを親プロジェクトである terasoluna-gfw-parent で管理する前提であるため、pom.xmlでのバージョンの指定は不要である。 
- Note - xlsファイル形式をサポートしたい場合は - AbstractXlsViewを使用されたい。 詳細は、AbstractXlsViewのJavaDocを参照されたい。
4.10.2.2.2. ViewResolverの定義¶
設定は、PDFファイルをレンダリングする場合と同様である。詳しくは、ViewResolverの定義を参照されたい。
4.10.2.2.3. コントローラでのViewの指定¶
BeanNameViewResolverにより、コントローラで”sampleExcelView”を返却することで、Javaソース
@RequestMapping(value = "home", params= "excel", method = RequestMethod.GET)
public String homeExcel(Model model) {
    model.addAttribute("serverTime", new Date());
    return "sampleExcelView";  // (1)
}
| 項番 | 説明 | 
|---|---|
| (1) | “sampleExcelView” をメソッドの戻り値として返却することで、 Springのコンテキストで管理された、 SampleExcelViewクラスが実行される。 | 
4.10.2.3. 任意のファイルのダウンロード¶
org.terasoluna.gfw.web.download.AbstractFileDownloadViewを継承したクラスを実装すればよい。AbstractFileDownloadViewでは、以下を実装する必要がある。- レスポンスボディへの書き込むためのInputStreamを取得する。
- HTTPヘッダに情報を設定する。
4.10.2.3.1. カスタムViewの実装¶
AbstractFileDownloadViewを継承したクラスの実装例
@Component  // (1)
public class TextFileDownloadView extends AbstractFileDownloadView {  // (2)
   @Override
   protected InputStream getInputStream(Map<String, Object> model,
           HttpServletRequest request) throws IOException {  // (3)
       Resource resource = new ClassPathResource("abc.txt");
       return resource.getInputStream();
   }
   @Override
   protected void addResponseHeader(Map<String, Object> model,
           HttpServletRequest request, HttpServletResponse response) {  // (4)
       response.setHeader("Content-Disposition",
               "attachment; filename=abc.txt");
       response.setContentType("text/plain");
   }
}
| 項番 | 説明 | 
|---|---|
| (1) | 本例では、 @Componentアノテーションを使用して、component-scanの対象としている。前述した、 org.springframework.web.servlet.view.BeanNameViewResolverの対象とすることができる。 | 
| (2) | AbstractFileDownloadViewを継承する。 | 
| (3) | getInputStreamメソッドを実装する。ダウンロード対象の、 InputStreamを返却すること。 | 
| (4) | addResponseHeaderメソッドを実装する。ダウンロードするファイルに合わせた、 Content-Dispositionや、ContentTypeを設定する。 | 
4.10.2.3.2. ViewResolverの定義¶
設定は、PDFファイルをレンダリングする場合と同様である。詳しくは、ViewResolverの定義を参照されたい。
4.10.2.3.3. コントローラでのViewの指定¶
BeanNameViewResolverにより、コントローラで”textFileDownloadView”を返却することで、Javaソース
@RequestMapping(value = "download", method = RequestMethod.GET)
public String download() {
    return "textFileDownloadView"; // (1)
}
| 項番 | 説明 | 
|---|---|
| (1) | “textFileDownloadView” をメソッドの戻り値として返却することで、 Springのコンテキストで管理された、 TextFileDownloadViewクラスが実行される。 | 
Tip
前述してきたように、SpringはModelの情報をいろいろなViewにレンダリングすることができる。 Springでは、複数のレンダリングエンジンをサポートしており、さまざまなViewを返却することが可能である。 詳細は、Spring の公式ドキュメントSpring Framework Documentation -View Technologies-を参照されたい。

