Java/JSP イメージのアップロード。これらの画像ファイルはどこに保存すればよいでしょうか?
-
23-08-2019 - |
質問
ユーザーが画像をアップロードできる簡単なアプリケーションを作成しています。アップロード後、ユーザーはタグを付けたり、削除したりできます。
ファイルをアップロードし、ファイルがアップロードされた後に保存する方法を見つけました。画像が保存されているグローバルパスを追跡しています。データベースには、ファイル名、タグなどの画像に関するメタデータが保存されます。
私は Java/JSP (特に Stripes フレームワークですが、私の問題は一般的なものです) を使用しています。
私の質問は、これらの画像ファイルをアップロードした後、どこに保存すればよいですか?
現在、Tomcat サーバーに 2 つの Web アプリケーションをデプロイしています。1 つはメインの Web アプリケーションで、もう 1 つは画像をアップロードする場所です。
しかし、Tomcat を再デプロイ/再起動するまで、メイン アプリケーションでアップロードされたイメージを表示できないため、これは機能しません。
Tomcat は新しくアップロードされた画像を自動的に選択しないようです。
誰かが解決策を持っていますか?
これは単純なプロジェクトなので、データベースに保存したり、画像に Apache を使用したりするつもりはありません。この小さなプロジェクトにとっては、すべてが複雑すぎます。
ありがとう。
解決
確かにデータベースに画像を保存していないが、あなたは、データベース内の画像のパスを保存したいと思うでしょう。これは、あなたがちょうど約どこでも画像を保存することができます。
は、2つのTomcatアプリケーションを使用しているので、あなたの最善の策は、いずれかのアプリの外に画像を保存し、バックの代わりにTomcatをさせるのユーザーに画像をストリームファイルを管理することであってもよいです。そうでなければ、私はあなたが2つのWebアプリケーションでこれをやろうとしている理由を尋ねます。
他のヒント
ただし、Web-appディレクトリ内のアップロードした画像を保存することは行うには賢明なことではない、とあなたはそれを知っています。
ところで、あなたはこののstackoverflowイメージを保存する場所を最近議論し、のスレッド。それは確かにあなたがやっていることにもっと自信を与えるだろう、あなたの問題を解決しない場合があります。
私はこれをさまざまな方法で解決しました。
まず、移植性のない方法ですが、Glassfish (Tomcat も同様だと思います) を使用すると、外部ディレクトリを Web アプリケーション階層にマップできます。これは非常にうまく機能し、まさにあなたが望んでいることを実行します。これにより、Web アプリから離れた外部ディレクトリに画像を保存しながら、画像を提供できます。
ただし、この手法は移植可能ではありません。
これを移植可能に行う方法は、フィルターを作成することです。
フィルターはわかりやすい場所、たとえば「/images」に配置します。
フィルターの機能は次のとおりです。
Web アプリ内の特別なディレクトリ内の画像 (または静的リソースで動作するもの) をチェックします。この例では、URL「/webapp/images」を使用します。
ファイルが存在しない場合は、外部の場所から Web アプリ内の適切な場所にファイルがコピーされます。したがって、要求 URL が「/images/banner.gif」であるとします。そして、ファイルはディスクの「/home/app/images」に保存されます。したがって、ソースファイルは「/home/app/images/banner.gif」です。次に、それを Web アプリ ツリー内の目的の場所にコピーします。これには「ServletContext.getRealPath」を使用します。したがって、宛先は「ServletContext.get RealPath("/webapp/images/banner.gif") になります。ソースを宛先にコピーするだけです。
- ファイルがすでに存在する場合、または現在存在する場合は、/webapp/images/banner.gif にある実際の画像に転送するだけです。
事実上、Web アプリのデプロイメント ツリー内にファイル キャッシュが存在することになります。欠点は、キャッシュであるため、維持する必要があることです (つまり、オリジナルがキャッシュよりも新しいかどうかを確認する必要があります。ソースが削除されている場合は必ず削除する必要があります。など)。また、リソースが複製されるため、画像は最終的に 2 倍のディスク容量を消費することになります。最後に、起動時の初期コピーのコストがあります。
ただし、これは機能し、独自のコードを使用して静的リソースを提供する必要がなくなります。(これは 3 番目の解決策です。フィルター/サーブレットをマップして URL をインターセプトし、それを自分でストリーミングするだけです。)
Tomcat 内の構造を (存在すると仮定して) 調べて、マッピングを行います。Glassfish にそれが存在することは知っています。(Glassfish の代替docroot を Google で調べて、その仕組みを確認してください。)
私は新しいメインアプリケーションwarファイルを再デプロイする場合にはアップロードされた画像を書いオーバーを避けるために2つのWebアプリケーションを使用していた。
しかし、あなたは私が彼らに外tomcatディレクトリを保つことができますねサーブレットか何かを介してそれらをストリーミングするしか他に選択肢がない言及してます。
私はこのストリーミングサーブレットを書く避けたかったです。ストリーミングサーブレットを書きながら、(適切なコンテンツタイプ、404、などのように)すべての混乱に対処するためにはあまりにも小さなプロジェクトます。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Image streaming Servlet.
*/
public class ImageDisplayServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public ImageDisplayServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String relativePath = trimToEmpty(request.getPathInfo());
// Make sure no one try to screw with us.
// This is important as user can literally access any file if we are not careful
if(isXSSAttack(relativePath) == false) {
String pathToFile = this.getServletContext().getRealPath(request.getPathInfo());
File file = new File(pathToFile);
System.out.println("Looking for file " + file.getAbsolutePath());
// show a 404 page
if(!file.exists() || !file.isFile()) {
httpError(404, response);
} else {
try {
streamImageFile(file, response);
} catch(Exception e) {
// Tell the user there was some internal server error.\
// 500 - Internal server error.
httpError(500, response);
e.printStackTrace();
}
}
} else {
// what to do if i think it is a XSS attack ?!?
}
}
private void streamImageFile(File file, HttpServletResponse response) {
// find the right MIME type and set it as content type
response.setContentType(getContentType(file));
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
response.setContentLength((int) file.length());
// Use Buffered Stream for reading/writing.
bis = new BufferedInputStream(new FileInputStream(file));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[(int) file.length()];
int bytesRead;
// Simple read/write loop.
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
// To late to do anything about it now, we may have already sent some data to user.
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
// To late to do anything about it now, we may have already sent some data to user.
}
}
}
}
private String getContentType(File file) {
if(file.getName().length() > 0) {
String[] parts = file.getName().split("\\.");
if(parts.length > 0) {
// only last part interests me
String extention = parts[parts.length - 1];
if(extention.equalsIgnoreCase("jpg")) {
return "image/jpg";
} else if(extention.equalsIgnoreCase("gif")) {
return "image/gif";
} else if(extention.equalsIgnoreCase("png")) {
return "image/png";
}
}
}
throw new RuntimeException("Can not find content type for the file " + file.getAbsolutePath());
}
private String trimToEmpty(String pathInfo) {
if(pathInfo == null) {
return "";
} else {
return pathInfo.trim();
}
}
private void httpError(int statusCode, HttpServletResponse response) {
try {
response.setStatus(statusCode);
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.append("<html><body><h1>Error Code: " + statusCode + "</h1><body></html>");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean isXSSAttack(String path) {
boolean xss = false;
// Split on the bases of know file separator
String[] parts = path.split("/|\\\\");
// Now verify that no part contains anything harmful
for(String part : parts) {
// No double dots ..
// No colons :
// No semicolons ;
if(part.trim().contains("..") || part.trim().contains(":") || part.trim().contains(";")) {
// Fire in the hole!
xss = true;
break;
}
}
return xss;
}
/**
* @see HttpServlet#doPost(Ht/promotions/some.jpgtpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
OK、これは画像をストリーミングできるサーブレットです。
制限事項と既知の問題のリストは次のとおりです。
- XSS の脆弱性がある可能性があるので使用には注意してください
- 本番環境に対応していないため、参考として使用してください
- 画像は Web アプリケーション ディレクトリに存在する必要があります。簡単に変更できますが、私は怠け者です(プロジェクトが小さすぎるため、変更する価値はありません)
- jpg、gif、または png ファイルのみをストリーミングします。
使用法:
このイメージという Web アプリケーションを別のアプリケーションとしてデプロイするとします。
http://www.example.com/images/promotions/promo.jpg
この画像 Web アプリケーションには、画像 "promo.jpg" を含むディレクトリが "promotions" に存在する必要があることを意味します。
追伸:なぜこのような時間のかかるサーブレット コンテナのみのソリューションを実行するのかは聞かないでください。
<servlet>
<description></description>
<display-name>ImageDisplayServlet</display-name>
<servlet-name>ImageDisplayServlet</servlet-name>
<servlet-class>com.example.images.ImageDisplayServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ImageDisplayServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
ああyaが最良の結果を得るために上記のようなあなたのサーブレットを設定:P