Недавно на работе была задача добавить в одну из форм возможность загрузки файлов. Я рассказал заказчику, что это займёт много времени и вообще сложно, надеясь выиграть время и не кидаться делать задачу сразу же. А потом оказалось, что это довольно просто, и я сделал задачу так быстро, что ещё даже не успел об этом оповестить заказчика.
В этой статье предлагаю своё решение с загрузкой файла. Фронт в моём проекте составлен из jsp-страниц и написан с участием jquery и ajax. Бэк на джаве и состоит из сервлетов.
HTML. Форма с файлом
Для контекста сначала покажу форму.
<form id="form" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<div class="data-edit-line-row">
<p>Описание</p>
<input name="description" type="text">
<input name="uploadFile" type="submit" value="Загрузить">
</div>
<div class="data-input">
<p>Комментарий</p>
<input name="comment" type="text" >
</div>
</form>
В форме есть поле для выбора файла, текстовые поля для введения комментария и описания и кнопка Загрузить с именем uploadFile.
Скрипт ajax
Теперь покажу, что надо писать в событии по клику на эту кнопку.
$(document).ready(function() {
$('[name="uploadFile"]').on('click', function () {
event.preventDefault();
var form = $('#form')[0];
var data = new FormData(form);
$.ajax({
url: "/sortingUploadFiles",
type: "POST",
enctype: 'multipart/form-data',
data: data,
processData: false,
contentType: false,
cache: false,
success: function () {
location.reload(true);
},
error: function (error) {
console.log("File upload failed: ", error);
}
});
});
})
Разберу строчки по порядку.
Сначала я прошу preventDefault, чтобы по нажатию на кнопку типа submit форма не начала обрабатываться. Мы сделаем это сами, автоматизация нам тут ни к чему.
Далее создаём объект из формы. Для этого находим все объекты по идентификатору #form. В моём примере такой объект всего один, поэтому можно смело брать нулевой объект с таким идентификатором и в следующей строчке превращаем форму в объект FormData и храним его под именем data.
Далее я иду по адресу /sortingUploadFiles (в вашем приложении он, конечно, будет другим). Там находится сервлет, который обработает запрос типа POST.
enctype задаём как multipart/form-data. А вот contentType должен быть false. Иначе добавится заголовок с типом данных, что нам тоже не нужно. processData тоже устанавливаем на false, чтобы jQuery не пытался превратить наши данные в строку (это ему не удалось бы).
В случае success я перезагружаю страницу (мне так надо), а в случае ошибки ничего путного не делаю, просто для вида вывожу строку в консоль. Вы же можете добавить туда какую только душе угодно логику.
Обработка на бэке. Java. Servlet
Напоследок поделюсь своим бэком. Почему-то его в подобных статьях о проблемах ajax и jQuery часто игнорируют. Видимо, подразумевают, что бэк может быть на любом языке… Но любого языка у меня для вас нет, только java.
public class sortingUploadFiles extends HttpServlet {
private Random random = new Random();
private String pathFiles = "/files/";
SortingFiles sortingFiles = new SortingFiles();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
File tempDir = (File)getServletContext().getAttribute("javax.servlet.context.tempdir");
factory.setRepository(tempDir);
ServletFileUpload upload = new ServletFileUpload(factory);
String pathDocum = "";
String descrDocum = "";
String comment = "";
int iRandom = random.nextInt();
try {
List<FileItem> items = upload.parseRequest(request);
Iterator<FileItem> iter = items.iterator();
while (iter.hasNext()) {
FileItem item = iter.next();
if (!item.isFormField()) {
processUploadedFile(item, iRandom);
pathDocum = item.getName();
} else if (item.getFieldName().equals("description")) {
descrDocum = item.getString("utf-8");
} else if (item.getFieldName().equals("comment")) {
comment = item.getString("utf-8");
}
}
sortingFiles.setName(descrDocum);
sortingFiles.setPath(iRandom + "_" + pathDocum);
sortingFiles.setComment(comment);
SortingDocumDAOImpl.save(sortingFiles);
} catch (Exception e) {
e.printStackTrace();
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
response.sendRedirect("/sorting");
response.flushBuffer();
}
private void processUploadedFile(FileItem item, int iRandom) throws Exception {
File uploadedFile = null;
String path = getServletContext().getRealPath(pathFiles + iRandom + "_" + item.getName());
uploadedFile = new File(path);
uploadedFile.createNewFile();
item.write(uploadedFile);
}
Здесь довольно много происходит. Но если вкратце, то я записываю файл на диск в нужную папку, а также по очереди достаю из запроса все данные (описание файла и комментарий к нему) и заполняю объект sortingFiles, который потом сохраняю в базе с помощью класса SortingDocumDAOImpl.
Случайное число добавляю к адресу файла, чтобы была большая гарантия, что каждое имя уникально.
Классы DiskFileItemFactory, ServletFileUpload и FileItem находятся в пакете org.apache.commons.fileupload. Если вас интересует лишь обработка формы, то они вам помогут больше всего. Но я специально оставил кусочки остальной логики, чтобы всунуть обработку формы в привычный контекст сохранения данных, в том числе в базу.