|
Java форум JavaTalks форум программистов
|
|
|
|
| Предыдущая тема :: Следующая тема |
| Автор |
Сообщение |
Pahan : 717 Постоянный посетитель Откуда: Минск
|
Фев 22, 2010 19:17 |
|
|
В примере будут испльзоватся Servlet 2.5, для версии 2.4 тоже должно работать.
Часть 1. Подготовка формы
Для того, что бы отправить файл на сервер с помщью браузера. Первым делом нужно правильно сформировать форму для отправки запроса. Выглядеть она должна примерно так:
| Код: |
<html>
<head>
<title>Upload</title>
</head>
<body>
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
<input name="description" type="text"><br>
<input name="data" type="file"><br>
<input type="submit"><br>
</form>
</body>
</html> |
Теперь подробно рассмотрим все аттрибуты тега form, все они являются обязательными, без них работать не будет.
action="http://localhost:8080/upload" - указывает адрес, куда будет направлен запрос, вместо http://localhost:8080/upload можно указать любой другой URL. Можно использовать относительные URLы.
method="post" - указывает кокого типа запрос будет сформирован.
По умолчанию аттрибут имеет значение get. Особенностью метода POST является, то что POST запрос может включать в себя произвольные данные. Эти данные обычно называют телом запроса(телом POST запроса). Файлы передаются как-раз в теле запроса.
enctype="multipart/form-data" - указывает браузеру каким образом должно формироваться тело запроса. По умолчанию имеет значение application/x-www-form-urlencoded. Когда в теле запроса передаются данные, они передаются не в сыром виде, так как они хранятся на диске или в памяти. Они предварительно кодируются. Атрибут enctype говорит каким способом нужно кодировать. Браузер добавляет к запросу заголовок Content-Type:enctype-value, где enctype-value это то, что указано в атрибуте enctype (в нашем случае multipart/form-data). Плюс этот заголовок может хранить ещё какие-то дополнительные данные. Сервер этот заголовок прочитает, и на основе его будет знать как раскодировать тело запроса. Или поймёт, что он не знает как это сделать. Именно такая ситуация в сервлетах до версии 2.5 включительно, в версии 3.0 ситуация изменилась. Сервер умеет раскодировать только application/x-www-form-urlencoded закодированные данные. Он понятия не имеет что делать multipart/form-data закодированными данными. И по этому приходится добавлять сторонние библиотеки которые берут эту задачу на себя.
Часть 2. Что делает сервер.
Можно пропустить и читать сразу следующую часть, здесь в основном теория для понимания процесса в целом.
Напишем, сервлет который будет этот запрос принимать.
| Код: |
package sevlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Upload extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String description = request.getParameter("description");
String data = request.getParameter("data");
response.getWriter().println("description="+description);
response.getWriter().println("data="+data);
}
}
|
Сервлет должен прочитать параметры, и отправить их обратно в виде текста.
Развернём сервлет и отправим на него запрос. В поле description вписываем, что угодно, в поле data выбираем любой файл. В результате получим:
| Цитата: |
description=null
data=null |
То есть ничего хорошего не получим. Параметр description и тот не получим даже. Давайте пошагово разберём, что там произошло.
Для отправки запроса, я использовал браузер Mozila Firefox, предварительно установив плагин LiveHTTPheaders, который показывает запросы и ответы в том виде в котором они передаются браузером.
Шаг 1.
В поле description ввёл qwerty. В качестве data указал файл data.txt в котором был стих Пушкина парус. И нажал отправить запрос.
Вид запроса который я получил с помощью плагина:
| Цитата: |
http://localhost:8080/upload
POST /upload HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------74482520013458
Content-Length: 650
-----------------------------74482520013458
Content-Disposition: form-data; name="description"
qwerty
-----------------------------74482520013458
Content-Disposition: form-data; name="data"; filename="data.txt"
Content-Type: text/plain
I"a`?o'n~
A'a*e"a*a*o` i"a`?o'n~ i^a"e`i'i^e^e`e'
A^ o`o'i`a`i'a* i`i^?y" a~i^e"o'a'i^i`!...
?o`i^ e`u`a*o` i^i' a^ n~o`?a`i'a* a"a`e"a*e^i^e'?
?o`i^ e^e`i'o'e" i^i' a^ e^?a`? ?i^a"i'i^i`?...
E`a~?a`?o` a^i^e"i'u^ - a^a*o`a*? n~a^e`u`a*o`,
E` i`a`?o`a` a~i'a*o`n~y" e` n~e^?u^i"e`o`...
O'a^u^, - i^i' n~?a`n~o`e`y" i'a* e`u`a*o`
E` i'a* i^o` n~?a`n~o`e`y" a'a*?e`o`!
I"i^a" i'e`i` n~o`?o'y" n~a^a*o`e"a*e' e"a`c,o'?e`,
I'a`a" i'e`i` e"o'? n~i^e"i'o"a` c,i^e"i^o`i^e'...
A` i^i', i`y"o`a*?i'u^e', i"?i^n~e`o` a'o'?e`,
E^a`e^ a'o'a"o`i^ a^ a'o'?y"o~ a*n~o`u" i"i^e^i^e'!
-----------------------------74482520013458--
|
Все вполне читаемо человеческим глазом, вместо паруса мы видим хрень потому, что это оно так русский текст закодировало.
Если не указать аттрибут enctype, или указать enctype=application/x-www-form-urlencoded, то этот же запрос будет выглядеть так. В этом случае браузер содержимое файла не трогает, а передает только имя фала. И видно, что тело запроса закодировано по другому(попроще).
| Цитата: |
http://localhost:8080/upload
POST /upload HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
description=qwerty&data=data.txt |
Шаг 2.
Браузер устанавливает соединение с сервером и начинает передавать
этот запрос. Сервер начинает читать его. Дочитывает все до строчки Content-Length:650 включительно. Потом смотрит заголовок Content-Type: multipart/form-data; boundary=---------------------------74482520013458. Видит там multipart/form-data и понимает, что не умеет такое орабатывать. Дальше он даже не дочитывает запрос до конца.
Шаг 3.
Довайте модернизируем наш сервлет, и дочитаем остаток запроса вручную.
| Код: |
package sevlet;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Upload extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String description = request.getParameter("description");
String data = request.getParameter("data");
response.getWriter().println("description="+description);
response.getWriter().println("data="+data);
response.getWriter().println("POST request body:");
InputStreamReader reader = new InputStreamReader(request.getInputStream());
int c;
while ((c=reader.read())>=0) {
response.getWriter().print((char)c);
}
}
}
|
Этот сервлет отправит в ответ, тело запроса(ту часть, что не дочитал сервер) в том виде что получил, без расшифровки. Отправим аналогичный запрос на обновленный сервлет. В результате получим:
| Цитата: |
description=null
data=null
POST request body:
-----------------------------204722362218538
Content-Disposition: form-data; name="description"
qwerty
-----------------------------204722362218538
Content-Disposition: form-data; name="data"; filename="data.txt"
Content-Type: text/plain
?????
?????? ????? ????????
? ?????? ???? ???????!...
??? ???? ?? ? ?????? ????????
??? ????? ?? ? ???? ???????...
?????? ????? - ????? ??????,
? ????? ?????? ? ???????...
???, - ?? ??????? ?? ????
? ?? ?? ??????? ?????!
??? ??? ????? ??????? ??????,
??? ??? ??? ?????? ???????...
? ??, ????????, ?????? ????,
??? ????? ? ????? ???? ?????!
-----------------------------204722362218538-- |
Вывод.
Все сторонние библиотеки, для закачки файла на сервер. Занимаются тем, что дочитывают тело запроса, раскодируют его и предоставляют данные в удобном виде.
Часть 3. Upload файла с помощью библиотеки FileUpload от Apache.
Первым делом нужно эту библиотеку скачать, скачать можно тут http://commons.apache.org/fileupload/, также она зависит от библиотеки IO ее тоже качаем http://commons.apache.org/io/. Закидываем их в WEB-INF/lib.
Теперь напишем сервлет, который будет принимать multipart/form-data запрос. И сохранять на сервере все файлы которые будут отправлены. Обычные параметры будет просто выводить в консоль.
| Код: |
package sevlet;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class Upload extends HttpServlet {
private Random random = new Random();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//проверяем является ли полученный запрос multipart/form-data
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);
//максимальный размер данных который разрешено загружать в байтах
//по умолчанию -1, без ограничений. Устанавливаем 10 мегабайт.
upload.setSizeMax(1024 * 1024 * 10);
try {
List items = upload.parseRequest(request);
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (item.isFormField()) {
//если принимаемая часть данных является полем формы
processFormField(item);
} else {
//в противном случае рассматриваем как файл
processUploadedFile(item);
}
}
} catch (Exception e) {
e.printStackTrace();
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
}
/**
* Сохраняет файл на сервере, в папке upload.
* Сама папка должна быть уже создана.
*
* @param item
* @throws Exception
*/
private void processUploadedFile(FileItem item) throws Exception {
File uploadetFile = null;
//выбираем файлу имя пока не найдём свободное
do{
String path = getServletContext().getRealPath("/upload/"+random.nextInt() + item.getName());
uploadetFile = new File(path);
}while(uploadetFile.exists());
//создаём файл
uploadetFile.createNewFile();
//записываем в него данные
item.write(uploadetFile);
}
/**
* Выводит на консоль имя параметра и значение
* @param item
*/
private void processFormField(FileItem item) {
System.out.println(item.getFieldName()+"="+item.getString());
}
} |
Для того, чтобы пример работал, необходимо вручную создать папку upload в корне web - приложения.
Теперь, отправляем например выше упомянутый запрос, смотрим результат.
В добавок скажу, что сохранять файлы можно в любом месте, не обязательно в директории web приложения. Главное, что бы у процесса в котором запущен сервер были права на запись.
Пример полного приложения можно скачать тут. |
|
|
|
 |
|
|
Страница 1 из 1
|
Список форумов
-> Примеры |
|
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах
|
|