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


131

TomcatでJava Webアプリケーションを実行しています。Web UIとアプリケーションによって生成されたPDFファイルの両方に表示される静的画像をロードしたいと思います。また、新しい画像が追加され、Web UIを介してアップロードすることで保存されます。

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

この時点で静的データを提供するために、Apacheのような別のWebサーバーを使用したくない。また、画像をバイナリでデータベースに保存するという考えも好きではありません。

画像ディレクトリをWebコンテナの外部のディレクトリを指すシンボリックリンクにするなどの提案を見てきましたが、このアプローチはWindows環境と* nix環境の両方で機能しますか?

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

回答:


161

画像ディレクトリをWebコンテナの外部のディレクトリを指すシンボリックリンクにするなどの提案を見てきましたが、このアプローチはWindows環境と* nix環境の両方で機能しますか?

* nixファイルシステムのパス規則を順守する場合(つまり、のようにスラッシュのみを使用する場合/path/to/files)は、醜いFile.separator文字列連結をいじる必要なく、Windowsでも機能します。ただし、このコマンドが呼び出されたときと同じ作業ディスクでのみスキャンされます。たとえば、TomcatがインストールされているC:場合、/path/to/filesは実際にはを指しC:\path\to\filesます。

ファイルがすべてwebappの外部にあり、TomcatでDefaultServletそれらを処理したい場合、基本的にTomcatで行う必要があるのは、次のContext要素をタグの/conf/server.xml内側に追加することだけです<Host>

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

このようにして、からアクセスできますhttp://example.com/files/...。GlassFish / Payaraの設定例はこちら、WildFlyの設定例はこちらにあります

あなたがファイルを自分で読出し/書込みを制御することがしたい場合は、作成する必要がありServlet、基本的にはなっている。このためにInputStream例えばの風味でファイルのをFileInputStreamし、それを書き込みOutputStreamしますHttpServletResponse

応答で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")。また、SEO対応であり、IEが[名前を付けて保存]中に正しいファイル名を選択しないため、request.getPathInfo()上記を使用するrequest.getParameter()ことをお勧めします

データベースからファイルを提供する場合と同じロジックを再利用できます。単にに置き換えnew FileInputStream()てくださいResultSet#getInputStream()

お役に立てれば。

以下も参照してください。


1
@SalutonMondo:最小の努力で道。
BalusC、2015

@BalusC、私はこれを試しました:<Context docBase="/path/to/images" path="/images" />Windowsでは、webappsフォルダーに対する相対パスを取得します:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV

Windowsの場合:<Context docBase="C:\tmp\" path="/images" />
ACV

そして、実行HTTP Status 404 - /images/時に取得:http://localhost:8080/images/、しかし、作業中の特定のファイルtmphttp://localhost:8080/images/Tulips.jpgOK
ACV

この方法では、フォルダー内に画像を配置すると、サーバーを再起動しない限り、画像へのリンクが応答404を返します。これには何らかの理由がありますか?
Mateus Viccari 16

9

これを行うには、固定パス(例:/ var / images、またはc:\ images)にイメージを配置し、アプリケーション設定に設定を追加し(この例では、Settings.classで表されます)、それらをロードします。そのように、HttpServletあなたのものの:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
FileInputStream fis = new FileInputStream(filename);

int b = 0;
while ((b = fis.read()) != -1) {
        response.getOutputStream().write(b);
}

または、画像を操作したい場合:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
File imageFile = new File(filename);
BufferedImage image = ImageIO.read(imageFile);
ImageIO.write(image, "image/png", response.getOutputStream());

次に、htmlコードは <img src="imageServlet?imageName=myimage.png" />

もちろん、たとえばファイル拡張子に基づいた「image / jpeg」など、さまざまなコンテンツタイプの提供を検討する必要があります。また、いくつかのキャッシングを提供する必要があります。

さらに、このサーブレットを使用して、幅と高さのパラメータを引数として提供しimage.getScaledInstance(w, h, Image.SCALE_SMOOTH、パフォーマンスを考慮して)を使用することで、画像の高品質の再スケーリングを行うことができます。


2
これにはJava 2D APIは本当に必要ありません。不必要にオーバーヘッドが増えるだけです。InputStreamを読み取り、OutputStreamに書き込むだけです。
BalusC 2009年

1
うん、私はリスケーリングやその他の操作のアイデアから応答を始めましたが、結局それを簡素化することになりました。
Bozho

7

server.xmlに追加します。

 <Context docBase="c:/dirtoshare" path="/dir" />

web.xmlのdirファイルリストパラメータを有効にします。

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>

web.xmlの変更により、選択ボックスに送信するためにファイルのリストを取得できますか?
Tomasz Waszczyk 2016年

1
この変更は、アプリケーションではなくtomcatのweb.xmlで行われます
vsingh

これをありがとう!web.xmlでdirファイルリストパラメータを有効にする必要がありますか?
dariru 2018

6

要件:WEBROOTディレクトリの外部またはローカルディスクから静的リソース(画像/ビデオなど)にアクセスする

手順1:
tomcatサーバーのwebappsの下にフォルダーを作成します。フォルダー名はmyprojとします。

ステップ2:
myprojの下に、この下にWEB-INFフォルダーを作成し、単純なweb.xmlを作成します

web.xmlの下のコード

<web-app>
</web-app>

上記の2つのステップのディレクトリ構造

c:\programfile\apachesoftwarefoundation\tomcat\...\webapps
                                                            |
                                                            |---myproj
                                                            |   |
                                                            |   |---WEB-INF
                                                                |   |
                                                                    |---web.xml

ステップ3:
次の場所にmyproj.xmlという名前のxmlファイルを作成します

c:\programfile\apachesoftwarefoundation\tomcat\conf\catalina\localhost

myproj.xmlのコード:

<Context path="/myproj/images" docBase="e:/myproj/" crossContext="false" debug="0" reloadable="true" privileged="true" /> 

ステップ4:
4 A)ハードディスクのEドライブにmyprojという名前のフォルダーを作成し、新しいフォルダーを作成します

名前の付いたフォルダと画像フォルダにいくつかの画像を配置 (e:myproj\images\)

myfoto.jpgが下にあるとしましょう e:\myproj\images\myfoto.jpg

4 B)次に、WEB-INFという名前のフォルダーをe:\myproj\WEB-INF作成し、WEB-INFフォルダーにweb.xmlを作成します

web.xmlのコード

<web-app>
</web-app>

ステップ5:
index.htmlという名前の.htmlドキュメントを作成し、e:\ myprojの下に配置します

index.htmlの下のコードMyprojへようこそ

上記の手順4と手順5のディレクトリ構造は次のとおりです。

E:\myproj
    |--index.html
    |
    |--images
    |     |----myfoto.jpg
    |
    |--WEB-INF
    |     |--web.xml

ステップ6:
次にApache Tomcatサーバーを起動します

手順7:
ブラウザーを開き、次のようにURLを入力します

http://localhost:8080/myproj    

次に、index.htmlで提供されるコンテンツを表示します

ステップ8:
ローカルのハードディスク(webrootの外)にあるイメージにアクセスするには

http://localhost:8080/myproj/images/myfoto.jpg

動的な値に対して同じことをする方法を教えてください。つまり、data(xml)をローカルディレクトリに書き込んだり、jspページで読み取ったりしたいのです。上記の手順を使用してアクセスできるように、ディレクトリで管理されているサーバーに書き込む方法はありますか?
Sudip7 2014年

index.htmlファイルは適切に実行できますが、Webブラウザーに画像が表示されません
rogerwar '29年

私の間違いの投稿は正常に機能しています。E:/ myprojの最後に/を付けるのを忘れたので、これをE:/ myproj /に変更し、正常に機能します。ありがとう@sbabamca
rogerwar

こんにちは、投稿ありがとうございます。ここで、インターフェイスを介してその特定のディレクトリにファイルをアップロードしたいと思います。POSTメソッドを有効にしたいと思います。誰かが私を助けてくれますか?
vicky 2015年

6

これは私の職場の話です。
-Struts 1とTomcat 7.xを使用して、複数の画像とドキュメントファイルをアップロードしようとしています。
-アップロードされたファイルをファイルシステム、ファイル名、およびデータベースレコードへのフルパスに書き込みます。-Webアプリディレクトリの外にあるファイルフォルダー
分離しようとします。(*)

以下のソリューションは非常にシンプルで、要件(*)に効果的です。

META-INF/context.xml次の内容のファイルfile:(例、私のアプリケーションはで実行されhttp://localhost:8080/ABC、私のアプリケーション/プロジェクトはという名前ですABC)。(これもファイルの全内容ですcontext.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/ABC" aliases="/images=D:\images,/docs=D:\docs"/>

(Tomcatバージョン7以降で動作)

結果: 2つのエイリアスが作成されました。たとえば、画像を次の場所に保存しD:\images\foo.jpg 、リンクから表示するか、画像タグを使用します。

<img src="http://localhost:8080/ABC/images/foo.jsp" alt="Foo" height="142" width="142">

または

<img src="/images/foo.jsp" alt="Foo" height="142" width="142">

(私はNetbeans 7.xを使用していますが、Netbeansは自動作成ファイルのようですWEB-INF\context.xml



0

受け入れられた回答で問題を解決できない人がいる場合は、以下の考慮事項に注意してください。

  1. 属性で言及localhost:<port>する必要はありません<img> src
  2. Eclipseはcontext docBaseローカルserver.xmlファイル内に独自にエントリを作成するため、Eclipseの外部でこのプロジェクトを実行していることを確認してください。

0

ファイルのInputStreamを読み取り、ServletOutputStreamバイナリデータをクライアントに送信するために書き込みます。

@WebServlet("/files/URLStream")
public class URLStream extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public URLStream() {
        super();
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        File source = new File("D:\\SVN_Commit.PNG");
        long start = System.nanoTime();

        InputStream image = new FileInputStream(source);

        /*String fileID = request.getParameter("id");
        System.out.println("Requested File ID : "+fileID);
        // Mongo DB GridFS - https://stackoverflow.com/a/33544285/5081877
        image = outputImageFile.getInputStream();*/

        if( image != null ) {
            BufferedInputStream bin = null;
            BufferedOutputStream bout = null;
            ServletOutputStream sos = response.getOutputStream();
            try {
                bin = new BufferedInputStream( image );
                bout = new BufferedOutputStream( sos );
                int ch =0; ;
                while((ch=bin.read())!=-1) {
                    bout.write(ch);
                }
            } finally {
                bin.close();
                image.close();
                bout.close();
                sos.close();
            }

        } else {
            PrintWriter writer = response.getWriter();
            writer.append("Something went wrong with your request.");
            System.out.println("Image not available.");
        }
        System.out.println("Time taken by Stream Copy = "+(System.nanoTime()-start));
    }
}

src属性に直接URLを結果します。

<img src='http://172.0.0.1:8080/ServletApp/files/URLStream?id=5a575be200c117cc2500003b' alt="mongodb File"/>
<img src='http://172.0.0.1:8080/ServletApp/files/URLStream' alt="local file"/>

<video controls="controls" src="http://172.0.0.1:8080/ServletApp/files/URLStream"></video>

0

JAX-RS(例:RESTEasy)で作業したい場合は、次を試してください。

@Path("/pic")
public Response get(@QueryParam("url") final String url) {
    String picUrl = URLDecoder.decode(url, "UTF-8");

    return Response.ok(sendPicAsStream(picUrl))
            .header(HttpHeaders.CONTENT_TYPE, "image/jpg")
            .build();
}

private StreamingOutput sendPicAsStream(String picUrl) {
    return output -> {
        try (InputStream is = (new URL(picUrl)).openStream()) {
            ByteStreams.copy(is, output);
        }
    };
}

使用javax.ws.rs.core.Responseしてcom.google.common.io.ByteStreams


-1

私はそれをさらに簡単にしました。問題:CSSファイルにimgフォルダーへのURLリンクがありました。404を取得します。

存在しないhttp:// tomcatfolder:port / img / blablah.pngという URLを見ました。しかし、それは本当にTomcatのROOTアプリを指し示しています。

だから私はちょうど私のwebappからそのROOTアプリにimgフォルダーをコピーしました。動作します!

もちろんプロダクションにはお勧めしませんが、これは内部ツール開発アプリ用です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.