Einführung
Zum Durchsuchen und Auswählen einer Datei zum Hochladen benötigen Sie eine HTML <input type="file">
Feld des Formulars. Wie in der HTML-Spezifikation müssen Sie die POST
Methode und die enctype
Attribut des Formulars muss gesetzt werden auf "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 Anfragekörper in ein anderes Format als wenn die enctype
nicht eingestellt ist.
Vor Servlet 3.0 (Dezember 2009) unterstützte die Servlet-API nicht nativ multipart/form-data
. Es unterstützt nur den Standard-Formular-Enctype von application/x-www-form-urlencoded
. El request.getParameter()
und Gefährtinnen würden alle zurückkehren null
bei der Verwendung von mehrteiligen Formulardaten. Hier ist die bekannte Apache Commons FileUpload kam ins Spiel.
Analysieren Sie es nicht manuell!
Theoretisch können Sie den Request Body selbst analysieren, indem Sie ServletRequest#getInputStream()
. Dies ist jedoch eine präzise und langwierige Arbeit, die eine genaue Kenntnis der RFC2388 . Sie sollten nicht versuchen, dies auf eigene Faust zu tun oder einen selbst erstellten, bibliothekslosen Code zu kopieren, den Sie irgendwo im Internet gefunden haben. Viele Online-Quellen, wie z. B. roseindia.net, sind hieran gescheitert. Siehe auch Hochladen einer pdf-Datei . Sie sollten lieber eine echte Bibliothek verwenden, die seit Jahren von Millionen von Nutzern verwendet (und implizit getestet!) wird. Eine solche Bibliothek hat ihre Robustheit bewiesen.
Wenn Sie bereits Servlet 3.0 oder eine neuere Version einsetzen, verwenden Sie die native API
Wenn Sie mindestens Servlet 3.0 verwenden (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3 usw., die es bereits seit 2010 gibt), dann können Sie einfach die Standard-API verwenden, die [HttpServletRequest#getPart()
](http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getParts()) um die einzelnen mehrteiligen Formulardaten zu sammeln (die meisten Servlet-3.0-Implementierungen じつに verwenden Sie Apache Commons FileUpload unter der Haube für diese!). Auch normale Formularfelder sind verfügbar durch getParameter()
auf die übliche Weise.
Kommentieren Sie zunächst Ihr Servlet mit @MultipartConfig
um sie erkennen und unterstützen zu lassen multipart/form-data
Anfragen und erhalten somit getPart()
zur Arbeit:
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
Dann implementieren Sie seine doPost()
wie folgt:
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 eine MSIE-Korrektur, um den Dateinamen zu erhalten. Dieser Browser sendet fälschlicherweise den vollständigen Dateipfad zusammen mit dem Namen und nicht nur den Dateinamen.
Falls Sie mehrere Dateien hochladen möchten, entweder über multiple="true"
,
<input type="file" name="files" multiple="true" />
oder auf die altmodische Weise mit mehreren Eingängen,
<input type="file" name="files" />
<input type="file" name="files" />
<input type="file" name="files" />
...
dann können Sie sie wie folgt sammeln (leider gibt es keine solche Methode wie 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)
}
}
Wenn Sie noch nicht mit Servlet 3.1 arbeiten, lassen Sie sich den Dateinamen manuell übermitteln
なお [Part#getSubmittedFileName()
](https://docs.oracle.com/javaee/7/api/javax/servlet/http/Part.html#getSubmittedFileName()) wurde mit Servlet 3.1 eingeführt (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4, usw. gibt es bereits seit 2013). Wenn Sie noch nicht mit Servlet 3.1 arbeiten (wirklich?), benötigen Sie eine zusätzliche Dienstprogrammmethode, um den übermittelten Dateinamen zu erhalten.
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 die MSIE-Korrektur, um den Dateinamen zu erhalten. Dieser Browser sendet fälschlicherweise den vollständigen Dateipfad zusammen mit dem Namen und nicht nur den Dateinamen.
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 vorzunehmen? es wurde vor über einem Jahrzehnt veröffentlicht! Apache Commons FileUpload zum Parsen der mehrteiligen Formulardatenanforderungen. Es hat eine ausgezeichnete Benutzerhandbuch y FAQ (gehen Sie beide sorgfältig durch). Es gibt auch die O'Reilly (" cos ") MultipartRequest
, aber es hat einige (kleinere) Fehler und wird seit Jahren nicht mehr aktiv gepflegt. Ich würde nicht empfehlen, es zu benutzen. Apache Commons FileUpload wird immer noch aktiv gepflegt und ist derzeit sehr ausgereift.
Um Apache Commons FileUpload nutzen zu können, müssen Sie mindestens die folgenden Dateien in Ihrer Webapplikation haben /WEB-INF/lib
:
Ihr erster Versuch scheiterte höchstwahrscheinlich, weil Sie das gemeinsame IO vergessen haben.
Hier ist ein Beispiel für den Start, wie die doPost()
Ihrer UploadServlet
aussehen kann, 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 anrufen getParameter()
, getParameterMap()
, getParameterValues()
, getInputStream()
, getReader()
usw. auf die gleiche Anfrage im Voraus. Andernfalls wird der Servlet-Container den Request-Body lesen und parsen und Apache Commons FileUpload erhält einen leeren Request-Body. Siehe auch a.o. ServletFileUpload#parseRequest(request) gibt eine leere Liste zurück .
Beachten Sie die FilenameUtils#getName()
. Dies ist eine MSIE-Korrektur, um den Dateinamen zu erhalten. Dieser Browser sendet fälschlicherweise den vollständigen Dateipfad zusammen mit dem Namen und nicht nur den Dateinamen.
Alternativ können Sie das Ganze auch in eine Filter
die alles automatisch parst und in die Parametermap der Anfrage zurückstellt, so dass Sie weiterhin mit request.getParameter()
auf die übliche Weise und rufen Sie die hochgeladene Datei über request.getAttribute()
. Ein Beispiel finden Sie in diesem Blog-Artikel .
Abhilfe für GlassFish3-Bug von getParameter()
immer noch zurückkehrend null
Beachten Sie, dass Glassfish-Versionen älter als 3.1.2 über eine Wanze wobei die getParameter()
kehrt immer noch zurück null
. Wenn Sie einen solchen Container anvisieren und ihn nicht aktualisieren können, müssen Sie den Wert aus getPart()
mit Hilfe dieser Utility-Methode:
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">
Speichern der hochgeladenen Datei (nicht mit getRealPath()
noch part.write()
!)
In den folgenden Antworten finden Sie ausführliche Informationen zum richtigen Speichern der erhaltenen Daten InputStream
(die fileContent
wie in den obigen Codeschnipseln gezeigt) auf die Festplatte oder in die Datenbank:
Hochgeladene Datei servieren
In den folgenden Antworten erfahren Sie, wie Sie die gespeicherte Datei von der Festplatte oder aus der Datenbank wieder an den Client übergeben:
Ajaxifizierung des Formulars
Lesen Sie in den folgenden Antworten, wie Sie mit Ajax (und jQuery) hochladen können. Bitte beachten Sie, dass der Servlet-Code zum Sammeln der Formulardaten dafür nicht geändert werden muss! Lediglich die Art und Weise, wie Sie antworten, muss geändert werden, aber das ist ziemlich trivial (d.h. anstatt an JSP weiterzuleiten, drucken Sie einfach etwas JSON oder XML oder sogar reinen Text aus, je nachdem, was das für den Ajax-Aufruf zuständige Skript erwartet).
Ich hoffe, das alles hilft :)