[Java]画像をアップロードしてBase64化する

## 概要

- Servlet/JSPを使った古き良き技術スタックで画像のアップロードを試します
- multipart/form-dataで送ったファイルをサーバサイドでBase64化するのが今回やりたいこと
- ServletもJSPもど素人です

## コード

### アップロード画面

- 最初に表示するアップロード画面のJSP

```java
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Java Sample</title>
  </header>
  <body>
    <h1>Hello</h1>
    <form action="upload" method="post" enctype="multipart/form-data">
      <input type="hidden" name="action_name" value="upload" />
      <input type="file" name="file" />
      <button>Upload</button>
    </form>
  </body>
</html>
```

- `/upload` に対してmultipart/form-dataで画像を投げます

### 画像の取得処理

- アップロードのリクエストを受け取るコントローラ
    - ベースとなっているサンプルそのままですが `action_name` が`upload` のときは `UploadAction` を呼び出してる感じ
    - 今回はmultipart/form-dataを受け取るので `@MultipartConfig` をつけてるところがポイント

```java
package com.example.controller;

import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.example.action.Action;
import com.example.action.UploadAction;

@WebServlet(urlPatterns = { "/upload" })
@MultipartConfig
public class UploadController extends HttpServlet {

  @Override
  protected void doPost(
    HttpServletRequest request,
    HttpServletResponse response
  )
    throws ServletException, IOException {
    String strActionName = request.getParameter("action_name");
    Action action = getInstance(strActionName);
    String forwardPath = action.execute(request);
    RequestDispatcher rd = request.getRequestDispatcher(forwardPath);

    rd.forward(request, response);
  }
  
  private static Action getInstance(String actionName) {
    switch (actionName) {
      case "upload":
        return new UploadAction();
      default:
        return null;
    }
  }
}
```

- 画像を受け取って処理するAction

```java
package com.example.action;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Base64;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;

public class UploadAction extends Action {

  @Override
  protected String processRequest(HttpServletRequest request) {
    try {
      Part filePart = request.getPart("file");
      String contentType = filePart.getContentType();
      InputStream fileContent = filePart.getInputStream();
      byte[] byteArray = getByteArray(fileContent);
      String base64String = Base64.getEncoder().encodeToString(byteArray);

      System.out.println("filePart: " + filePart);
      System.out.println("contentType: " + contentType);
      System.out.println("base64String: " + base64String);

      // 必要に応じてcontentTypeとbase64化した画像情報をDBに保存などする

      // 表示用にcontentTypeとbase64化した画像を組み合わせる
      String image = "data:" + contentType + ";base64," + base64String;
      request.setAttribute("image", image);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return "image.jsp";
  }

  private static byte[] getByteArray(InputStream is) throws Exception {
    ByteArrayOutputStream b = new ByteArrayOutputStream();
    BufferedOutputStream os = new BufferedOutputStream(b);
    while (true) {
      int i = is.read();
      if (i == -1) break;
      os.write(i);
    }
    os.flush();
    os.close();
    return b.toByteArray();
  }
}
```

- 通常のパラメータは `request.getParameter("xxx")` で取得しますが今回はファイルなので `request.getPart("xxx")` で取得してます
- その後はInputStream → byte → Base64って感じでBase64までたどり着きました
- せっかくなのでJSPにBase64化した値を渡して画面に出すところまで作ってます

### 画像の表示

- 画像を表示するJSP

```java
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<jsp:useBean id="image" scope="request" type="java.lang.String" />
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Java Sample</title>
  </header>
  <body>
    <h1>Hello</h1>
    <img src="<%= image %>" />
  </body>
</html>
```

- imgタグのsrc属性にBase64もセットできるので埋め込んでます
- ここまでできると画像をアップロードするとプレビューする画面が表示されるという一連の流れができました

## まとめ

- Servlet/JSP使ってるとはいえfileのを受け取ったあとの処理は純粋なJavaですね
- Servlet/JSP力が無駄にあがってしまった