【Struts2】ファイルダウンロード機能の作成

# 概要
Struts2でファイルをダウンロードするAPIを作成しました。（厳密には全部Struts2ではない。。。）

ファイル情報を取得するクラスからリターンされたらダウンロードできる仕組みです。
今の現場でここしかStruts2を触ってきていませんが、せっかくなので備忘録として残します。

流石に現場のソースを丸々書けないので、今回は機能として主要な部分だけを記載します。
記載されていることだけでは動かないので悪しからず。。。

Struts2でどうやってファイルをダウンロードすればいいんだと詰まっている人の参考にはなるかなと思います！

##### この記事でわかること
・Streamを使ってレスポンスでファイルをダウンロードさせる

##### 逆にこの記事ではわからないこと
・Struts2の概要
・ファイル情報の作成の仕方

##### 前提
・ファイル情報がある（モデルクラスが存在している）


# ファイル情報（モデルクラス）
今回、ファイル情報が格納されるクラスは以下のようなものを例として使用します。
最低限のシンプルなクラスです。

```Java:FileDataModel.java
public class FileDataModel {
    private String filename;
    private String contentType;
    private Long contentLength;
    private byte[] binaryData;
    
    //以下にゲッターとセッターを用意


}
```
これからこのクラスを使用してファイル情報を扱いますが直接ファイル名などを文字列で使用しても動きます。
ですがこのようなクラスがあればシステム的に便利ですよね

オブジェクト指向的な考え方になるのかな。。。？


# ファイルを取得してリターンする
先にファイルを取得してリターンするクラスを書いてしまいます。
こんな感じ。

```Java:GetFileData.java
pakage sample;

public class GetFileData {
    private String filename;
    private String encodedFilename;
    private Mediatype contentType;
    private Long contentLength;
    private InputStream;
    
    public String execute() throws Exception {
        try {
            //ファイルの情報を取得
            FileDataModel fileData = getFileData();

            //バイナリデータからinputStreamを作成します
            byte[] source = fileData.getBinaryData();
            ByteArrayInputStream bis = new ByteArrayInputStream(source);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] temp = new byte[10240];
            int size = 0;
            while ((size = bis.read(temp)) > 0) {
                bos.write(temp, 0, size);
            }
            inputStream = new ByteArrayInputStream(bos.toByteArray());
            bos.close();
            bis.close();

            //ファイル名を取得してエンコードします
            String value = fileData.getFileName();
            fileName = java.net.URLEncoder.encode(value, "UTF-8");
            encodedFileName = Java.net.URLEncoder.encode(value, "UTF-8");

            //contentLengthを取得します
            contentLength = fileData.getContentLength();
    
            //Content-Typeを取得します。
            Path path = Path.of("tmp", fileName);
            contentType = MediaType.parseMediaType(Files.probeContentType(path));
                
            return "success"
        } catch (Exception e){
            throw e;
        }
    }
    
    /*
     *ファイルデータを取得するメソッドです
     */
    private FileDataModel getFileData() {
        //今回は割愛します。
    }

    //以下にゲッターを用意
}
```

注釈にも書いていますが、ファイルデータのクラスから定数にそれぞれ値を作成してゲッターで返す準備をしています。
ちなみにファイルデータのクラスを作成していない場合は

```diff_java:バイナリ形式のファイル取得方法
- byte[] source = fileData.getBinaryData();
+ Path file = Paths.get("ファイルパス");
+ byte[] source = Files.readAllBytes(file);
```
```diff_java:ファイルのcontent-length取得方法
- contentLength = fileData.getContentLength();
+ contentLength = file.length();
```
```diff_java:ファイルのContent-Type取得方法
- Path path = Path.of("tmp", fileName);
+ Path path = Path.of("tmp", "ファイル名");
contentType = MediaType.parseMediaType(Files.probeContentType(path));
```

こんな感じで変えてあげればすぐに試せます。

# struts.xmlの作成
前置きが長くなりましたが本題です。
ファイルをダウンロードさせるxmlを作成します。

ミソはresultタグのtype要素に”stream”を指定して、paramを書いていくことです。
```xml:struts.xml
<action name="getFile" class="sample.GetFileData" method="execute">
    <result name="success" type="stream">
        <param name="inputStream">inputStream</param>
        <param name="contentDisposition">attacchment;filename=${fileName};fileName*=utf-8''${encodedFileName}</param>
        <param name="contentType">${contentType}</param>
        <param name="contentLength">${contentLength}</param>
    </result>
</action>
```

処理の流れとしては、"getFile"というアクションが実行されると、GetFileDataクラスのexecuteメソッドが呼び出されます。
GetFileData.execute()の戻り値が`success`ならばファイルがダウンロードされる流れになります。
肝心のダウンロードされる処理ですが、GetFileFlieクラスでinputStreamを作成しました。
ここで作成した上で、struts.xmlに`<param name="inputStream">inputStream</param>`を書いてあげるとファイルがダウンロードされる仕組み？（ここの理解が怪しい。。。　contentDispotionも関係してるっぽい？　誰か教えてください:bow_tone1:）

クラスで作成した定数は`${}`で囲ってあげることでヘッダーにそのまま設定が可能です。
※${inputStream}としているとファイルをうまく取得できない。。。公式ドキュメントにも書いてあるのに原因不明。。。

ちなみに参考にしたものはこちらです。

https://struts.apache.org/core-developers/stream-result

https://tomy2002.hatenablog.com/entry/2016/07/04/010454

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


# まとめ
なんか記事が怪しい感じで締まりましたが。。。

とりあえず動くものはできたけど、全部理解できないままだった。。。特にStream関連の処理をわかってない感。。。
ファイル操作って難しいです。。。

ファイルをダウンロードしたかったらstreamを使えってことです！！