Spring MVC ファイルダウンロードとリダイレクトを両立したい

Spring MVCで楽をしていると、ファイルダウンロードとリダイレクトの組み合わせに悩みました。スマートではないかも知れないのですが実装方法が判りましたので記載しておきます。

前提1:ファイルダウンロードの書き方

そもそもファイルの内容をレスポンスとして返したい場合は以下のように書くと楽に書けました。

@RequestMapping(value = "downloadFile/{fileId}", method = RequestMethod.GET)
public ResponseEntity<byte[]> downloadFile(Model model, MailEditForm mailEditForm,@PathVariable("fileId") int fileId
		,@AuthenticationPrincipal LoginUserDetails userDetails) {
	HttpHeaders h = new HttpHeaders();
	return new ResponseEntity<>(bytes, h, HttpStatus.OK);
}

前提2:リダイレクトの書き方

他にもあるようですが、リダイレクトはこうやって書くようです。
Spring Framework Reference Documentation#mvc-redirecting

@PostMapping("/files/{path}")
public String upload(...) {
    // ...
    return "redirect:files/{path}";
}

他にも”RequestMappingを設定するメソッドの返り値”の型を変えることで色々楽ができます。
Spring Framework Reference Documentation#mvc-ann-return-types

両立する場合の書き方

しかし、一つのメソッドで同時にやろうとすると、返り値の型が合わないので実装できません。その場合はとりあえず以下のように実装すると動きました。

ポイント
・返り値の型はStringにする。
・ファイルの内容はHttpServletResponseに直接渡す。
・通常時はreturnをnullにする。
・リダイレクト時はリダイレクトの文字列を。

@RequestMapping(value = "downloadFile/{fileId}", method = RequestMethod.GET)
public String downloadFile(Model model, MailEditForm mailEditForm,@PathVariable("fileId") int fileId
		,@AuthenticationPrincipal LoginUserDetails userDetails, HttpServletResponse httpServletResponse) {
	
	// ダウンロードできないならリダイレクト
	if(ダウンロードできるか()){
		return "redirect:/error/404/";
	}
	
	// ヘッダーは以下のメソッドで設定する
	httpServletResponse.setHeader("Content-Length",""+file.length()+"");
	
	// ファイルの内容をhttpServletResponseに渡す
	try {
		httpServletResponse.setContentLength(bytes.length);
		httpServletResponse.getOutputStream().write(bytes, 0, bytes.length);
		
	} catch (IOException e) {
		logger.error("ファイルの内容をhttpServletResponseに書き込む際に失敗しました。", e);
	}
	
	return null;
}

おわり

ちなみにHttpServletResponseにredirect用のメソッドがあるみたいなので、返り値はResponseEntityのままでも、このメソッドを使用すればリダイレクトできそうです。

たぶん、HttpServletResponseあたりはSpringではなく、Javaの知識ですよね。
普段Springが隠して便利にしてくれてるので困らないのですが、何かあると、ベースの知識が必要になりますね。当たり前ですが、そこらも学んでおかないと。

以上。