Как позволить Java HttpServer обрабатывать пост-запрос для загрузки файла
Прежде всего, мой вопрос о HttpServer в Java для обработки запроса POST от клиента, а не о клиенте Java, который может загружать файлы на веб-сервер.
ХОРОШО. Я использую облегченный HttpServer в Java для обработки "GET" || "POST" запросы. Исходный код HttpServer скопирован с http://www.prasannatech.net/2008/11/http-web-server-java-post-file-upload.html.
/*
* HTTPPOSTServer.java
* Author: S.Prasanna
* @version 1.00
*/
import java.io.*;
import java.net.*;
import java.util.*;
public class HTTPPOSTServer extends Thread {
static final String HTML_START =
"<html>" +
"<title>HTTP POST Server in java</title>" +
"<body>";
static final String HTML_END =
"</body>" +
"</html>";
Socket connectedClient = null;
BufferedReader inFromClient = null;
DataOutputStream outToClient = null;
public HTTPPOSTServer(Socket client) {
connectedClient = client;
}
public void run() {
String currentLine = null, postBoundary = null, contentength = null, filename = null, contentLength = null;
PrintWriter fout = null;
try {
System.out.println( "The Client "+
connectedClient.getInetAddress() + ":" + connectedClient.getPort() + " is connected");
inFromClient = new BufferedReader(new InputStreamReader (connectedClient.getInputStream()));
outToClient = new DataOutputStream(connectedClient.getOutputStream());
currentLine = inFromClient.readLine();
String headerLine = currentLine;
StringTokenizer tokenizer = new StringTokenizer(headerLine);
String httpMethod = tokenizer.nextToken();
String httpQueryString = tokenizer.nextToken();
System.out.println(currentLine);
if (httpMethod.equals("GET")) {
System.out.println("GET request");
if (httpQueryString.equals("/")) {
// The default home page
String responseString = HTTPPOSTServer.HTML_START +
"<form action=\"http://127.0.0.1:5000\" enctype=\"multipart/form-data\"" +
"method=\"post\">" +
"Enter the name of the File <input name=\"file\" type=\"file\"><br>" +
"<input value=\"Upload\" type=\"submit\"></form>" +
"Upload only text files." +
HTTPPOSTServer.HTML_END;
sendResponse(200, responseString , false);
} else {
sendResponse(404, "<b>The Requested resource not found ...." +
"Usage: http://127.0.0.1:5000</b>", false);
}
}
else { //POST request
System.out.println("POST request");
do {
currentLine = inFromClient.readLine();
if (currentLine.indexOf("Content-Type: multipart/form-data") != -1) {
String boundary = currentLine.split("boundary=")[1];
// The POST boundary
while (true) {
currentLine = inFromClient.readLine();
if (currentLine.indexOf("Content-Length:") != -1) {
contentLength = currentLine.split(" ")[1];
System.out.println("Content Length = " + contentLength);
break;
}
}
//Content length should be < 2MB
if (Long.valueOf(contentLength) > 2000000L) {
sendResponse(200, "File size should be < 2MB", false);
}
while (true) {
currentLine = inFromClient.readLine();
if (currentLine.indexOf("--" + boundary) != -1) {
filename = inFromClient.readLine().split("filename=")[1].replaceAll("\"", "");
String [] filelist = filename.split("\\" + System.getProperty("file.separator"));
filename = filelist[filelist.length - 1];
System.out.println("File to be uploaded = " + filename);
break;
}
}
String fileContentType = inFromClient.readLine().split(" ")[1];
System.out.println("File content type = " + fileContentType);
inFromClient.readLine(); //assert(inFromClient.readLine().equals("")) : "Expected line in POST request is "" ";
fout = new PrintWriter(filename);
String prevLine = inFromClient.readLine();
currentLine = inFromClient.readLine();
//Here we upload the actual file contents
while (true) {
if (currentLine.equals("--" + boundary + "--")) {
fout.print(prevLine);
break;
}
else {
fout.println(prevLine);
}
prevLine = currentLine;
currentLine = inFromClient.readLine();
}
sendResponse(200, "File " + filename + " Uploaded..", false);
fout.close();
} //if
}while (inFromClient.ready()); //End of do-while
}//else
} catch (Exception e) {
e.printStackTrace();
}
}
public void sendResponse (int statusCode, String responseString, boolean isFile) throws Exception {
String statusLine = null;
String serverdetails = "Server: Java HTTPServer";
String contentLengthLine = null;
String fileName = null;
String contentTypeLine = "Content-Type: text/html" + "\r\n";
FileInputStream fin = null;
if (statusCode == 200)
statusLine = "HTTP/1.1 200 OK" + "\r\n";
else
statusLine = "HTTP/1.1 404 Not Found" + "\r\n";
if (isFile) {
fileName = responseString;
fin = new FileInputStream(fileName);
contentLengthLine = "Content-Length: " + Integer.toString(fin.available()) + "\r\n";
if (!fileName.endsWith(".htm") && !fileName.endsWith(".html"))
contentTypeLine = "Content-Type: \r\n";
}
else {
responseString = HTTPPOSTServer.HTML_START + responseString + HTTPPOSTServer.HTML_END;
contentLengthLine = "Content-Length: " + responseString.length() + "\r\n";
}
outToClient.writeBytes(statusLine);
outToClient.writeBytes(serverdetails);
outToClient.writeBytes(contentTypeLine);
outToClient.writeBytes(contentLengthLine);
outToClient.writeBytes("Connection: close\r\n");
outToClient.writeBytes("\r\n");
if (isFile) sendFile(fin, outToClient);
else outToClient.writeBytes(responseString);
outToClient.close();
}
public void sendFile (FileInputStream fin, DataOutputStream out) throws Exception {
byte[] buffer = new byte[1024] ;
int bytesRead;
while ((bytesRead = fin.read(buffer)) != -1 ) {
out.write(buffer, 0, bytesRead);
}
fin.close();
}
public static void main (String args[]) throws Exception {
ServerSocket Server = new ServerSocket (5000);
System.out.println ("HTTP Server Waiting for client on port 5000");
while(true) {
Socket connected = Server.accept();
(new HTTPPOSTServer(connected)).start();
}
}
}
Я прочитал код, я думаю, что код должен быть в порядке.
Но когда я пытаюсь загрузить файл, он распечатывает запрос POST, а затем зависает и никогда не получает никаких байтов.
Если вы хотите, вы можете запустить приведенный выше исходный код напрямую. После запуска вы можете набрать 127.0.0.1:5000 в браузере, и он покажет загрузку файла, затем, если я попытаюсь загрузить файл, он будет зависать там после печати запроса PoST.
Если вам скучно читать код, могу ли я задать следующий более простой вопрос?
Итак, что именно Chrome или любой другой веб-браузер делают с формой -> input type='file'?
Если я использую ServerSocket для обработки HTTP-запроса, я просто получаю InputStream запроса, и тогда весь контент (включая заголовки HTTP и содержимое загружаемого файла) будет проходить через этот InputStream, верно?
Приведенный выше код может анализировать заголовки, но, похоже, из браузера больше ничего не отправляется.
Может кто-нибудь, пожалуйста, помогите?
Спасибо
3 ответа
Он зависает, потому что клиент (Chrome, в моем случае) не обеспечивает Content-Length
, RFC 1867 довольно расплывчато. Это как бы подсказывает, но не принуждает и не имеет примера. Очевидно клиенты не всегда отправляли бы это. Код должен защищать от недостающей длины. Вместо этого он проходит цикл, пока не достигнет конца файла. Тогда это висит.
Использование отладчика иногда очень полезно.
Это много кода:)
Позвольте начать с перемещения break;
Вызывает операторы if внутри раздела POST, начиная со строки 83. Нам нужно выяснить, что висят и операторы while могут легко стать проблемой.
Это поможет нам выяснить, где проблема.
Если вы просто хотите, чтобы это сработало, и вам все равно, в чем заключается ошибка, это своего рода переизобретение колеса, существует масса библиотек, чтобы сделать эту операцию одной или двумя строками.
Вот хороший: http://www.servlets.com/cos/ - посмотрите на MultiPartRequest
класс (источник доступен для скачивания)
Это старый вопрос, но в случае, если другие натолкнутся на него, ища ответ на заглавный вопрос, а не на конкретную ошибку, упомянутую в деталях (у меня такое ощущение, что большинство зрителей)... Я бы порекомендовал вам избавить себя от боли и не используйте приведенный выше фрагмент кода.
Это не HTTP-сервер, и он потерпит неудачу в любом случае, кроме простейшего варианта использования (и, видимо, даже тогда), а также не будет следовать основным нормам хорошего кодирования. Даже если бы это было не так, он явно заявляет, что поддерживает только текстовые файлы (и действительно читает весь файл в строку), и даже в этом случае неправильно обрабатывает кодировки кодировки.
При этом, вы, вероятно, здесь ищете один из двух ответов - как вы можете принять загрузку файлов в вашем приложении, или как сам HTTP-сервер реализует это.
Если вы заинтересованы в том, как сделать это для развлечения или в образовательных целях, я настоятельно рекомендую прочитать RFC (RFC 7230 для ядра HTTP/1.1, RFC 2046, раздел 5.1 для многочастного анализа) и попробовать написать простой HTTP-подобный сервер, такой как этот. Это хороший способ обучения, особенно если у вас есть опытный разработчик, который проанализирует ваш код, даст вам советы и поможет найти наиболее эффективные варианты. В качестве упражнения вы можете даже начать с приведенного выше кода и попытаться исправить его недостатки и сделать его немного более удобным, хотя, тем не менее, не путайте это с реальным HTTP-сервером для использования в производстве.
Если вы не заботитесь обо всем этом и просто хотите добиться успеха в реальном приложении, я рекомендую просто использовать надлежащий HTTP-сервер. Вокруг их много, что позволит вам делать то, что пытается описанный выше фрагмент кода, с гораздо меньшим количеством кода и ошибок.
Отказ от ответственности: я являюсь автором JLHTTP - облегченного HTTP-сервера Java, который представляет собой крошечный однофайловый сервер (или ~50K/35K jar) без каких-либо зависимостей, который стремится быть RFC-совместимым и поддерживает загрузку файлов, среди прочего. Не стесняйтесь использовать его или просто просмотрите код и документацию для сравнительно простого примера того, как может работать HTTP-сервер и многоэтапный синтаксический анализ (загрузка файлов), как это реализовано самим сервером, так и то, как вы можете использовать это в своем приложении. Или посмотрите любой другой HTTP-сервер, который может сделать то же самое.