Вступ
Для перегляду та вибору файлу для завантаження потрібне 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 API Servlet не підтримував початково multipart/form-data
. Він підтримує лише ентетип форми за замовчуванням application/x-www-form-urlencoded
. Усі request.getParameter()
та повертаються всі повертаються null
при використанні даних про багаточастинні форми. Тут вийшов добре відомий файл Apache Commons FileUpload .
Не розбирайте її вручну!
Теоретично ви можете самостійно проаналізувати орган запиту на основі ServletRequest#getInputStream()
. Однак це точна і копітка робота, яка вимагає точного знання RFC2388 . Не слід намагатися робити це самостійно або копіювати код, що не містить домашню бібліотеку, який можна знайти в інших місцях Інтернету. Багато джерел в Інтернеті важко провалилися в цьому, як-от roseindia.net. Дивіться також завантаження файлу pdf . Вам слід скористатися справжньою бібліотекою, яку мільйони користувачів використовують (і неявно перевіряють!) Протягом багатьох років. Така бібліотека довела свою стійкість.
Коли ви вже користуєтеся Servlet 3.0 або новішою версією, використовуйте нативний API
Якщо ви використовуєте щонайменше Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3 тощо), то ви можете просто використовувати стандартний 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 щодо отримання імені файлу. Цей браузер неправильно надсилає повний шлях до файлу вздовж імені, а не лише ім'я файлу.
Якщо у вас є <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
Якщо ви ще не користуєтеся сервлетом 3.0 (чи не час оновити?), Загальною практикою є використання Apache Commons FileUpload для розбору запитів даних багатопартійної форми. У ньому є чудове керівництво користувача та поширені запитання (ретельно проходьте обидва). Існує також O'Reilly (" cos ") MultipartRequest
, але він має деякі (незначні) помилки і вже активно не підтримується роками. Я б не рекомендував його використовувати. Apache Commons FileUpload все ще активно підтримується і на даний момент дуже зрілий.
Для того, щоб використовувати Apache Commons FileUpload, вам потрібно мати принаймні такі файли у своїх веб-сайтах /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 отримає порожнє тіло запиту. Див. Також ao ServletFileUpload # parseRequest (запит) повертає порожній список .
Зверніть увагу на 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
змінної, як показано у наведених вище фрагментах коду) на диску чи базі даних:
Подання завантаженого файлу
Перейдіть до наступних відповідей, щоб отримати детальну інформацію про правильне обслуговування збереженого файлу з диска або бази даних назад клієнту:
Аяксифікуюча форма
Перейдіть до наступних відповідей, як завантажувати за допомогою Ajax (та jQuery). Зауважте, що сервлет-код для збору даних форми для цього не потрібно змінювати! Змінити може лише те, як ви реагуєте, але це досить тривіально (тобто замість переадресації на JSP, просто надрукуйте якийсь JSON чи XML або навіть звичайний текст залежно від того, що очікує сценарій, відповідальний за виклик Ajax).
Сподіваюся, що це все допомагає :)