Wprowadzenie
Aby przeglądać i wybrać plik do przesłania, potrzebujesz <input type="file">pola HTML w formularzu. Jak podano w specyfikacji HTML , musisz użyć POSTmetody, a enctypeatrybut formularza musi być ustawiony na "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>
Po przesłaniu takiego formularza binarne dane wieloczęściowego formularza są dostępne w treści żądania w innym formacie niż wtedy, gdy enctypenie jest ustawiony.
Przed serwletem 3.0 interfejs API serwletów nie obsługiwał natywnie multipart/form-data. Obsługuje tylko domyślny typ formularza application/x-www-form-urlencoded. Wszyscy request.getParameter()i małżonkowie wróciliby, nullgdy użyli danych wieloczęściowych. To tutaj pojawił się dobrze znany plik Apache Commons FileUpload .
Nie parsuj go ręcznie!
Teoretycznie możesz samodzielnie przeanalizować treść żądania ServletRequest#getInputStream(). Jest to jednak dokładna i żmudna praca, która wymaga dokładnej znajomości RFC2388 . Nie powinieneś próbować tego robić samemu lub kopiować własnego kodu bez biblioteki znalezionego gdzie indziej w Internecie. Wiele źródeł internetowych zawiodło w tym mocno, takich jak roseindia.net. Zobacz także przesyłanie pliku pdf . Powinieneś raczej używać prawdziwej biblioteki, która jest używana (i domyślnie testowana!) Przez miliony użytkowników od lat. Taka biblioteka dowiodła swojej solidności.
Jeśli korzystasz już z Servlet 3.0 lub nowszego, użyj natywnego API
Jeśli używasz co najmniej Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3 itp.), Możesz po prostu użyć standardowego interfejsu API podanego HttpServletRequest#getPart()do zbierania poszczególnych elementów danych formularza wieloczęściowego (większość implementacji Servlet 3.0 faktycznie używa Apache Plik Commons Załaduj pod przykryciem!). Ponadto zwykłe pola formularza są dostępne getParameter()w zwykły sposób.
Najpierw dodaj adnotację do serwletu @MultipartConfig, aby umożliwić mu rozpoznawanie i obsługę multipart/form-datażądań, a tym samym getPart()rozpoczęcie pracy:
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
Następnie zaimplementuj go doPost()w następujący sposób:
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)
}
Uwaga Path#getFileName(). Jest to poprawka MSIE dotycząca uzyskiwania nazwy pliku. Ta przeglądarka niepoprawnie wysyła pełną ścieżkę pliku wzdłuż nazwy zamiast samej nazwy pliku.
Jeśli masz możliwość <input type="file" name="file" multiple="true" />przesyłania wielu plików, zbierz je jak poniżej (niestety nie ma takiej metody jak 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)
}
}
Jeśli nie korzystasz jeszcze z serwletu 3.1, ręcznie pobierz przesłaną nazwę pliku
Uwaga: Part#getSubmittedFileName()została wprowadzona w Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4 itp.). Jeśli nie korzystasz jeszcze z serwletu 3.1, potrzebujesz dodatkowej metody narzędzia, aby uzyskać nazwę przesłanego pliku.
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);
Zwróć uwagę na poprawkę MSIE dotyczącą uzyskiwania nazwy pliku. Ta przeglądarka niepoprawnie wysyła pełną ścieżkę pliku wzdłuż nazwy zamiast samej nazwy pliku.
Jeśli nie korzystasz jeszcze z Servlet 3.0, użyj Apache Commons FileUpload
Jeśli nie korzystasz jeszcze z Servlet 3.0 (czy nie czas na aktualizację?), Powszechną praktyką jest używanie Apache Commons FileUpload do analizowania żądań danych formularza wieloczęściowego. Ma doskonałą instrukcję obsługi i często zadawane pytania (dokładnie przejrzyj oba). Jest też O'Reilly („ cos ”) MultipartRequest, ale ma kilka (drobnych) błędów i nie jest już aktywnie utrzymywany przez lata. Nie polecałbym tego używać. Plik Apache Commons FileUpload jest nadal aktywnie utrzymywany i obecnie bardzo dojrzały.
Aby korzystać z Apache Commons FileUpload, musisz mieć co najmniej następujące pliki w swojej aplikacji internetowej /WEB-INF/lib:
Twoja pierwsza próba nie powiodła się najprawdopodobniej, ponieważ zapomniałeś o wspólnym We / Wy.
Oto przykładowy przykład tego, jak może wyglądać doPost()twój UploadServletpodczas używania 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);
}
// ...
}
To bardzo ważne, aby nie dzwonić getParameter(), getParameterMap(), getParameterValues(), getInputStream(), getReader(), itp na ten sam wniosek wcześniej. W przeciwnym razie kontener serwletu odczyta i przeanalizuje treść żądania, a zatem Apache Commons FileUpload otrzyma pustą treść żądania. Zobacz także ao ServletFileUpload # parseRequest (request) zwraca pustą listę .
Uwaga FilenameUtils#getName(). Jest to poprawka MSIE dotycząca uzyskiwania nazwy pliku. Ta przeglądarka niepoprawnie wysyła pełną ścieżkę pliku wzdłuż nazwy zamiast samej nazwy pliku.
Alternatywnie możesz również zawinąć to wszystko w Filterparsowanie wszystkiego w sposób automatyczny i umieścić rzeczy z powrotem w parametryzacji żądania, abyś mógł nadal używać request.getParameter()zwykłego sposobu i pobierać przesłany plik request.getAttribute(). Możesz znaleźć przykład w tym artykule na blogu .
Obejście problemu z błędem GlassFish3, który getParameter()wciąż powracanull
Należy pamiętać, że starsze niż GlassFish wersje 3.1.2 miałem błąd przy czym getParameter()powraca jeszcze null. Jeśli celujesz w taki kontener i nie możesz go zaktualizować, musisz wyodrębnić wartość getPart()za pomocą tej metody narzędzia:
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">
Zapisywanie przesłanego pliku (nie używaj getRealPath()ani part.write()!)
Przejdź do poniższych odpowiedzi, aby uzyskać szczegółowe informacje na temat prawidłowego zapisywania uzyskanej InputStream( fileContentzmiennej, jak pokazano w powyższych fragmentach kodu) na dysk lub bazę danych:
Udostępnianie przesłanego pliku
Przejdź do następujących odpowiedzi, aby uzyskać szczegółowe informacje na temat prawidłowego udostępniania zapisanego pliku z dysku lub bazy danych z powrotem do klienta:
Ajaxifying the form
Przejdź do poniższych odpowiedzi na temat przesyłania za pomocą Ajax (i jQuery). Należy pamiętać, że kod serwletu do gromadzenia danych formularza nie musi być w tym celu zmieniany! Można zmienić tylko sposób, w jaki reagujesz, ale jest to raczej trywialne (tj. Zamiast przekierowywać do JSP, po prostu wydrukuj trochę JSON lub XML, a nawet zwykły tekst, w zależności od tego, czego oczekuje skrypt odpowiedzialny za wywołanie Ajax).
Mam nadzieję, że to wszystko pomoże :)