接續〈getReader()、getInputStream()〉 檔案上傳的議題。在 Servlet 3.0 中,新增了 Part
類別,可以讓你方便地進行檔案上傳的處理。你可以透過 HttpServletRequest
的 getPart()
取得 Part
物件。例如若有個上傳表單如下:
<!DOCTYPE html>
<html>
<body>
<form action="upload" method="post"
enctype="multipart/form-data">
Photo :<input type="file" name="photo"/><br>
<input type="submit" value="Upload" name="upload" />
</form>
</body>
</html>
則你可以撰寫一個 Servlet 來進行檔案上傳的處理:
package cc.openhome;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@MultipartConfig
@WebServlet("/upload")
public class Upload extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Part photo = request.getPart("photo");
String filename = photo.getSubmittedFileName();
try(InputStream in = photo.getInputStream();
OutputStream out = new FileOutputStream("c:/workspace/" + filename)) {
byte[] buffer = new byte[1024];
int length = -1;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
}
}
}
getPart()
方法接受一個字串,代表著檔案上傳欄位的 name
屬性,getPart()
方法上有著 getHeader()
、getInputStream()
等方法,其中 getSubmittedFileName()
是 Servlet 3.1 中新增,可以取得上傳的檔案名稱,getInputStream()
可以取得代表上傳檔案區段的檔案內容之輸入串流。
如果要能使用 getPart()
,Servlet 上要標註 @MultipartConfig
。
如果你有多個檔案要上傳,則可以使用 getParts()
方法,這會傳回一個 Collection
,當中是每個上傳檔案的 Part
物件。例如若有個表單如下:
<!DOCTYPE html>
<html>
<body>
<form action="upload" method="post"
enctype="multipart/form-data">
File 1 :<input type="file" name="file1"/><br>
File 2 :<input type="file" name="file2"/><br>
File 3 :<input type="file" name="file3"/><br>
<input type="submit" value="Upload" name="upload" />
</form>
</body>
</html>
則你可以使用以下的 Servlet 來處理檔案上傳請求:
package cc.openhome;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@MultipartConfig
@WebServlet("/upload")
public class Upload extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); // 為了處理中文檔名
request.getParts()
.stream()
.filter(part -> !"upload".equals(part.getName()))
.forEach(this::write);
}
private void write(Part part) {
String filename = part.getSubmittedFileName();
try(InputStream in = part.getInputStream();
OutputStream out = new FileOutputStream("c:/workspace/" + filename)) {
byte[] buffer = new byte[1024];
int length = -1;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
} catch(IOException ex) {
throw new UncheckedIOException(ex);
}
}
}
事實上,Part
有個方便的 write()
方法,可以讓你直接將上傳的檔案寫入磁碟中,write()
可指定檔名,寫入的路徑是相對於 @MultipartConfig
的 location
所設定的路徑。例如上例可以修改為:
package cc.openhome;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@MultipartConfig(location="c:/workspace")
@WebServlet("/upload")
public class Upload extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); // 為了處理中文檔名
request.getParts()
.stream()
.filter(part -> !"upload".equals(part.getName()))
.forEach(part -> {
try {
part.write(part.getSubmittedFileName());
} catch(IOException ex) {
throw new UncheckedIOException(ex);
}
});
}
}
如果使用 web.xml 設定,則可以用 <multipart-config>
來指定儲存位置。例如:
...
<servlet>
<servlet-name>Upload</servlet-name>
<servlet-class>cc.openhome.Upload</servlet-class>
<multipart-config>
<location>c:/workspace</location>
</multipart-config>
</servlet>
...