ファイルダウンロード用のViewを自作する

Springでファイルダウンロードを実装する方法はいろいろあるが、今回はViewを自作する方向でやってみたい。

利点

  • レスポンスヘッダーの設定など、冗長になりがちなコードをコントローラーに書かなくて済む。
  • 戻り値の型を文字列で統一できるので、条件によってダウンロードか画面表示かに分岐するようなコントローラーが作れる。

環境

Spring Boot 1.4

やり方

ファイルの種類ごとにViewを作成する。以下はPDFの例。

View

ファイルダウンロードの体裁を整えるクラス。コントローラーとのデータの受け渡しはModelを通して行う。ここではファイルデータをcontents、ファイル名をfileNameという名前で受け取ることにしている。

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;

public class PdfView extends AbstractView {
    @Override
    protected boolean generatesDownloadContent() {
        return true;
    }
    
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        byte[] pdf = (byte[]) model.get("contents");
        String fileName = (String) model.get("fileName");

        response.setHeader("Content-Disposition", "attachment; filename*=utf-8''"
                + java.net.URLEncoder.encode(fileName, "UTF-8"));
        response.setContentLength(pdf.length);
        response.setContentType("application/pdf");
        FileCopyUtils.copy(pdf, response.getOutputStream());
    }
}

設定

自作したViewとBeanNameViewResolverをBean定義する。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.BeanNameViewResolver;

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Bean
    public BeanNameViewResolver fileDownloadViewResolver() {
        BeanNameViewResolver resolver = new BeanNameViewResolver();
        resolver.setOrder(0);
        return resolver;
    }

    @Bean
    public PdfView pdfView() {
        return new PdfView();
    }
}

Controller

先に書いた通り、ファイルデータをcontents、ファイル名をfileNameという名前でModelにセットした後、自作したViewのBean名、pdfViewを戻り値として返す。

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class RootController {
    @GetMapping("pdf")
    public String pdf(Model model) {
        // PDFファイルの取得
        byte[] contents = getPdf();
        
        model.addAttribute("contents", contents);
        model.addAttribute("fileName", "テスト.pdf");
        return "pdfView";
    }
}