はじめに (対象読者・この記事でわかること)

この記事は、JavaEE(Servlet 3.x/JSP 2.x)で「画像アップロード+一覧表示」を実装中に「画像が表示されない」「リストが空」などの壁に突き当たった開発者を対象としています。
記事を読み終えると、以下のことがわかります。

  • アップロードした画像が「サーバー上にちゃんと保存されているか」の確認手順
  • JSP側で<img src="...">と書いても画像が表示されない根本原因と、正しいコンテキストパスの扱い方
  • Partインターフェースでファイル名を取得する際の「Chrome vs 他ブラウザ」差分を吸収するコード
  • 画像リストを「再起動後も残す」「再起動後はクリア」の2パターンで実装する際の注意点

この記事を書いたのは、社内のレガシー環境(JavaEE 7、WildFly 26)で「ドラッグ&ドロップで画像アップロード」機能を追加した際、画像表示まで丸1日つまずんだ経験があるからです。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。

  • JavaEE の基本的なディレクトリ構成(WEB-INF、コンテキストルートの概念)
  • Servlet 3.x で提供される @MultipartConfig アノテーション
  • JSP での JSTL(<c:forEach>)と EL(${})の記法

JavaEE でのファイルアップロードの仕組みと「見えない」理由

JavaEE 標準だけでファイルアップロードを実装する場合、HttpServletRequest#getPart を使います。
@MultipartConfig をクラスに付与すると、サーバーがマルチパートリクエストを自動でパースしてくれます。

Java
@WebServlet("/upload") @MultipartConfig(location = "/tmp", maxFileSize = 5 * 1024 * 1024) public class UploadServlet extends HttpServlet { ... }

ここで「画像が表示されない」根本原因の90%は、以下の2つに集約されます。

  1. アップロード先ディレクトリが「ウェブコンテンツとして公開されていない」
  2. JSP側で誤ったパスを<img src>に指定している

JavaEE のルール上、WEB-INF以下のファイルは直接ブラウザからアクセスできません
そのため、/WEB-INF/uploaded/以下に画像を置いたままでは、ブラウザが画像をリクエストしても404が返ります。

画像アップロード&一覧表示を0から実装してみる

ステップ1:公開可能なディレクトリを用意する

アプリケーションのルート(WebContentsrc/main/webapp)にuploadsフォルダを作ります。
これはhttp://localhost:8080/<context-root>/uploads/...で直接アクセス可能な位置です。

Bash
src/main/webapp ├─ uploads ← ここに画像を保存 ├─ WEB-INF └─ index.jsp

アップロード先の絶対パスを取得するには、ServletContext#getRealPathを使います。

Java
String uploadDir = getServletContext().getRealPath("/uploads"); File repo = new File(uploadDir); if (!repo.exists()) repo.mkdirs();

ステップ2:Part → ファイル保存までの一連処理

ブラウザ差分を吸収するため、ファイル名抽出メソッドを切り出しておきます。

Java
private String getFileName(Part part) { String cd = part.getHeader("Content-Disposition"); if (cd == null) return null; for (String seg : cd.split(";")) { if (seg.trim().startsWith("filename")) { return seg.substring(seg.indexOf('=') + 1) .replace("\"", ""); } } return null; }

アップロード処理本体:

Java
Part filePart = request.getPart("file"); String fileName = getFileName(filePart); if (fileName == null || fileName.isEmpty()) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "ファイル名が取得できません"); return; } String ext = fileName.substring(fileName.lastIndexOf('.')); String savedName = UUID.randomUUID() + ext; File saveFile = new File(uploadDir, savedName); filePart.write(saveFile.getAbsolutePath()); // 一覧表示用にセッションに保存(単純な例) @SuppressWarnings("unchecked") List<String> list = (List<String>) request.getSession() .getAttribute("images"); if (list == null) { list = new ArrayList<>(); request.getSession().setAttribute("images", list); } list.add("uploads/" + savedName); response.sendRedirect("list.jsp");

ステップ3:JSP側で一覧表示

list.jspでは、保存したパスをそのまま<img>に埋め込みます。

Jsp
<c:forEach var="path" items="${sessionScope.images}"> <div> <img src="${pageContext.request.contextPath}/${path}" width="200"> <p>${path}</p> </div> </c:forEach>

ポイントは${pageContext.request.contextPath}先頭に付けること。
これを忘れると、コンテキストルートが省略され「404 Not Found」が返されます。

ハマった点・エラー解決

現象A:「Partがnull」と例外が出る

formタグにenctype="multipart/form-data"が指定されていないと、request.getPartはnullを返します。
必ず以下を確認しましょう。

Html
<form action="upload" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <button>アップロード</button> </form>

現象B:「ファイル名が日本語だと文字化けする」

WildFly/Tomcatともに、URIEncoding設定が必要です。

Tomcatの例(conf/server.xml):

Xml
<Connector port="8080" URIEncoding="UTF-8" />

現象C:「再起動したら画像リストが消える」

サンプルではセッションにパスを保持しているため、アプリケーション再起動でリストが空になります。
永続化が必要な場合は、DBや外部ファイル(JSON/CSV)に保存するか、ServletContextListenerでList<String>ServletContextに保持する方法もあります。

解決策まとめ

  1. 画像保存先はWEB-INF外に置く
  2. <img src>には常に${pageContext.request.contextPath}を付与
  3. ファイル名抽出はContent-Dispositionを自分でパースする
  4. 日本語ファイル名はサーバー側のエンコーディング設定を確認

まとめ

本記事では、JavaEE(Servlet/JSP)だけで「画像アップロード&一覧表示」を実装する際の落とし穴と、正しいディレクトリ配置・パス指定方法を解説しました。

  • アップロード先はgetServletContext().getRealPath("/uploads")で取得し、公開ディレクトリに保存
  • JSP側で画像を表示する際は${pageContext.request.contextPath}を絶対に付ける
  • Partからのファイル名取得はブラウザ差分を吸収したヘルパーメソッドを用意
  • 永続化が必要ならセッションではなくファイルorDBに保存

この記事を通して、レガシーながらもJavaEE標準だけで手軽に画像アップロード機能を追加できることが伝われば幸いです。
次回は「ドラッグ&ドロップ対応+Progress Bar実装」について、Vue.jsとAjaxを使った手法を紹介する予定です。

参考資料