getPart()、getParts()



接續〈getReader()、getInputStream()〉 檔案上傳的議題。在 Servlet 3.0 中,新增了 Part 類別,可以讓你方便地進行檔案上傳的處理。你可以透過 HttpServletRequestgetPart() 取得 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() 可指定檔名,寫入的路徑是相對於 @MultipartConfiglocation 所設定的路徑。例如上例可以修改為:

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>
...