Einführung
Zum Durchsuchen und Auswählen einer Datei zum Hochladen benötigen Sie ein HTML- <input type="file">
Feld im Formular. Wie in der HTML-Spezifikation angegeben , müssen Sie die POST
Methode verwenden und das enctype
Attribut des Formulars muss festgelegt werden "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>
Nach dem Absenden eines solchen Formulars stehen die binären mehrteiligen Formulardaten im Anforderungshauptteil in einem anderen Format zur Verfügung als beimenctype
nicht festgelegt ist.
Vor Servlet 3.0 wurde die Servlet-API von Haus aus nicht unterstützt multipart/form-data
. Es wird nur der Standardformular-Enctype von unterstützt application/x-www-form-urlencoded
. Die request.getParameter()
und -Konsorten würden alle zurückkehren, null
wenn mehrteilige Formulardaten verwendet werden. Hier befindet sich der bekannte Apache Commons FileUpload ins .
Analysieren Sie es nicht manuell!
Sie können den Anfragetext theoretisch selbst anhand von analysieren ServletRequest#getInputStream()
. Dies ist jedoch eine präzise und langwierige Arbeit, die genaue Kenntnisse von RFC2388 erfordert . Sie sollten nicht versuchen, dies selbst zu tun oder einen selbst entwickelten Code ohne Bibliothek zu kopieren, der an anderer Stelle im Internet zu finden ist. Viele Online-Quellen wie roseindia.net sind dabei schwer gescheitert. Siehe auch Hochladen der PDF-Datei . Sie sollten lieber eine echte Bibliothek verwenden, die jahrelang von Millionen von Benutzern verwendet (und implizit getestet!) Wird. Eine solche Bibliothek hat ihre Robustheit bewiesen.
Wenn Sie bereits Servlet 3.0 oder höher verwenden, verwenden Sie die native API
Wenn Sie mindestens Servlet 3.0 verwenden (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3 usw.), können Sie einfach die bereitgestellte Standard-API verwenden HttpServletRequest#getPart()
, um die einzelnen mehrteiligen Formulardatenelemente zu erfassen (die meisten Servlet 3.0-Implementierungen verwenden tatsächlich Apache Commons FileUpload unter der Decke dafür!). Außerdem sind normale Formularfelder auf getParameter()
die übliche Weise verfügbar .
Kommentieren Sie zuerst Ihr Servlet mit @MultipartConfig
, damit es multipart/form-data
Anforderungen erkennt und unterstützt und somit getPart()
an die Arbeit geht:
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
Implementieren Sie dann doPost()
Folgendes:
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)
}
Beachten Sie die Path#getFileName()
. Dies ist ein MSIE-Fix zum Abrufen des Dateinamens. Dieser Browser sendet fälschlicherweise den vollständigen Dateipfad entlang des Namens anstelle nur des Dateinamens.
Wenn Sie einen <input type="file" name="file" multiple="true" />
Upload für mehrere Dateien haben, sammeln Sie diese wie folgt (leider gibt es keine Methode wie 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)
}
}
Wenn Sie noch nicht mit Servlet 3.1 arbeiten, rufen Sie den übermittelten Dateinamen manuell ab
Beachten Sie, dass dies Part#getSubmittedFileName()
in Servlet 3.1 eingeführt wurde (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4 usw.). Wenn Sie noch nicht mit Servlet 3.1 arbeiten, benötigen Sie eine zusätzliche Dienstprogrammmethode, um den übermittelten Dateinamen abzurufen.
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);
Beachten Sie den MSIE-Fix zum Abrufen des Dateinamens. Dieser Browser sendet fälschlicherweise den vollständigen Dateipfad entlang des Namens anstelle nur des Dateinamens.
Wenn Sie noch nicht mit Servlet 3.0 arbeiten, verwenden Sie Apache Commons FileUpload
Wenn Sie noch nicht mit Servlet 3.0 arbeiten (ist es nicht an der Zeit, ein Upgrade durchzuführen ?), Wird in der Regel Apache Commons FileUpload verwendet , um die mehrteiligen Formulardatenanforderungen zu analysieren. Es hat eine ausgezeichnete Bedienungsanleitung und FAQ (gehen Sie beide sorgfältig durch). Es gibt auch den O'Reilly (" cos ") MultipartRequest
, aber er hat einige (kleinere) Fehler und wird seit Jahren nicht mehr aktiv gewartet. Ich würde es nicht empfehlen. Apache Commons FileUpload wird immer noch aktiv gepflegt und ist derzeit sehr ausgereift.
Um Apache Commons FileUpload verwenden zu können, müssen mindestens die folgenden Dateien in Ihrer Webanwendung enthalten sein /WEB-INF/lib
:
Ihr erster Versuch ist höchstwahrscheinlich fehlgeschlagen, weil Sie die Commons-E / A vergessen haben.
Hier ist ein Kickoff-Beispiel, wie das doPost()
von Ihnen UploadServlet
aussehen könnte, wenn Sie Apache Commons FileUpload verwenden:
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);
}
// ...
}
Es ist sehr wichtig , dass Sie nicht nennen getParameter()
, getParameterMap()
, getParameterValues()
, getInputStream()
, getReader()
, usw. auf den gleichen Antrag zuvor. Andernfalls liest und analysiert der Servlet-Container den Anforderungshauptteil, sodass Apache Commons FileUpload einen leeren Anforderungshauptteil erhält. Siehe auch ao ServletFileUpload # parseRequest (Anfrage) gibt eine leere Liste zurück .
Beachten Sie die FilenameUtils#getName()
. Dies ist ein MSIE-Fix zum Abrufen des Dateinamens. Dieser Browser sendet fälschlicherweise den vollständigen Dateipfad entlang des Namens anstelle nur des Dateinamens.
Alternativ können Sie dies alles auch in eine Filter
Datei packen, die alles automatisch analysiert und das Material wieder in die Parameterkarte der Anforderung einfügt, sodass Sie auf request.getParameter()
die übliche Weise fortfahren und die hochgeladene Datei von abrufen können request.getAttribute()
. Ein Beispiel finden Sie in diesem Blog-Artikel .
Problemumgehung für den GlassFish3-Fehler, getParameter()
immer noch zurückzukehrennull
Beachten Sie, dass Glassfish-Versionen älter als 3.1.2 einen Fehler hatten, bei dem das getParameter()
immer noch zurückkehrt null
. Wenn Sie auf einen solchen Container abzielen und ihn nicht aktualisieren können, müssen Sie den Wert getPart()
mithilfe dieser Dienstprogrammmethode extrahieren :
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">
Hochgeladene Datei speichern (weder verwenden getRealPath()
noch part.write()
!)
Weitere Informationen zum ordnungsgemäßen Speichern der erhaltenen InputStream
(der fileContent
in den obigen Codeausschnitten gezeigten Variablen) auf Festplatte oder Datenbank finden Sie in den folgenden Antworten :
Hochgeladene Datei bereitstellen
Weitere Informationen zum ordnungsgemäßen Bereitstellen der gespeicherten Datei von der Festplatte oder Datenbank auf dem Client finden Sie in den folgenden Antworten:
Ajaxifizierung der Form
Lesen Sie die folgenden Antworten zum Hochladen mit Ajax (und jQuery). Beachten Sie, dass der Servlet-Code zum Sammeln der Formulardaten dafür nicht geändert werden muss! Nur die Art und Weise, wie Sie reagieren, kann geändert werden. Dies ist jedoch eher trivial (dh anstatt an JSP weiterzuleiten, drucken Sie einfach JSON oder XML oder sogar einfachen Text, je nachdem, was das für den Ajax-Aufruf verantwortliche Skript erwartet).
Hoffe das alles hilft :)