1. ホーム
  2. tomcat

[解決済み] Java Web アプリケーションでアプリケーションサーバーの外側から静的データを提供する最も簡単な方法

2022-06-13 15:04:34

質問

Tomcat 上で動作する Java Web アプリケーションを持っています。私は、Web UI とアプリケーションによって生成される PDF ファイルの両方に表示される静的画像をロードしたいと思っています。また、新しい画像は Web UI 経由でアップロードすることによって追加および保存されます。

静的データをWebコンテナ内に格納することでこれを行うのは問題ありませんが、Webコンテナの外からそれらを格納およびロードすることは頭痛の種です。

この際、静的データを提供するためにApacheなどの別のWebサーバーを使うのは避けたいですね。また、画像をバイナリでデータベースに保存するというアイデアも好きではありません。

画像ディレクトリを Web コンテナ外のディレクトリを指すシンボリック リンクにするというような提案をいくつか見ましたが、このアプローチは Windows と *nix 環境の両方で動作するのでしょうか?

画像配信を処理するためにフィルターやサーブレットを書くことを提案する人もいますが、これらの提案は非常に曖昧で、これを達成する方法に関するより詳細な情報へのポインタなしにハイレベルなものでした。

どのように解決するのですか?

イメージ ディレクトリを Web コンテナの外のディレクトリを指すシンボリック リンクにするなどの提案を見ましたが、この方法は Windows と *nix 環境の両方で機能するのでしょうか?

nix ファイルシステムのパス規則を遵守する場合 (すなわち、次のようにスラッシュのみを使用する) /path/to/files のようにスラッシュだけを使う)、Windows でも醜い File.separator といった文字列の連結をする必要がない。しかし、このコマンドを実行したのと同じ作業ディスク上でのみスキャンされます。したがって、例えばTomcatが以下の場所にインストールされている場合 C: にインストールされている場合 /path/to/files が実際に指すのは C:\path\to\files .

ファイルがすべてウェブアプリケーションの外部にあり、Tomcat の DefaultServlet で処理したい場合、Tomcat で基本的に必要なことは、以下の Context 要素を /conf/server.xml 内部 <Host> タグの中にあります。

<Context docBase="/path/to/files" path="/files" />

こうすることで、これらは http://example.com/files/... . JBoss EAP 6.x 以前の Tomcat ベースのサーバでは、基本的に同じ方法です。 ここで . GlassFish/Payaraの設定例があります。 はこちら と WildFly の設定例があります。 はこちら .

ファイルの読み書きを自分で制御したい場合は、ファイル名を指定するための Servlet を作成する必要があります。 InputStream を取得します。 FileInputStream に書き出し、それを OutputStreamHttpServletResponse .

レスポンス上では Content-Type ヘッダを設定し、クライアントがどのアプリケーションが提供されたファイルに関連づけられるかを知ることができるようにします。そして Content-Length ヘッダを設定し、クライアントがダウンロードの進捗を計算できるようにします(そうしないと、不明になってしまいます)。そして Content-Disposition ヘッダを attachment が必要な場合は として保存 ダイアログが必要な場合、そうでなければ、クライアントはそれをインラインで表示しようとします。最後に、ファイルの内容をレスポンス出力ストリームに書き込むだけです。

このようなサーブレットの基本的な例です。

@WebServlet("/files/*")
public class FileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String filename = URLDecoder.decode(request.getPathInfo().substring(1), "UTF-8");
        File file = new File("/path/to/files", filename);
        response.setHeader("Content-Type", getServletContext().getMimeType(filename));
        response.setHeader("Content-Length", String.valueOf(file.length()));
        response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
        Files.copy(file.toPath(), response.getOutputStream());
    }

}

にマップされた場合 url-pattern にマップされた場合、例えば /files/* で呼び出すことができます。 http://example.com/files/image.png . この方法では、リクエストをより細かく制御することができます。 DefaultServlet が行うような、より詳細な制御が可能になります。 if (!file.exists()) file = new File("/path/to/files", "404.gif") など)。を使うことも request.getPathInfo() よりも優先されます。 request.getParameter() の方が SEO 対策になるし、そうでないと IE が正しいファイル名を選択できないからです。 として保存 .

同じロジックをデータベースからファイルを提供するために再利用することができます。単に new FileInputStream()ResultSet#getInputStream() .

これが役立つといいのですが。

こちらもご覧ください。