前書き
アップロードするファイルを参照して選択するに<input type="file">
は、フォームにHTML フィールドが必要です。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
。
サーブレット3.0以前は、サーブレットAPIはをネイティブにサポートしていませんでしたmultipart/form-data
。デフォルトのフォームenctypeのみをサポートしていますapplication/x-www-form-urlencoded
。request.getParameter()
そしてCONSORTSすべての復帰を希望null
マルチパートフォームデータを使用した場合。これは、よく知られているApache Commons FileUploadが登場した場所です。
手動で解析しないでください!
理論的には、に基づいてリクエスト本文を自分で解析できますServletRequest#getInputStream()
。ただし、これはRFC2388の正確な知識を必要とする正確で退屈な作業です。自分でこれを実行したり、インターネット上の他の場所で見つかった独自のライブラリなしのコードをコピー貼り付けしたりしないでください。roseindia.netなど、多くのオンラインソースがこれに失敗しました。PDFファイルのアップロードもご覧ください。何百万人ものユーザーによって何年も使用されている(そして暗黙的にテストされている)実際のライブラリを使用するべきです。このようなライブラリは、その堅牢性を証明しています。
サーブレット3.0以降を使用している場合は、ネイティブAPIを使用します
少なくともサーブレット3.0(Tomcat 7、Jetty 9、JBoss AS 6、GlassFish 3など)を使用している場合は、提供さHttpServletRequest#getPart()
れている標準APIを使用して、個々のマルチパートフォームデータ項目を収集できます(ほとんどの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の修正です。このブラウザは、ファイル名だけではなく、名前に沿って完全なファイルパスを誤って送信します。
<input type="file" name="file" multiple="true" />
マルチファイルのアップロードがある場合は、以下のように収集してください(残念ながらのような方法はありませんrequest.getParts("file")
)。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
List<Part> fileParts = request.getParts().stream().filter(part -> "file".equals(part.getName()) && part.getSize() > 0).collect(Collectors.toList()); // Retrieves <input type="file" name="file" multiple="true">
for (Part filePart : fileParts) {
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}
}
まだサーブレット3.1を使用していない場合は、送信されたファイル名を手動で取得します
Part#getSubmittedFileName()
サーブレット3.1(Tomcat 8、Jetty 9、WildFly 8、GlassFish 4など)で導入されたことに注意してください。まだサーブレット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の修正に注意してください。このブラウザは、ファイル名だけではなく、名前に沿って完全なファイルパスを誤って送信します。
まだサーブレット3.0を使用していない場合は、Apache Commons FileUploadを使用します
まだServlet 3.0を使用していない場合(アップグレードの時期ではありませんか?)、一般的な方法は、Apache Commons FileUploadを使用してマルチパートフォームデータリクエストを解析することです。優れたユーザーガイドとFAQがあります(両方を注意深く読んでください)。O'Reilly( " cos ")もありますがMultipartRequest
、いくつかの(マイナー)バグがあり、何年も積極的にメンテナンスされていません。私はそれを使用することをお勧めしません。Apache Commons FileUploadはまだ積極的に維持されており、現在非常に成熟しています。
Apache Commons FileUploadを使用するには、Webアプリケーションに少なくとも以下のファイルが必要です/WEB-INF/lib
。
コモンズIOを忘れたため、最初の試行が失敗した可能性があります。
Apache Commons FileUploadを使用した場合のキックオフの例を次に示しdoPost()
ますUploadServlet
。
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は空のリクエストボディを取得します。ao ServletFileUpload#parseRequest(request)が空のリストを返すも参照してください。
に注意してくださいFilenameUtils#getName()
。これは、ファイル名の取得に関するMSIEの修正です。このブラウザは、ファイル名だけではなく、名前に沿って完全なファイルパスを誤って送信します。
またはFilter
、すべてを自動的に解析するでこれをすべてラップし、リクエストのパラメーターマップにデータを戻すことでrequest.getParameter()
、通常の方法を使用してアップロードしたファイルをで取得できるようにすることもできますrequest.getAttribute()
。このブログ記事で例を見つけることができます。
GlassFish3バグがgetParameter()
まだ戻るという回避策null
3.1.2より前のバージョンのGlassfishにはバグが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
ディスクまたはデータベースに上記のコードスニペットに示すように、変数):
アップロードされたファイルを提供する
保存されたファイルをディスクまたはデータベースからクライアントに適切に提供する方法の詳細については、次の回答を参照してください。
フォームをAjax化する
Ajax(およびjQuery)を使用してアップロードする方法については、次の回答を参照してください。フォームデータを収集するためのサーブレットコードを変更する必要はありません。応答方法のみが変更される可能性がありますが、これはかなり自明です(つまり、JSPに転送する代わりに、Ajax呼び出しを担当するスクリプトが期待するものに応じて、JSONまたはXMLまたはプレーンテキストを印刷するだけです)。
これがすべて役に立てば幸い:)