【Java】ファイルダウンロード

# まえがき
ファイルをバイナリに変換したものをレスポンスにセットすることで、ファイルダウンロードを実現する。やり方は２つ。

① レスポンス(Controllerメソッドの戻り値)にバイナリをセットする。
② レスポンス(メソッドの引数/HttpServletResponse)にバイナリをセットする。

# 参考

① レスポンス(Controllerメソッドの戻り値)にバイナリをセットする。

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

② レスポンス(メソッドの引数/HttpServletResponse)にバイナリをセットする。

https://stackoverflow.com/questions/17933508/how-to-delete-a-file-after-sending-it-in-a-web-app

# ① レスポンス(Controllerメソッドの戻り値)にバイナリをセットする。

## ソースコード
```java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/files")
public class FileController {
    @GetMapping("/downloadFile")
    public ResponseEntity<Resource> downloadFile()
            throws Exception {
        Path path = Path.of("src/main/resources/file/sample_file_1.txt");
        Resource resource = new PathResource(path);
        return ResponseEntity.ok()
                .contentType(getContentType(path))
                .contentLength(resource.contentLength())
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    }

    private MediaType getContentType(Path path) throws IOException {
        try {
            return MediaType.parseMediaType(Files.probeContentType(path));
        } catch (IOException e) {
            return MediaType.APPLICATION_OCTET_STREAM;
        }
    }
}
```

## 補足

### HttpHeaders.CONTENT_DISPOSITIONとは？
・**レスポンスとして返したファイルをどう処理するか**を定めるヘッダー情報。
|ヘッダ内容  |処理内容  |
|---|---|
|inline|WEBページとして表示|
|attachment|ダウンロードする|
|attachment; filename="filename.jpg"|ファイル名指定でダウンロードする|

```java
// WEBページとして表示
ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "inline");
// ダウンロードする
ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment")
// ファイル名指定でダウンロードする
ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
```

### MediaType.APPLICATION_OCTET_STREAMとは？
・MIMEの種類の１つ。
・「ファイルの種類は気にするな！」を意図した表現。
```
return MediaType.APPLICATION_OCTET_STREAM;
```

# ② レスポンス(メソッドの引数/HttpServletResponse)にバイナリをセットする。
・ダウンロード用に作成した一時ファイルを最後に削除する、という処理を差し込みたい場合は①ではなくこの②を使う。
## ソースコード
```java
@Controller
@RequestMapping("/files")
public class SampleController {

    @Autowired
    FastExcel fastExcel;

    private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    @GetMapping("/downloadFile2")
    public void downloadFile2(HttpServletResponse response) throws Exception {
        // テンプレートファイルをコピー → 一時ファイル作成
        Path templateFile = Path.of("src/main/resources/file/templateFile.txt");
        Path temporaryFile = Path.of("src/main/resources/tmp/" + format.format(new Date()) + ".txt");
        Files.copy(templateFile, temporaryFile);
        Resource resource = new PathResource(temporaryFile);
        // レスポンス作成
        response.setContentType(getContentType(temporaryFile).getType());
        response.setContentLengthLong(resource.contentLength());
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"");
        try (
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        ) {
            out.write(Files.readAllBytes(temporaryFile));
        }
        // 一時ファイル削除
        Files.deleteIfExists(temporaryFile);
    }

    private MediaType getContentType(Path path) throws IOException {
        try {
            return MediaType.parseMediaType(Files.probeContentType(path));
        } catch (IOException e) {
            return MediaType.APPLICATION_OCTET_STREAM;
        }
    }
}
```