テキストファイルをダウンロードする（Java, SpringBoot）

### 動作環境
Java 17
Spring boot 3.2.1
Gradle

### まずは小さく動かしてみる
下記はテキストファイルをダウンロードするためだけのSpringBootのプロジェクトです。

https://github.com/yusuke/text-file-download

クローン、ビルド、実行して、`http://localhost:8080/download`にアクセスすれば直書きした文字列が、txtファイルでダウンロードされるのでお試し下さい。

こちらのソースは[『プロになるJava』](https://gihyo.jp/book/2022/978-4-297-12685-8)の著者の一人である[山本裕介さん](https://twitter.com/yusuke)が管理されるLINEオープンチャットにて質問した際に、ご本人から直接ご回答いただいた時のものです。（CC0のため引用）

## 記事の内容
- ローカルのファイルのダウンロード処理を実装
- テキストファイルのダウンロードが失敗
- Content-Disposition「filename」をURLエンコード
- その他：関連知識

## ローカルのファイルをダウンロード処理の実装
いきなり他人まかせですが、きり丸さんの記事から拝借し実装しました。

https://nainaistar.hatenablog.com/entry/2021/10/21/120000

・実装抜粋
```java
// … Controller処理
 Resource resource = new PathResource(path);
 return ResponseEntity.ok()
   .contentType(getContentType(path))
   .header(HttpHeaders.CONTENT_DISPOSITION,
     "attachment; filename=\"" + 
        resource.getFilename() + "\"")
   .body(resource);

}

// MediaTyp取得
private MediaType getContentType(
  Path path // ダウンロード対象ファイルのパス
) throws IOException {
  try {
    return MediaType.parseMediaType(Files.probeContentType(path));
  } catch (IOException e) {
    return MediaType.APPLICATION_OCTET_STREAM;
  }
}
```

記事の通り実装できればjpgやpngなど、テキストファイル以外はダウンロードできます。
（※pdfはブラウザによってはタブで開かれますが、その画面からダウンロードできるので今回はスルーします。）

一方でtxtファイルは「タブ」で開かれかつ文字化けしている状態...

### Content-Disposition で設定する fileName に日本語が含む場合はURLエンコードを行う

##### ■機能を最小限にしたプロジェクトを作成し試行
冒頭のプロジェクトで、テキストファイルのダウンロードを試行してみたところ、アルファベットのみのファイル名はダウンロードでき、**日本語を含むファイル名は失敗**することが判明。

**ファイル名の文字コード**に起因していると推測されました。早速調べ...

https://qiita.com/mikene_koko/items/bf2677c5ce3218d7c2bd
> 日本語でダウンロードさせるにはURLエンコードが必要になってくる


```java
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(filename, StandardCharsets.UTF_8) + "\"")
```
#### そして最終形態

```java
return ResponseEntity.ok()
	.contentType(mediaType)
	.header(
        // Springのhttp系のクラスを利用した形で実装
	    HttpHeaders.CONTENT_DISPOSITION,
	    ContentDisposition.attachment()
	    	.filename(filename, StandardCharsets.UTF_8)
	        .build().toString())
	.body(resource);
```

動確して日本語のテキストファイルもダウンロードできました◎

## その他、実装しながら調べたこと
### ■ Content-Disposition
こちらを参照

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Disposition

今回の関連でいれば、例えば下記
```
Content-Disposition: attachment; filename="filename.jpg"
```
attachment：ダウンロードすべきであることを示す
inline：既定値。ウェブページとして表示することを示す

つまり attachment に `filename` 引数の値を使い、「名前を付けて保存」= **ダウンロードするように設定**できる。

実装を確認。
```java
.header(HttpHeaders.CONTENT_DISPOSITION,
     "attachment; filename=\"" + 
        resource.getFilename() + "\"")
```

なってるじゃないか！たしかに画像ファイルなどはうまくいっている。ということは別の原因ということが分かった

### ■ MediaType
##### Webのデータ送受信を行うHTTPなどで、データの種類を表すコード(Content-Type) に設定する種別のこと
ダウンロードできるファイルとできないファイルは何が違うのか...文字化けもあったので、まず気になったのは
コンテントタイプにセットしている「**MediaType**」、別名 **MIMEタイプ**
> メディア種別 (別名 Multipurpose Internet Mail Extensions または MIME タイプ) は、文書、ファイル、またはバイト列の性質や形式を示します。 MIME タイプは IETF の RFC 6838 で定義され、標準化されています。

> ブラウザーは URL を処理する方法を決定するために、ファイル拡張子ではなく MIME タイプを使用しますので、ウェブサーバーは正しい MIME タイプをレスポンスの Content-Type ヘッダーで送信することが重要です。 

https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_Types

構造は`タイプ/サブタイプ`。
例えば、`image/jpeg`や`image/png`、`application/pdf`、テキストファイルだと`text/plain`。
（またSpringではMINEに準じるクラスとして[MediaTypeクラス](https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/http/MediaType.html)が用意されている。）

MediaType が `text/plain`の場合にはダウンロードがうまくいかないので、ここが原因かと思って沼った。。

### ■ HTTPレスポンス の「body」
※HTTP分からんという人はとりあえずこれ見て

https://wa3.i-3-i.info/word1848.html

> HTTPレスポンスは3つの部品から成り立っています。
> １．HTTPステータスライン（ステータスライン）
> ２．HTTPレスポンスヘッダ（ヘッダ）
> ３．HTTPレスポンスボディ（レスポンスボディ）

HTTPレスポンスの部品で『ファイルの中身』に当たるところです。
`Resource resource = new PathResource(path);`で取得した`resource`を直接 body に設定していました。

冒頭のシンプルなプロジェクトでは、バイト配列で文字列をファイルの中身としていました。
なので`resource`を直接渡すのではなく、下記のように変換しても同様の結果でテキストファイルの中身を渡せます。
```java
.body(new ByteArrayResource(resource.getContentAsByteArray()));
```

※他にも調べましたが、とくに関連があったもののみ備忘録として記載します

## 引用・参考
 - [山本裕介さん：github（テキストファイルダウンロード）](https://github.com/yusuke/text-file-download)
 - [きり丸の技術日記「SpringでAPIからファイルをダウンロードする（Java）」](https://nainaistar.hatenablog.com/entry/2021/10/21/120000)
 - [[Java]HTTPヘッダのfilenameに日本語名ファイルを返す](https://qiita.com/mikene_koko/items/bf2677c5ce3218d7c2bd)
 - [Content-Disposition 公式](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Disposition)
 - [MediaType 公式](https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/http/MediaType.html)
 - [MIME タイプ（IANA メディア種別）](https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_Types)
 - [HTTPレスポンスボディ（「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典）](https://wa3.i-3-i.info/word1848.html)