ファイルをダウンロードする
このページでは、ファイルをダウンロードする方法について説明します。
ファイルのダウンロード処理は、AbstractView を継承したカスタムViewクラスで実施します。 AbstractViewを継承したクラスをSpringのDIコンテナに登録しておくと、 BeanNameViewResolver が Controllerから返却されたBean名と一致する場合に、Viewとして選択してくれます。
Tip
Spring Web MVCやThymeleafでは、Controllerから返却された値に対応するViewを解決するため、いくつかのViewResolverが提供されています。Spring Bootを使用する場合、これらのViewResolverは自動で構成され、優先度の高いViewResolverから使用されます。Spring Web MVCが提供しているものについては View Resolution を、Thymeleafが提供しているものについては Views and View Resolvers を参照してください。
以下のサンプルコードの動作確認環境については、 動作確認環境と依存ライブラリについて を参照してください。
処理フロー
Controllerはファイルのダウンロード処理を実施するカスタムViewクラスのBean名を返却します。(このサンプルではテキストファイルのダウンロード処理を実施するBean名を返却します。)
BeanNameViewResolverは、Controllerから返却されたBean名に一致するカスタムViewクラスを選択します。
カスタムViewクラスは、レスポンスヘッダを設定してダウンロードファイルをレスポンスボディに書込みます。
実装例
- Controller
@GetMapping("/download") public String download(Model model) { // Viewで使用するダウンロードファイルパスをModelに設定します // (もしユーザが画面で入力したファイルパス等を使用する場合は、ディレクトリトラバーサル攻撃への対策も考慮してください) model.addAttribute(TextFileDownloadView.DOWNLOAD_FILE_INFO_KEY, new FileDownloadAttributes(targetFilePath, "ダウンロード.txt")); // ViewのBean名を返します return "textFileDownloadView"; }
- TextFileDownloadView
// @Componentを設定して、コンポーネントスキャン対象にします @Component public class TextFileDownloadView extends AbstractView { public static final String DOWNLOAD_FILE_INFO_KEY = "fileInfo"; private final ResourceLoader resourceLoader; public TextFileDownloadView(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { FileDownloadAttributes attributes = (FileDownloadAttributes) model.get(DOWNLOAD_FILE_INFO_KEY); // レスポンスヘッダを設定します response.setContentType("text/plain"); // 日本語のファイル名に対応するため、Content-DispositionはRFC6266の形式に沿って設定する ContentDisposition contentDisposition = ContentDisposition .attachment() .filename(attributes.getDownloadFileName(), StandardCharsets.UTF_8) .build(); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString()); // ダウンロード対象のファイルを読込み、レスポンスボディに書込みます try (InputStream in = resourceLoader.getResource(attributes.getTargetFilePath()).getInputStream()) { in.transferTo(response.getOutputStream()); } } }
サンプル全体は file-download を参照してください。