HttpServletRequest
上定義有 getReader()
方法,可以讓你取得一個 BufferedReader
,透過該 BufferedReader
,可以讓你讀取請求的本體(Body)資料。例如:
package cc.openhome;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebServlet("/postbody")
public class PostBody extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<body>");
out.println(bodyContent(request.getReader()));
out.println("</body>");
out.println("</html>");
}
private String bodyContent(BufferedReader reader) throws IOException {
String input = null;
StringBuilder requestBody = new StringBuilder();
while((input = reader.readLine()) != null) {
requestBody.append(input)
.append("<br>");
}
return requestBody.toString();
}
}
試著對這個 Servlet 以下列表單發出請求:
<!DOCTYPE html>
<html>
<body>
<form action="postbody" method="post">
Username: <input type="text" name="user"><br>
Password: <input type="password" name="passwd"><br>
<input type="submit" name="login">
</form>
</body>
</html>
則你在網頁上會看到以下內容,因為 POST
時,查詢字串(Query string)是放在請求本體之中發送:
user=caterpillar&passwd=123456&login=%B0e%A5X%ACd%B8%DF
HTML 的 <form>
標籤,enctype
屬性預設值是 "application/x-www-form-urlencoded"
,如果上傳的是檔案,必須設定為 "multipart/form-data"
,原理可以參考〈HTTP 檔案上傳機制解析〉的內容。如果你使用以下的表單選擇一個檔案發送:
<!DOCTYPE html>
<html>
<body>
<form action="postbody" method="post"
enctype="multipart/form-data">
Select File :<input type="file" name="filename" value="" /><br>
<input type="submit" value="Upload" name="upload" />
</form>
</body>
</html>
例如發送一個 JPG 圖檔,則在網頁上會看到:
-----------------------------7e11a63520166
Content-Disposition: form-data; name="filename"; filename="caterpillar.jpg"
Content-Type: image/pjpeg
會有一堆奇奇怪怪的字元,這些字元是實際的檔案內容
-----------------------------7e11a63520166
Content-Disposition: form-data; name="upload"
Upload
-----------------------------7e11a63520166--
所以你要取得上傳的檔案,就是判斷檔案的開始與結束區段,你可以使用 HttpServletRequest
的 getInputStream()
取得 ServletInputStream
,它是 InputStream
的子類別,代表請求本體的串流物件,你可以利用它來處理上傳的檔案區段。例如:
package cc.openhome;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebServlet("/upload")
public class Upload extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
byte[] content = bodyContent(request);
String contentAsTxt = new String(content, "ISO-8859-1");
String filename = filename(contentAsTxt);
Range fileRange = fileRange(contentAsTxt, request.getContentType());
write(
content,
contentAsTxt.substring(0, fileRange.start)
.getBytes("ISO-8859-1")
.length,
contentAsTxt.substring(0, fileRange.end)
.getBytes("ISO-8859-1")
.length,
"c:/workspace/" + filename
);
}
private byte[] bodyContent(HttpServletRequest request) throws IOException {
try(ByteArrayOutputStream out = new ByteArrayOutputStream()) {
InputStream in = request.getInputStream();
byte[] buffer = new byte[1024];
int length = -1;
while((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
return out.toByteArray();
}
}
private String filename(String contentTxt) throws UnsupportedEncodingException {
Pattern pattern = Pattern.compile("filename=\"(.*)\"");
Matcher matcher = pattern.matcher(contentTxt);
matcher.find();
return matcher.group(1);
}
private static class Range {
final int start;
final int end;
public Range(int start, int end) {
this.start = start;
this.end = end;
}
}
private Range fileRange(String content, String contentType) {
Pattern pattern = Pattern.compile("filename=\".*\"\\r\\n.*\\r\\n\\r\\n(.*+)");
Matcher matcher = pattern.matcher(content);
matcher.find();
int start = matcher.start(1);
String boundary = contentType.substring(
contentType.lastIndexOf("=") + 1, contentType.length());
int end = content.indexOf(boundary, start) - 4;
return new Range(start, end);
}
private void write(byte[] content, int start, int end, String file) throws IOException {
try(FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(content, start, (end - start));
}
}
}
你可以利用這個表單來選擇檔案上傳:
<!DOCTYPE html>
<html>
<body>
<form action="upload" method="post"
enctype="multipart/form-data">
Select File :<input type="file" name="filename" value="" /><br>
<input type="submit" value="Upload" name="upload" />
</form>
</body>
</html>
在同一個請求期間,getReader()
與 getInputStream()
只能擇一呼叫,若同一請求期間兩者都有呼叫,則會丟出 IllegalStateException
例外。