[解決済み] JSP/Servletを使用してサーバーにファイルをアップロードするにはどうすればよいですか?
質問
JSP/Servletを使用してサーバーにファイルをアップロードするにはどうすればよいですか?
こんなことをやってみました。
<form action="upload" method="post">
<input type="text" name="description" />
<input type="file" name="file" />
<input type="submit" />
</form>
しかし、ファイル名だけが表示され、ファイルの内容は表示されません。私が
enctype="multipart/form-data"
に
<form>
であれば
request.getParameter()
を返します。
null
.
調査中に偶然見つけた Apache 共通 FileUpload . これを試してみました。
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request); // This line is where it died.
残念なことに、サーブレットは明確なメッセージと原因なしに例外をスローしました。以下はそのスタックトレースです。
SEVERE: Servlet.service() for servlet UploadServlet threw exception
javax.servlet.ServletException: Servlet execution threw an exception
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:637)
解決方法は?
はじめに
アップロードするファイルを参照・選択するためには、HTMLの
<input type="file">
フィールドをフォームに追加します。に記載されているように
HTML仕様
を使用する必要があります。
POST
メソッドと
enctype
属性を設定する必要があります。
"multipart/form-data"
.
<form action="upload" method="post" enctype="multipart/form-data">
<input type="text" name="description" />
<input type="file" name="file" />
<input type="submit" />
</form>
このようなフォームを送信した後、バイナリのマルチパートフォームデータはリクエストボディにある
別のフォーマット
のときよりも
enctype
が設定されていない場合。
Servlet 3.0 より前のバージョンでは、Servlet API はネイティブに
multipart/form-data
. これは、デフォルトのフォームenctypeである
application/x-www-form-urlencoded
. その
request.getParameter()
とコンソートすると、すべて
null
マルチパートフォームデータを使用する場合 ここで、よく知られている
Apache Commons FileUpload
が登場した。
手動でパースしないでください
を元に自分でリクエストボディを解析することは理論的には可能です。
ServletRequest#getInputStream()
. しかし、これは精密で面倒な作業で、以下のような正確な知識が必要です。
RFC2388
. これを自分でやろうとしたり、インターネット上のどこかにあるライブラリのない自作コードをコピーペーストするのはやめたほうがいいでしょう。多くのオンラインソースは、roseindia.netのように、これで大失敗しています。以下もご参照ください。
pdfファイルのアップロード
. むしろ、何百万人ものユーザーによって何年も使われている(そして暗黙のうちにテストされている!)本物のライブラリを使うべきでしょう。そのようなライブラリは、その堅牢性を証明しています。
サーブレット3.0以降の場合は、ネイティブAPIを使用する。
少なくともServlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3, etc) を使用しているのであれば、以下の標準APIを使用することが可能です。
HttpServletRequest#getPart()
を使用して、個々のマルチパートフォームデータアイテムを収集します (ほとんどの Servlet 3.0 実装では、このために Apache Commons FileUpload を実際に使用しています!)。また、通常のフォームフィールドは
getParameter()
は、通常の方法です。
まずサーブレットにアノテーションを付けます。
@MultipartConfig
を認識しサポートするために
multipart/form-data
リクエストを取得し、その結果
getPart()
を動作させることができます。
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
そして、その
doPost()
を次のようにする。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String description = request.getParameter("description"); // Retrieves <input type="text" name="description">
Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}
注意
Path#getFileName()
. これは、ファイル名の取得に関して、MSIEの修正です。このブラウザは、ファイル名だけでなく、ファイル名と一緒にフルパスを不正に送信します。
複数のファイルをアップロードしたい場合は
multiple="true"
,
<input type="file" name="files" multiple="true" />
または、昔ながらの複数入力の方法です。
<input type="file" name="files" />
<input type="file" name="files" />
<input type="file" name="files" />
...
であれば、以下のように収集することができます。
request.getParts("files")
):
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
List<Part> fileParts = request.getParts().stream().filter(part -> "files".equals(part.getName()) && part.getSize() > 0).collect(Collectors.toList()); // Retrieves <input type="file" name="files" multiple="true">
for (Part filePart : fileParts) {
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}
}
Servlet 3.1でない場合は、手動でファイル名を取得します。
注意点
Part#getSubmittedFileName()
は Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4, etc) で導入されました。もしまだ Servlet 3.1 でないなら、提出されたファイル名を取得するために追加のユーティリティメソッドが必要です。
private static String getSubmittedFileName(Part part) {
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
}
}
return null;
}
String fileName = getSubmittedFileName(filePart);
ファイル名の取得に関して、MSIEの修正に注意してください。このブラウザは、ファイル名だけでなく、ファイル名と一緒にフルパスを誤って送信します。
Servlet 3.0 になっていないときは、Apache Commons FileUpload を使ってください。
まだ Servlet 3.0 を使っていない場合 (そろそろアップグレードの時期ではないでしょうか?) は、一般的に
アパッチコモンズファイルアップロード
を使用して、マルチパートフォームデータリクエストを解析します。これには優れた
ユーザーガイド
と
よくある質問
(両方とも丁寧に見てください)。また、オライリー("
コス
")
MultipartRequest
しかし、それはいくつかの(マイナーな)バグを持っており、何年も積極的にメンテナンスされていません。私はそれを使用することをお勧めしません。Apache Commons FileUploadはまだ活発にメンテナンスされており、現在は非常に成熟しています。
Apache Commons FileUploadを使用するためには、少なくとも以下のファイルがWebアプリケーションに含まれている必要があります。
/WEB-INF/lib
:
最初の試みは、コモンズIOを忘れたために失敗した可能性が高いです。
ここでは、キックオフの例として
doPost()
の
UploadServlet
は、Apache Commons FileUploadを使用した場合、以下のようになります。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) {
// Process regular form field (input type="text|radio|checkbox|etc", select, etc).
String fieldName = item.getFieldName();
String fieldValue = item.getString();
// ... (do your job here)
} else {
// Process form file field (input type="file").
String fieldName = item.getFieldName();
String fileName = FilenameUtils.getName(item.getName());
InputStream fileContent = item.getInputStream();
// ... (do your job here)
}
}
} catch (FileUploadException e) {
throw new ServletException("Cannot parse multipart request.", e);
}
// ...
}
を呼び出さないことが非常に重要です。
getParameter()
,
getParameterMap()
,
getParameterValues()
,
getInputStream()
,
getReader()
などをあらかじめ同じリクエストに設定しておきます。そうしないと、サーブレットコンテナはリクエストボディを読んで解析してしまうので、Apache Commons FileUpload は空のリクエストボディを取得してしまうことになります。a.o も参照してください。
ServletFileUpload#parseRequest(request)は空のリストを返します。
.
なお
FilenameUtils#getName()
. これは、ファイル名の取得に関して、MSIEの修正です。このブラウザは、ファイル名だけでなく、ファイル名と一緒にフルパスを不正に送信します。
また、これをすべて
Filter
を使い続けられるように、リクエストのパラメターマップの中に戻してくれます。
request.getParameter()
でアップロードされたファイルを取得することができます。
request.getAttribute()
.
このブログの記事で例が紹介されています
.
のGlassFish3バグに対する回避策。
getParameter()
を返したまま
null
なお、Glassfishの3.1.2より前のバージョンでは
バグ
ここで
getParameter()
を返します。
null
. このようなコンテナをターゲットにしていて、アップグレードできない場合は、値を
getPart()
このユーティリティ・メソッドの助けを借りてください。
private static String getValue(Part part) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), "UTF-8"));
StringBuilder value = new StringBuilder();
char[] buffer = new char[1024];
for (int length = 0; (length = reader.read(buffer)) > 0;) {
value.append(buffer, 0, length);
}
return value.toString();
}
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
アップロードされたファイルの保存(
getRealPath()
また
part.write()
!)
取得したデータを適切に保存するための詳細は、次の回答をご覧ください。
InputStream
(その
fileContent
をディスクまたはデータベースに保存します。
アップロードされたファイルの処理
ディスクまたはデータベースからクライアントに保存されたファイルを適切に提供するための詳細については、次の回答を参照してください。
- h:graphicImage> または <img> タグを使用して、Webapps / webcontext / deploy フォルダの外から画像を読み込む。
- データベースから画像を取得し、JSPページに表示するには?
- Java Web アプリケーションでアプリケーションサーバーの外側から静的データを提供する最も簡単な方法
- HTTP キャッシングをサポートする静的リソースサーブレットの抽象テンプレートです。
フォームのAjax化
Ajax(とjQuery)を使ってアップロードする方法については、次の回答をご覧ください。フォームデータを収集するサーブレットコードは、このために変更する必要がないことに注意してください! 応答する方法だけが変更されるかもしれませんが、これはむしろ些細なことです(すなわち、JSPに転送する代わりに、Ajax呼び出しの責任者が期待するものに応じて、JSONまたはXML、あるいはプレーンテキストを表示するだけです)。
- JSP/サーブレットとAjaxを使用してサーバーにファイルをアップロードするには?
- xmlHttpRequestでファイルをマルチパートとして送信する。
- JavaサーブレットへのHTML5ドラッグ&ドロップによるファイルアップロード
これがすべて役に立つことを願っています :)
関連
-
eclipse アクセス制限です。タイプ 'xxx' は API ではありません(必須ライブラリ '' の制限)。
-
Dateが型に解決できない問題を解決する
-
JQuery DataTable 详解
-
Error: java.lang.NoClassDefFoundError: クラス XXXX を初期化できませんでした
-
SocketTimeoutExceptionです。読み込みがタイムアウトしました
-
JSPで「リストが型解決できない!」の解決方法
-
[解決済み] Javaでメモリーリークを発生させるにはどうしたらいいですか?
-
[解決済み] Mavenを使用して、依存関係を持つ実行可能なJARを作成するにはどうすればよいですか?
-
[解決済み】JSP 2を使用して、JSPファイル内のJavaコードを回避するにはどうすればよいですか?
-
[解決済み】JSF、Servlet、JSPの違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
eclipse アクセス制限です。タイプ 'xxx' は API ではありません(必須ライブラリ '' の制限)。
-
java Mail send email smtp is not authenticated by TLS encryption solution.
-
eclipseにプロジェクトをインポートした後、Editorにmain typeが含まれない問題
-
Eclipseでプロジェクトエクスプローラービューとパッケージエクスプローラービューを使う
-
自動配線された依存性のインジェクションに失敗しました。
-
Eclipse起動エラー:javaは起動したが、終了コード=1を返した(ネット上の様々な落とし穴)
-
あるコードに出会いましたが、何に使うのか理解できません。 List<String> list = new ArrayList<String>() { { a
-
このラインで複数のマーカーを解決する方法
-
テストが見つかりませんでした
-
Google Chromeのエラー「Not allowed to load local resource」の解決策について